# HG changeset patch # User unc0rr # Date 1372332782 -14400 # Node ID 8e8b908970c23f42b8bf805ebfc5d9cc5ce043c8 # Parent 76e68c136a110223e59f85f5f5b72000c42539c3 Refactoring: get rid of GSHandlers.inc diff -r 76e68c136a11 -r 8e8b908970c2 hedgewars/CMakeLists.txt --- a/hedgewars/CMakeLists.txt Thu Jun 27 14:37:03 2013 +0400 +++ b/hedgewars/CMakeLists.txt Thu Jun 27 15:33:02 2013 +0400 @@ -73,9 +73,6 @@ uScript.pas hwengine.pas - GSHandlers.inc - VGSHandlers.inc - ArgParsers.inc options.inc ${CMAKE_CURRENT_BINARY_DIR}/config.inc ) diff -r 76e68c136a11 -r 8e8b908970c2 hedgewars/GSHandlers.inc --- a/hedgewars/GSHandlers.inc Thu Jun 27 14:37:03 2013 +0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5638 +0,0 @@ -(* - * Hedgewars, a free turn based strategy game - * Copyright (c) 2004-2013 Andrey Korotaev - * - * 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 - *) - -(* - * This file contains the step handlers for gears. - * - * Important: Since gears change the course of the game, calculations that - * lead to different results for different clients/players/machines - * should NOT occur! - * Use safe functions and data types! (e.g. GetRandom() and hwFloat) - *) - -procedure doStepPerPixel(Gear: PGear; step: TGearStepProcedure; onlyCheckIfChanged: boolean); -var - dX, dY, sX, sY: hwFloat; - i, steps: LongWord; - caller: TGearStepProcedure; -begin - dX:= Gear^.dX; - dY:= Gear^.dY; - steps:= max(abs(hwRound(Gear^.X+dX)-hwRound(Gear^.X)), abs(hwRound(Gear^.Y+dY)-hwRound(Gear^.Y))); - - // Gear is still on the same Pixel it was before - if steps < 1 then - begin - if onlyCheckIfChanged then - begin - Gear^.X := Gear^.X + dX; - Gear^.Y := Gear^.Y + dY; - EXIT; - end - else - steps := 1; - end; - - if steps > 1 then - begin - sX:= dX / steps; - sY:= dY / steps; - end - - else - begin - sX:= dX; - sY:= dY; - end; - - caller:= Gear^.doStep; - - for i:= 1 to steps do - begin - Gear^.X := Gear^.X + sX; - Gear^.Y := Gear^.Y + sY; - step(Gear); - if (Gear^.doStep <> caller) - or ((Gear^.State and gstCollision) <> 0) - or ((Gear^.State and gstMoving) = 0) then - break; - end; -end; - -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 - PlaySoundV(sndOops, gi^.Hedgehog^.Team^.voicepack) - - else - begin - if ((gi^.State and gstMoving) = 0) and (gi^.Hedgehog^.Effects[heFrozen] = 0) then - begin - gi^.dX.isNegative:= X r div 2 then - PlaySoundV(sndNooo, gi^.Hedgehog^.Team^.voicepack) - else - PlaySoundV(sndUhOh, gi^.Hedgehog^.Team^.voicepack); - end; - end; - end; - - gi := gi^.NextGear - end; -end; - -procedure HideHog(HH: PHedgehog); -begin - ScriptCall('onHogHide', HH^.Gear^.Uid); - DeleteCI(HH^.Gear); - if FollowGear = HH^.Gear then - FollowGear:= nil; - - if lastGearByUID = HH^.Gear then - lastGearByUID := nil; - - HH^.Gear^.Message:= HH^.Gear^.Message or gmRemoveFromList; - with HH^.Gear^ do - begin - Z := cHHZ; - HH^.Gear^.Active:= false; - State:= State and (not (gstHHDriven or gstAttacking or gstAttacked)); - Message := Message and (not gmAttack); - end; - HH^.GearHidden:= HH^.Gear; - HH^.Gear:= nil -end; - - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepDrowningGear(Gear: PGear); - begin - AllInactive := false; - Gear^.Y := Gear^.Y + cDrownSpeed; - Gear^.X := Gear^.X + Gear^.dX * cDrownSpeed; - // Create some bubbles (0.5% might be better but causes too few bubbles sometimes) - if ((not SuddenDeathDmg and (WaterOpacity < $FF)) - or (SuddenDeathDmg and (SDWaterOpacity < $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); - if (not SuddenDeathDmg and (WaterOpacity > $FE)) - or (SuddenDeathDmg and (SDWaterOpacity > $FE)) - or (hwRound(Gear^.Y) > Gear^.Radius + cWaterLine + cVisibleWater) then - DeleteGear(Gear); - end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepFallingGear(Gear: PGear); -var - isFalling: boolean; - //tmp: QWord; - tdX, tdY: hwFloat; - collV, collH: LongInt; - land: word; -begin - // clip velocity at 2 - over 1 per pixel, but really shouldn't cause many actual problems. - if Gear^.dX.Round > 2 then - Gear^.dX.QWordValue:= 8589934592; - if Gear^.dY.Round > 2 then - Gear^.dY.QWordValue:= 8589934592; - - if (Gear^.State and gstSubmersible <> 0) and (hwRound(Gear^.Y) > cWaterLine) then - begin - Gear^.dX:= Gear^.dX * _0_999; - Gear^.dY:= Gear^.dY * _0_999 - end; - - Gear^.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) < min(LAND_WIDTH div -2, -2048)) - or (hwRound(Gear^.X) > max(LAND_WIDTH * 3 div 2, 6144)) then - Gear^.State := Gear^.State or gstCollision; - - if Gear^.dY.isNegative then - begin - isFalling := true; - land:= TestCollisionYwithGear(Gear, -1); - if land <> 0 then - begin - collV := -1; - if land and lfIce <> 0 then - Gear^.dX := Gear^.dX * (_0_9 + Gear^.Friction * _0_1) - else - 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) <> 0) then - collV := 1; - end - else - begin // Gear^.dY.isNegative is false - land:= TestCollisionYwithGear(Gear, 1); - if land <> 0 then - begin - collV := 1; - isFalling := false; - if land and lfIce <> 0 then - Gear^.dX := Gear^.dX * (_0_9 + Gear^.Friction * _0_1) - else - Gear^.dX := Gear^.dX * Gear^.Friction; - - Gear^.dY := - Gear^.dY * Gear^.Elasticity; - Gear^.State := Gear^.State or gstCollision - end - else - begin - isFalling := true; - if (Gear^.AdvBounce=1) and (TestCollisionYwithGear(Gear, -1) <> 0) then - collV := -1 - end - 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) then - if (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 - begin - Gear^.dY := Gear^.dY + cGravity; - if (GameFlags and gfMoreWind) <> 0 then - Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density - end; - - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - if Gear^.Kind <> gtBee then - CheckGearDrowning(Gear); - //if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) < _0_0002) and - if (not isFalling) and ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_02.QWordValue) then - Gear^.State := Gear^.State and (not gstMoving) - else - Gear^.State := Gear^.State or gstMoving; - - if (Gear^.nImpactSounds > 0) and - (Gear^.State and gstCollision <> 0) and - (((Gear^.Kind <> gtMine) and (Gear^.Damage <> 0)) or (Gear^.State and gstMoving <> 0)) and - (((Gear^.Radius < 3) and (Gear^.dY < -_0_1)) or - ((Gear^.Radius >= 3) 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, gdX: hwFloat; - vg: PVisualGear; -begin - AllInactive := false; - - doStepFallingGear(Gear); - - dec(Gear^.Timer); - if Gear^.Timer = 1000 then // might need adjustments - case Gear^.Kind of - gtGrenade: 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, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx); - end; - - if (Gear^.Kind = gtGasBomb) and ((GameTicks mod 200) = 0) then - begin - vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite); - if vg <> nil then - vg^.Tint:= $FFC0C000; - end; - - if Gear^.Timer = 0 then - begin - case Gear^.Kind of - gtGrenade: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - gtBall: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 40, Gear^.Hedgehog, EXPLAutoSound); - gtClusterBomb: - begin - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - gdX:= Gear^.dX; - doMakeExplosion(x, y, 20, Gear^.Hedgehog, EXPLAutoSound); - for i:= 0 to 4 do - begin - dX := rndSign(GetRandomf * _0_1) + gdX / 5; - dY := (GetRandomf - _3) * _0_08; - FollowGear := AddGear(x, y, gtCluster, 0, dX, dY, 25) - end - end; - gtWatermelon: - begin - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - gdX:= Gear^.dX; - doMakeExplosion(x, y, 75, Gear^.Hedgehog, EXPLAutoSound); - for i:= 0 to 5 do - begin - dX := rndSign(GetRandomf * _0_1) + gdX / 5; - dY := (GetRandomf - _1_5) * _0_3; - FollowGear:= AddGear(x, y, gtMelonPiece, 0, dX, dY, 75); - FollowGear^.DirAngle := i * 60 - end - end; - gtHellishBomb: - begin - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - doMakeExplosion(x, y, 90, Gear^.Hedgehog, EXPLAutoSound); - - for i:= 0 to 127 do - begin - dX := AngleCos(i * 16) * _0_5 * (GetRandomf + _1); - dY := AngleSin(i * 16) * _0_5 * (GetRandomf + _1); - if i mod 2 = 0 then - begin - AddGear(x, y, gtFlame, gstTmpFlag, dX, dY, 0); - AddGear(x, y, gtFlame, 0, dX, -dY, 0) - end - else - begin - AddGear(x, y, gtFlame, 0, dX, dY, 0); - AddGear(x, y, gtFlame, gstTmpFlag, dX, -dY, 0) - end; - end - end; - gtGasBomb: - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound); - for i:= 0 to 2 do - begin - x:= GetRandom(60); - y:= GetRandom(40); - FollowGear:= AddGear(hwRound(Gear^.X) - 30 + x, hwRound(Gear^.Y) - 20 + y, gtPoisonCloud, 0, _0, _0, 0); - end - end; - 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 - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace); - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepMolotov(Gear: PGear); -var - s: Longword; - i, gX, gY: LongInt; - dX, dY: hwFloat; - smoke, glass: PVisualGear; -begin - AllInactive := false; - - doStepFallingGear(Gear); - CalcRotationDirAngle(Gear); - - // let's add some smoke depending on speed - s:= max(32,152 - round((abs(hwFloat2FLoat(Gear^.dX))+abs(hwFloat2Float(Gear^.dY)))*120))+random(10); - if (GameTicks mod s) = 0 then - begin - // adjust angle to match the texture - if Gear^.dX.isNegative then - i:= 130 - else i:= 50; - - smoke:= AddVisualGear(hwRound(Gear^.X)-round(cos((Gear^.DirAngle+i) * pi / 180)*20), hwRound(Gear^.Y)-round(sin((Gear^.DirAngle+i) * pi / 180)*20), vgtSmoke); - if smoke <> nil then - smoke^.Scale:= 0.75; - end; - - if (Gear^.State and gstCollision) <> 0 then - begin - PlaySound(sndMolotov); - gX := hwRound(Gear^.X); - gY := hwRound(Gear^.Y); - for i:= 0 to 4 do - begin - (*glass:= AddVisualGear(gx+random(7)-3, gy+random(5)-2, vgtEgg); - if glass <> nil then - begin - glass^.Frame:= 2; - glass^.Tint:= $41B83ED0 - i * $10081000; - glass^.dX:= 1/(10*(random(11)-5)); - glass^.dY:= -1/(random(4)+5); - end;*) - glass:= AddVisualGear(gx+random(7)-3, gy+random(7)-3, vgtStraightShot); - if glass <> nil then - with glass^ do - begin - Frame:= 2; - Tint:= $41B83ED0 - i * $10081000; - Angle:= random(360); - dx:= 0.0000001; - dy:= 0; - if random(2) = 0 then - dx := -dx; - FrameTicks:= 750; - State:= ord(sprEgg) - end; - end; - for i:= 0 to 24 do - begin - dX := AngleCos(i * 2) * ((_0_15*(i div 5))) * (GetRandomf + _1); - dY := AngleSin(i * 8) * _0_5 * (GetRandomf + _1); - AddGear(gX, gY, gtFlame, gstTmpFlag, dX, dY, 0); - AddGear(gX, gY, gtFlame, gstTmpFlag, dX,-dY, 0); - AddGear(gX, gY, gtFlame, gstTmpFlag,-dX, dY, 0); - AddGear(gX, gY, gtFlame, gstTmpFlag,-dX,-dY, 0); - end; - DeleteGear(Gear); - exit - end; -end; - -//////////////////////////////////////////////////////////////////////////////// - -procedure doStepCluster(Gear: PGear); -begin - AllInactive := false; - doStepFallingGear(Gear); - if (Gear^.State and gstCollision) <> 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Timer, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end; - - if (Gear^.Kind = gtMelonPiece) - or (Gear^.Kind = gtBall) then - CalcRotationDirAngle(Gear) - else if (GameTicks and $1F) = 0 then - begin - if hwRound(Gear^.Y) > cWaterLine then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) - else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepShell(Gear: PGear); -begin - AllInactive := false; - if (GameFlags and gfMoreWind) = 0 then - Gear^.dX := Gear^.dX + cWindSpeed; - doStepFallingGear(Gear); - if (Gear^.State and gstCollision) <> 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end; - if (GameTicks and $3F) = 0 then - begin - if hwRound(Gear^.Y) > cWaterLine then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) - else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSnowball(Gear: PGear); -var kick, i: LongInt; - particle: PVisualGear; - gdX, gdY: hwFloat; -begin - AllInactive := false; - if (GameFlags and gfMoreWind) = 0 then - Gear^.dX := Gear^.dX + cWindSpeed; - gdX := Gear^.dX; - gdY := Gear^.dY; - doStepFallingGear(Gear); - CalcRotationDirAngle(Gear); - if (Gear^.State and gstCollision) <> 0 then - begin - kick:= hwRound((hwAbs(gdX)+hwAbs(gdY)) * _20); - Gear^.dX:= gdX; - Gear^.dY:= gdY; - AmmoShove(Gear, 0, kick); - for i:= 15 + kick div 10 downto 0 do - begin - particle := AddVisualGear(hwRound(Gear^.X) + Random(25), hwRound(Gear^.Y) + Random(25), vgtDust); - if particle <> nil then - particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480) - end; - DeleteGear(Gear); - exit - end; - if ((GameTicks and $1F) = 0) and (Random(3) = 0) then - begin - particle:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust); - if particle <> nil then - particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSnowflake(Gear: PGear); -var xx, yy, px, py, rx, ry, lx, ly: LongInt; - move, draw, allpx, gun: Boolean; - s: PSDL_Surface; - p: PLongwordArray; - lf: LongWord; -begin -inc(Gear^.Pos); -gun:= (Gear^.State and gstTmpFlag) <> 0; -move:= false; -draw:= false; -if gun then - begin - Gear^.State:= Gear^.State and (not gstInvisible); - doStepFallingGear(Gear); - CheckCollision(Gear); - if ((Gear^.State and gstCollision) <> 0) or ((Gear^.State and gstMoving) = 0) then - draw:= true; - xx:= hwRound(Gear^.X); - yy:= hwRound(Gear^.Y); - end -else if GameTicks and $7 = 0 then - begin - with Gear^ do - begin - State:= State and (not gstInvisible); - X:= X + cWindSpeed * 3200 + dX; - Y:= Y + dY + cGravity * vobFallSpeed * 8; // using same value as flakes to try and get similar results - xx:= hwRound(X); - yy:= hwRound(Y); - if vobVelocity <> 0 then - begin - DirAngle := DirAngle + (Damage / 1000); - if DirAngle < 0 then - DirAngle := DirAngle + 360 - else if 360 < DirAngle then - DirAngle := DirAngle - 360; - end; -(* -We aren't using frametick right now, so just a waste of cycles. - inc(Health, 8); - if longword(Health) > vobFrameTicks then - begin - dec(Health, vobFrameTicks); - inc(Timer); - if Timer = vobFramesCount then - Timer:= 0 - end; -*) - // move back to cloud layer - if yy > cWaterLine then - move:= true - else if (xx > snowRight) or (xx < snowLeft) then - move:=true - // Solid pixel encountered - else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then - begin - lf:= Land[yy, xx] and (lfObject or lfBasic or lfIndestructible); - if lf = 0 then lf:= lfObject; - // If there's room below keep falling - if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (Land[yy-1, xx] = 0) then - begin - X:= X - cWindSpeed * 1600 - dX; - end - // If there's room below, on the sides, fill the gaps - else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx-(1*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx-(1*hwSign(cWindSpeed)))] = 0) then - begin - X:= X - _0_8 * hwSign(cWindSpeed); - Y:= Y - dY - cGravity * vobFallSpeed * 8; - end - else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx-(2*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx-(2*hwSign(cWindSpeed)))] = 0) then - begin - X:= X - _0_8 * 2 * hwSign(cWindSpeed); - Y:= Y - dY - cGravity * vobFallSpeed * 8; - end - else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx+(1*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx+(1*hwSign(cWindSpeed)))] = 0) then - begin - X:= X + _0_8 * hwSign(cWindSpeed); - Y:= Y - dY - cGravity * vobFallSpeed * 8; - end - else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx+(2*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx+(2*hwSign(cWindSpeed)))] = 0) then - begin - X:= X + _0_8 * 2 * hwSign(cWindSpeed); - Y:= Y - dY - cGravity * vobFallSpeed * 8; - end - // if there's an hog/object below do nothing - else if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((Land[yy+1, xx] and $FF) <> 0)) - then move:=true - else draw:= true - end - end - end; -if draw then - with Gear^ do - begin - // we've collided with land. draw some stuff and get back into the clouds - move:= true; - if (Pos > 20) and ((CurAmmoGear = nil) - or (CurAmmoGear^.Kind <> gtRope)) then - begin -////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS //////////////////////////////////// - if not gun then - begin - dec(yy,3); - dec(xx,1) - end; - s:= SpritesData[sprSnow].Surface; - p:= s^.pixels; - allpx:= true; - for py:= 0 to Pred(s^.h) do - begin - for px:= 0 to Pred(s^.w) do - begin - lx:=xx + px; ly:=yy + py; - if (ly and LAND_HEIGHT_MASK = 0) and (lx and LAND_WIDTH_MASK = 0) and (Land[ly, lx] and $FF = 0) then - begin - rx:= lx; - ry:= ly; - if cReducedQuality and rqBlurryLand <> 0 then - begin - rx:= rx div 2;ry:= ry div 2; - end; - if Land[yy + py, xx + px] <= lfAllObjMask then - if gun then - begin - LandDirty[yy div 32, xx div 32]:= 1; - if LandPixels[ry, rx] = 0 then - Land[ly, lx]:= lfDamaged or lfObject - else Land[ly, lx]:= lfDamaged or lfBasic - end - else Land[ly, lx]:= lf; - if gun then - LandPixels[ry, rx]:= (ExplosionBorderColor and (not AMask)) or (p^[px] and AMask) - else LandPixels[ry, rx]:= addBgColor(LandPixels[ry, rx], p^[px]); - end - else allpx:= false - end; - p:= @(p^[s^.pitch shr 2]) - end; - - // Why is this here. For one thing, there's no test on +1 being safe. - //Land[py, px+1]:= lfBasic; - - if allpx then - UpdateLandTexture(xx, Pred(s^.h), yy, Pred(s^.w), true) - else - begin - UpdateLandTexture( - max(0, min(LAND_WIDTH, xx)), - min(LAND_WIDTH - xx, Pred(s^.w)), - max(0, min(LAND_WIDTH, yy)), - min(LAND_HEIGHT - yy, Pred(s^.h)), false // could this be true without unnecessarily creating blanks? - ); - end; -////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS //////////////////////////////////// - end - end; - -if move then - begin - if gun then - begin - DeleteGear(Gear); - exit - end; - Gear^.Pos:= 0; - Gear^.X:= int2hwFloat(LongInt(GetRandom(snowRight - snowLeft)) + snowLeft); - Gear^.Y:= int2hwFloat(LAND_HEIGHT + LongInt(GetRandom(50)) - 1325); - Gear^.State:= Gear^.State or gstInvisible; - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepGrave(Gear: PGear); -begin - if (Gear^.Message and gmDestroy) <> 0 then - begin - DeleteGear(Gear); - exit - end; - - AllInactive := 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 + cGravity -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepBeeWork(Gear: PGear); -var - t: hwFloat; - gX,gY,i: LongInt; - uw, nuw: boolean; - flower: PVisualGear; - -begin - AllInactive := false; - gX := hwRound(Gear^.X); - gY := hwRound(Gear^.Y); - uw := (Gear^.Tag <> 0); // was bee underwater last tick? - nuw := (cWaterLine < gy + Gear^.Radius); // is bee underwater now? - - // if water entered or left - if nuw <> 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); - StopSoundChan(Gear^.SoundChannel); - if nuw then - begin - Gear^.SoundChannel := LoopSound(sndBeeWater); - Gear^.Tag := 1; - end - else - begin - Gear^.SoundChannel := LoopSound(sndBee); - Gear^.Tag := 0; - end; - end; - - - if Gear^.Timer = 0 then - Gear^.RenderTimer:= false - else - begin - if (GameTicks and $F) = 0 then - begin - if (GameTicks and $30) = 0 then - AddVisualGear(gX, gY, vgtBeeTrace); - Gear^.dX := Gear^.Elasticity * (Gear^.dX + _0_000064 * (Gear^.Target.X - gX)); - Gear^.dY := Gear^.Elasticity * (Gear^.dY + _0_000064 * (Gear^.Target.Y - gY)); - // make sure new speed isn't higher than original one (which we stored in Friction variable) - t := Gear^.Friction / Distance(Gear^.dX, Gear^.dY); - Gear^.dX := Gear^.dX * t; - Gear^.dY := Gear^.dY * t; - end; - - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - - end; - - - CheckCollision(Gear); - if ((Gear^.State and gstCollision) <> 0) then - begin - StopSoundChan(Gear^.SoundChannel); - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - for i:= 0 to 31 do - begin - flower:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot); - if flower <> nil then - with flower^ do - begin - Scale:= 0.75; - dx:= 0.001 * (random(200)); - dy:= 0.001 * (random(200)); - if random(2) = 0 then - dx := -dx; - if random(2) = 0 then - dy := -dy; - FrameTicks:= random(250) + 250; - State:= ord(sprTargetBee); - end; - end; - DeleteGear(Gear); - end; - - if (Gear^.Timer > 0) then - dec(Gear^.Timer) - else - begin - if nuw then - begin - StopSoundChan(Gear^.SoundChannel); - CheckGearDrowning(Gear); - end - else - doStepFallingGear(Gear); - end; -end; - -procedure doStepBee(Gear: PGear); -begin - AllInactive := 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, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end; - dec(Gear^.Timer); - if Gear^.Timer = 0 then - begin - Gear^.Hedgehog^.Gear^.Message:= Gear^.Hedgehog^.Gear^.Message and (not gmAttack); - Gear^.Hedgehog^.Gear^.State:= Gear^.Hedgehog^.Gear^.State and (not gstAttacking); - AttackBar:= 0; - - Gear^.SoundChannel := LoopSound(sndBee); - Gear^.Timer := 5000; - // save initial speed in otherwise unused Friction variable - Gear^.Friction := Distance(Gear^.dX, Gear^.dY); - Gear^.doStep := @doStepBeeWork - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepShotIdle(Gear: PGear); -begin - AllInactive := false; - inc(Gear^.Timer); - if Gear^.Timer > 75 then - begin - DeleteGear(Gear); - AfterAttack - end -end; - -procedure doStepShotgunShot(Gear: PGear); -var - i: LongWord; - shell: PVisualGear; -begin - AllInactive := 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.QWordValue / -17179869184; - shell^.dY := gear^.dY.QWordValue / -17179869184; - shell^.Frame := 0 - end; - Gear^.State := Gear^.State or gstAnimation - end; - exit - end else - if(Gear^.Hedgehog^.Gear = nil) or ((Gear^.Hedgehog^.Gear^.State and gstMoving) <> 0) then - begin - DeleteGear(Gear); - AfterAttack; - exit - end - else - inc(Gear^.Timer); - - i := 200; - repeat - Gear^.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 := @doStepShotIdle -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure spawnBulletTrail(Bullet: PGear); -var oX, oY: hwFloat; - VGear: PVisualGear; -begin - if Bullet^.PortalCounter = 0 then - begin - ox:= CurrentHedgehog^.Gear^.X + Int2hwFloat(GetLaunchX(CurrentHedgehog^.CurAmmoType, hwSign(CurrentHedgehog^.Gear^.dX), CurrentHedgehog^.Gear^.Angle)); - oy:= CurrentHedgehog^.Gear^.Y + Int2hwFloat(GetLaunchY(CurrentHedgehog^.CurAmmoType, CurrentHedgehog^.Gear^.Angle)); - end - else - begin - ox:= Bullet^.Elasticity; - oy:= Bullet^.Friction; - end; - - // Bullet trail - VGear := AddVisualGear(hwRound(ox), hwRound(oy), vgtLineTrail); - - if VGear <> nil then - begin - VGear^.X:= hwFloat2Float(ox); - VGear^.Y:= hwFloat2Float(oy); - VGear^.dX:= hwFloat2Float(Bullet^.X); - VGear^.dY:= hwFloat2Float(Bullet^.Y); - - // reached edge of land. assume infinite beam. Extend it way out past camera - if (hwRound(Bullet^.X) and LAND_WIDTH_MASK <> 0) - or (hwRound(Bullet^.Y) and LAND_HEIGHT_MASK <> 0) then - // only extend if not under water - if hwRound(Bullet^.Y) < cWaterLine then - begin - VGear^.dX := VGear^.dX + max(LAND_WIDTH,4096) * (VGear^.dX - VGear^.X); - VGear^.dY := VGear^.dY + max(LAND_WIDTH,4096) * (VGear^.dY - VGear^.Y); - end; - - VGear^.Timer := 200; - end; -end; - -procedure doStepBulletWork(Gear: PGear); -var - i, x, y: LongWord; - oX, oY: hwFloat; - VGear: PVisualGear; -begin - AllInactive := 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); - // let's interrupt before a collision to give portals a chance to catch the bullet - if (Gear^.Damage = 1) and (Gear^.Tag = 0) and not(CheckLandValue(x, y, lfLandMask)) then - begin - Gear^.Tag := 1; - Gear^.Damage := 0; - Gear^.X := Gear^.X - Gear^.dX; - Gear^.Y := Gear^.Y - Gear^.dY; - CheckGearDrowning(Gear); - break; - end - else - Gear^.Tag := 0; - - if Gear^.Damage > 5 then - if Gear^.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 ((not SuddenDeathDmg and (WaterOpacity < $FF)) or (SuddenDeathDmg and (SDWaterOpacity < $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 (Ammoz[Gear^.AmmoType].Ammo.NumPerTurn <= CurrentHedgehog^.MultiShootAttacks) and ((GameFlags and gfArtillery) = 0) then - cArtillery := false; - - // Bullet Hit - if (hwRound(Gear^.X) and LAND_WIDTH_MASK = 0) and (hwRound(Gear^.Y) and LAND_HEIGHT_MASK = 0) then - begin - VGear := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBulletHit); - if VGear <> nil then - begin - VGear^.Angle := DxDy2Angle(-Gear^.dX, Gear^.dY); - end; - end; - - spawnBulletTrail(Gear); - Gear^.doStep := @doStepShotIdle - end; -end; - -procedure doStepDEagleShot(Gear: PGear); -begin - PlaySound(sndGun); - // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just plain old weird angles - Gear^.X := Gear^.X + Gear^.dX * 3; - Gear^.Y := Gear^.Y + Gear^.dY * 3; - Gear^.doStep := @doStepBulletWork -end; - -procedure doStepSniperRifleShot(Gear: PGear); -var - HHGear: PGear; - shell: PVisualGear; -begin - cArtillery := true; - HHGear := 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) then - dec(HHGear^.Angle,32) - end; - - if (HHGear^.Message and gmAttack) <> 0 then - begin - shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell); - if shell <> nil then - begin - shell^.dX := gear^.dX.QWordValue / -8589934592; - shell^.dY := gear^.dY.QWordValue / -8589934592; - 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); - // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just weird angles - Gear^.X := Gear^.X + Gear^.dX * 3; - Gear^.Y := Gear^.Y + Gear^.dY * 3; - Gear^.doStep := @doStepBulletWork; - end - else - 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) then - dec(HHGear^.Angle); - - if (TurnTimeLeft > 0) then - dec(TurnTimeLeft) - else - begin - DeleteGear(Gear); - AfterAttack - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepActionTimer(Gear: PGear); -begin -dec(Gear^.Timer); -case Gear^.Kind of - gtATStartGame: - begin - AllInactive := false; - if Gear^.Timer = 0 then - begin - AddCaption(trmsg[sidStartFight], cWhiteColor, capgrpGameState); - 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(_S'N'); - SendIPC(_S'q'); - GameState := gsExit - end - end; - end; -if Gear^.Timer = 0 then - DeleteGear(Gear) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepPickHammerWork(Gear: PGear); -var - i, ei, x, y: LongInt; - HHGear: PGear; -begin - AllInactive := false; - HHGear := Gear^.Hedgehog^.Gear; - dec(Gear^.Timer); - if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then - dec(TurnTimeLeft); - if (TurnTimeLeft = 0) or (Gear^.Timer = 0) - or((Gear^.Message and gmDestroy) <> 0) - or((HHGear^.State and gstHHDriven) =0) then - begin - StopSoundChan(Gear^.SoundChannel); - DeleteGear(Gear); - AfterAttack; - doStepHedgehogMoving(HHGear); // for gfInfAttack - exit - end; - - x:= hwRound(Gear^.X); - y:= hwRound(Gear^.Y); - if (Gear^.Timer mod 33) = 0 then - begin - HHGear^.State := HHGear^.State or gstNoDamage; - doMakeExplosion(x, y + 7, 6, Gear^.Hedgehog, EXPLDontDraw); - HHGear^.State := HHGear^.State and (not gstNoDamage) - end; - - if (Gear^.Timer mod 47) = 0 then - begin - // ok. this was an attempt to turn off dust if not actually drilling land. I have no idea why it isn't working as expected - if (( (y + 12) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y + 12, x] > 255) then - for i:= 0 to 1 do - AddVisualGear(x - 5 + Random(10), y + 12, vgtDust); - - i := x - Gear^.Radius - LongInt(GetRandom(2)); - ei := x + Gear^.Radius + LongInt(GetRandom(2)); - while i <= ei do - begin - DrawExplosion(i, y + 3, 3); - inc(i, 1) - end; - - if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9), lfIndestructible) then - begin - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + _1_9; - end; - SetAllHHToActive; - end; - if TestCollisionYwithGear(Gear, 1) <> 0 then - begin - Gear^.dY := _0; - SetLittle(HHGear^.dX); - HHGear^.dY := _0; - end - else - begin - if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y + Gear^.dY + cGravity), lfLandMask) then - begin - Gear^.dY := Gear^.dY + cGravity; - Gear^.Y := Gear^.Y + Gear^.dY - end; - if hwRound(Gear^.Y) > cWaterLine then - Gear^.Timer := 1 - end; - - Gear^.X := Gear^.X + HHGear^.dX; - if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y)-cHHRadius, lfLandMask) then - begin - HHGear^.X := Gear^.X; - HHGear^.Y := Gear^.Y - int2hwFloat(cHHRadius) - end; - - if (Gear^.Message and gmAttack) <> 0 then - if (Gear^.State and gsttmpFlag) <> 0 then - Gear^.Timer := 1 - else //there would be a mistake. - else - if (Gear^.State and gsttmpFlag) = 0 then - Gear^.State := Gear^.State or gsttmpFlag; - if ((Gear^.Message and gmLeft) <> 0) then - Gear^.dX := - _0_3 - else - if ((Gear^.Message and gmRight) <> 0) then - Gear^.dX := _0_3 - else Gear^.dX := _0; -end; - -procedure doStepPickHammer(Gear: PGear); -var - i, y: LongInt; - ar: TRangeArray; - HHGear: PGear; -begin - i := 0; - HHGear := 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 := @doStepPickHammerWork -end; - -//////////////////////////////////////////////////////////////////////////////// -var - BTPrevAngle, BTSteps: LongInt; - -procedure doStepBlowTorchWork(Gear: PGear); -var - HHGear: PGear; - b: boolean; - prevX: LongInt; -begin - AllInactive := false; - dec(Gear^.Timer); - if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then - dec(TurnTimeLeft); - - HHGear := Gear^.Hedgehog^.Gear; - - HedgehogChAngle(HHGear); - - b := false; - - if abs(LongInt(HHGear^.Angle) - BTPrevAngle) > 7 then - begin - Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.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 (gmAttack or gmUp or gmDown)) or gmLeft - else - HHGear^.Message := (HHGear^.Message and (gmAttack or gmUp or gmDown)) or gmRight; - - 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), - lfIndestructible) then HedgehogStep(HHGear); - - if (prevX = hwRound(HHGear^.X)) and - CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y), - lfIndestructible) 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)),lfIndestructible) 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 - begin - DrawTunnel(HHGear^.X + Gear^.dX * cHHRadius, - HHGear^.Y + Gear^.dY * cHHRadius - _1 - - ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7), - Gear^.dX, Gear^.dY, - cHHStepTicks, cHHRadius * 2 + 7); - end; - - if (TurnTimeLeft = 0) or (Gear^.Timer = 0) - or ((HHGear^.Message and gmAttack) <> 0) then - begin - HHGear^.Message := 0; - HHGear^.State := HHGear^.State and (not gstNotKickable); - DeleteGear(Gear); - AfterAttack - end -end; - -procedure doStepBlowTorch(Gear: PGear); -var - HHGear: PGear; -begin - BTPrevAngle := High(LongInt); - BTSteps := 0; - HHGear := Gear^.Hedgehog^.Gear; - HedgehogChAngle(HHGear); - Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.dX); - Gear^.dY := AngleCos(HHGear^.Angle) * ( - _0_5); - DrawTunnel(HHGear^.X, - HHGear^.Y + Gear^.dY * cHHRadius - _1 - - ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7), - Gear^.dX, Gear^.dY, - cHHStepTicks, cHHRadius * 2 + 7); - HHGear^.Message := 0; - HHGear^.State := HHGear^.State or gstNotKickable; - Gear^.doStep := @doStepBlowTorchWork -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepMine(Gear: PGear); -var vg: PVisualGear; - dxdy: hwFloat; -begin - if Gear^.Health = 0 then dxdy:= hwAbs(Gear^.dX)+hwAbs(Gear^.dY); - if (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^.Health = 0) then - begin - if (dxdy > _0_4) and (Gear^.State and gstCollision <> 0) then - inc(Gear^.Damage, hwRound(dxdy * _50)); - - if ((GameTicks and $FF) = 0) and (Gear^.Damage > random(30)) then - begin - vg:= AddVisualGear(hwRound(Gear^.X) - 4 + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke); - if vg <> nil then - vg^.Scale:= 0.5 - end; - - if (Gear^.Damage > 35) then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end - end; - - 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, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear) - end - else - begin - vg:= AddVisualGear(hwRound(Gear^.X) - 4 + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke); - if vg <> nil then - vg^.Scale:= 0.5; - PlaySound(sndVaporize); - Gear^.Health := 0; - Gear^.Damage := 0; - Gear^.State := Gear^.State and (not gstAttacking) - end; - exit - end; - dec(Gear^.Timer); - end - else // gsttmpFlag = 0 - if (TurnTimeLeft = 0) - or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime)) - or (Gear^.Hedgehog^.Gear = nil) then - Gear^.State := Gear^.State or gsttmpFlag; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSMine(Gear: PGear); -begin - // TODO: do real calculation? - if TestCollisionXwithGear(Gear, 2) - or (TestCollisionYwithGear(Gear, -2) <> 0) - or TestCollisionXwithGear(Gear, -2) - or (TestCollisionYwithGear(Gear, 2) <> 0) then - begin - if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then - begin - PlaySound(sndRopeAttach); - Gear^.dX:= _0; - Gear^.dY:= _0; - AddGearCI(Gear); - end; - end - else - begin - DeleteCI(Gear); - doStepFallingGear(Gear); - AllInactive := false; - CalcRotationDirAngle(Gear); - end; - - if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then - begin - 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 = 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end else - if (Gear^.Timer and $FF) = 0 then - PlaySound(sndMineTick); - - dec(Gear^.Timer); - end - end - else // gsttmpFlag = 0 - if (TurnTimeLeft = 0) - or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime)) - or (Gear^.Hedgehog^.Gear = nil) then - Gear^.State := Gear^.State or gsttmpFlag; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepDynamite(Gear: PGear); -begin - doStepFallingGear(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, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end; - dec(Gear^.Timer); -end; - -/////////////////////////////////////////////////////////////////////////////// - -procedure doStepRollingBarrel(Gear: PGear); -var - i: LongInt; - particle: PVisualGear; - dxdy: hwFloat; -begin - if (Gear^.dY.QWordValue = 0) and (Gear^.dY.QWordValue = 0) and (TestCollisionYwithGear(Gear, 1) = 0) then - SetLittle(Gear^.dY); - Gear^.State := Gear^.State or gstAnimation; - if Gear^.Health < cBarrelHealth then Gear^.State:= Gear^.State and not gstFrozen; - - if ((Gear^.dX.QWordValue <> 0) - or (Gear^.dY.QWordValue <> 0)) then - begin - DeleteCI(Gear); - AllInactive := false; - dxdy:= hwAbs(Gear^.dX)+hwAbs(Gear^.dY); - doStepFallingGear(Gear); - if (Gear^.State and gstCollision <> 0) and(dxdy > _0_4) then - begin - if (TestCollisionYwithGear(Gear, 1) <> 0) then - begin - Gear^.State := Gear^.State or gsttmpFlag; - for i:= min(12, hwRound(dxdy*_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.QWordValue / 21474836480) - end - end; - inc(Gear^.Damage, hwRound(dxdy * _50)) - end; - CalcRotationDirAngle(Gear); - //CheckGearDrowning(Gear) - end - else - 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) <> 0) then - Gear^.dY := _0; - if hwAbs(Gear^.dX) < _0_001 then - Gear^.dX := _0; - - if (Gear^.Health > 0) and ((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 - doStepCase(Gear); -end; - -procedure doStepCase(Gear: PGear); -var - i, x, y: LongInt; - k: TGearType; - dX, dY: HWFloat; - hog: PHedgehog; - sparkles: PVisualGear; - gi: PGear; -begin - k := Gear^.Kind; - - if (Gear^.Message and gmDestroy) > 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 (gmLJump or gmHJump)); - exit - end; - if (k = gtExplosives) and (Gear^.Health < cBarrelHealth) then Gear^.State:= Gear^.State and not gstFrozen; - - if ((k <> gtExplosives) and (Gear^.Damage > 0)) or ((k = gtExplosives) and (Gear^.Health<=0)) then - begin - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - hog:= Gear^.Hedgehog; - - DeleteGear(Gear); - // <-- delete gear! - - if k = gtCase then - begin - doMakeExplosion(x, y, 25, hog, 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, hog, EXPLAutoSound); - for i:= 0 to 31 do - begin - dX := AngleCos(i * 64) * _0_5 * (getrandomf + _1); - dY := AngleSin(i * 64) * _0_5 * (getrandomf + _1); - AddGear(x, y, gtFlame, 0, dX, dY, 0); - AddGear(x, y, gtFlame, gstTmpFlag, -dX, -dY, 0); - end - end; - 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 - begin - Gear^.doStep := @doStepRollingBarrel; - exit; - end - else 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; - end - else - begin - if (Gear^.Pos <> posCaseHealth) and (GameTicks and $1FFF = 0) then // stir 'em up periodically - begin - gi := GearsList; - while gi <> nil do - begin - if gi^.Kind = gtGenericFaller then - begin - gi^.Active:= true; - gi^.X:= int2hwFloat(GetRandom(rightX-leftX)+leftX); - gi^.Y:= int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY); - gi^.dX:= _90-(GetRandomf*_360); - gi^.dY:= _90-(GetRandomf*_360) - end; - gi := gi^.NextGear - end - end; - - if Gear^.Timer = 500 then - begin -(* Can't make sparkles team coloured without working out what the next team is going to be. This should be solved, really, since it also screws up - voices. Reinforcements voices is heard for active team, not team-to-be. Either that or change crate spawn from end of turn to start, although that - has its own complexities. *) - // Abuse a couple of gear values to track origin - Gear^.Angle:= hwRound(Gear^.Y); - Gear^.Tag:= random(2); - inc(Gear^.Timer) - end; - if Gear^.Timer < 1833 then inc(Gear^.Timer); - if Gear^.Timer = 1000 then - begin - sparkles:= AddVisualGear(hwRound(Gear^.X), Gear^.Angle, vgtDust, 1); - if sparkles <> nil then - begin - sparkles^.dX:= 0; - sparkles^.dY:= 0; - sparkles^.Angle:= 270; - if Gear^.Tag = 1 then - sparkles^.Tint:= $3744D7FF - else sparkles^.Tint:= $FAB22CFF - end; - end; - if Gear^.Timer < 1000 then - begin - AllInactive:= false; - exit - end - end; - - - if (Gear^.dY.QWordValue <> 0) - or (TestCollisionYwithGear(Gear, 1) = 0) then - begin - AllInactive := false; - - Gear^.dY := Gear^.dY + cGravity; - - if (Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, -1) <> 0) then - Gear^.dY := _0; - - Gear^.Y := Gear^.Y + Gear^.dY; - - if (not Gear^.dY.isNegative) and (Gear^.dY > _0_001) then - SetAllHHToActive(false); - - if (not Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, 1) <> 0) then - begin - if (Gear^.dY > _0_2) and (k = gtExplosives) then - inc(Gear^.Damage, hwRound(Gear^.dY * _70)); - - 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); -begin - if (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 - Gear^.Tag := 2 - else if Gear^.Tag = 2 then - if Gear^.Timer > 0 then - dec(Gear^.Timer) - else - begin - DeleteGear(Gear); - exit; - end; - - doStepCase(Gear) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepIdle(Gear: PGear); -begin - AllInactive := false; - dec(Gear^.Timer); - if Gear^.Timer = 0 then - begin - DeleteGear(Gear); - AfterAttack - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepShover(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.State := HHGear^.State or gstNoDamage; - DeleteCI(HHGear); - - AmmoShove(Gear, 30, 115); - - HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving; - Gear^.Timer := 250; - Gear^.doStep := @doStepIdle -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepWhip(Gear: PGear); -var - HHGear: PGear; - i: LongInt; -begin - HHGear := 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)) or gstMoving; - - Gear^.Timer := 250; - Gear^.doStep := @doStepIdle -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepFlame(Gear: PGear); -var - gX,gY,i: LongInt; - sticky: Boolean; - vgt: PVisualGear; - tdX,tdY: HWFloat; -begin - sticky:= (Gear^.State and gsttmpFlag) <> 0; - if not sticky then AllInactive := false; - - if TestCollisionYwithGear(Gear, 1) = 0 then - begin - AllInactive := false; - - if ((GameTicks mod 100) = 0) then - begin - vgt:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFire, gstTmpFlag); - if vgt <> nil then - begin - vgt^.dx:= 0; - vgt^.dy:= 0; - vgt^.FrameTicks:= 1800 div (Gear^.Tag mod 3 + 2); - end; - end; - - - if Gear^.dX.QWordValue > _0_01.QWordValue then - Gear^.dX := Gear^.dX * _0_995; - - Gear^.dY := Gear^.dY + cGravity; - // if sticky then Gear^.dY := Gear^.dY + cGravity; - - if Gear^.dY.QWordValue > _0_2.QWordValue then - Gear^.dY := Gear^.dY * _0_995; - - //if sticky 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 sticky and (GameTicks and $F = 0) then - begin - Gear^.Radius := 7; - tdX:= Gear^.dX; - tdY:= Gear^.dY; - Gear^.dX.QWordValue:= 120000000; - Gear^.dY.QWordValue:= 429496730; - Gear^.dX.isNegative:= getrandom(2)<>1; - Gear^.dY.isNegative:= true; - AmmoShove(Gear, 2, 125); - Gear^.dX:= tdX; - Gear^.dY:= tdY; - Gear^.Radius := 1 - end; - if Gear^.Timer > 0 then - begin - dec(Gear^.Timer); - inc(Gear^.Damage) - end - else - begin - gX := hwRound(Gear^.X); - gY := hwRound(Gear^.Y); - // Standard fire - if not sticky then - begin - if ((GameTicks and $1) = 0) then - begin - Gear^.Radius := 7; - tdX:= Gear^.dX; - tdY:= Gear^.dY; - Gear^.dX.QWordValue:= 214748365; - Gear^.dY.QWordValue:= 429496730; - Gear^.dX.isNegative:= getrandom(2)<>1; - Gear^.dY.isNegative:= true; - AmmoShove(Gear, 6, 100); - Gear^.dX:= tdX; - Gear^.dY:= tdY; - Gear^.Radius := 1; - end - else if ((GameTicks and $3) = 3) then - doMakeExplosion(gX, gY, 8, Gear^.Hedgehog, 0);//, EXPLNoDamage); - //DrawExplosion(gX, gY, 4); - - if ((GameTicks and $7) = 0) and (Random(2) = 0) then - for i:= Random(2) downto 0 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:= Random(3) downto 0 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 not sticky then - begin - if ((GameTicks and $3) = 0) and (Random(1) = 0) then - for i:= Random(2) downto 0 do - AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); - end - else - for i:= Random(3) downto 0 do - AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); - - DeleteGear(Gear) - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepFirePunchWork(Gear: PGear); -var - HHGear: PGear; -begin - AllInactive := false; - if ((Gear^.Message and gmDestroy) <> 0) then - begin - DeleteGear(Gear); - AfterAttack; - exit - end; - - HHGear := 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)), - lfIndestructible) then - HHGear^.Y := HHGear^.Y + HHGear^.dY -end; - -procedure doStepFirePunch(Gear: PGear); -var - HHGear: PGear; -begin - AllInactive := false; - HHGear := Gear^.Hedgehog^.Gear; - DeleteCI(HHGear); - //HHGear^.X := int2hwFloat(hwRound(HHGear^.X)) - _0_5; WTF? - 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); - - PlaySoundV(TSound(ord(sndFirePunch1) + GetRandom(6)), HHGear^.Hedgehog^.Team^.voicepack) -end; - -//////////////////////////////////////////////////////////////////////////////// - -procedure doStepParachuteWork(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - - inc(Gear^.Timer); - - if (TestCollisionYwithGear(HHGear, 1) <> 0) - or ((HHGear^.State and gstHHDriven) = 0) - or CheckGearDrowning(HHGear) - or ((Gear^.Message and gmAttack) <> 0) then - begin - with HHGear^ do - begin - Message := 0; - SetLittle(dX); - dY := _0; - State := State or gstMoving; - end; - DeleteGear(Gear); - isCursorVisible := false; - ApplyAmmoChanges(HHGear^.Hedgehog^); - exit - end; - - HHGear^.X := HHGear^.X + cWindSpeed * 200; - - if (Gear^.Message and gmLeft) <> 0 then - HHGear^.X := HHGear^.X - cMaxWindSpeed * 80 - - else if (Gear^.Message and gmRight) <> 0 then - HHGear^.X := HHGear^.X + cMaxWindSpeed * 80; - - if (Gear^.Message and gmUp) <> 0 then - HHGear^.Y := HHGear^.Y - cGravity * 40 - - else if (Gear^.Message and gmDown) <> 0 then - HHGear^.Y := HHGear^.Y + cGravity * 40; - - // don't drift into obstacles - if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then - HHGear^.X := HHGear^.X - int2hwFloat(hwSign(HHGear^.dX)); - HHGear^.Y := HHGear^.Y + cGravity * 100; - Gear^.X := HHGear^.X; - Gear^.Y := HHGear^.Y -end; - -procedure doStepParachute(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - - DeleteCI(HHGear); - - AfterAttack; - - HHGear^.State := HHGear^.State and (not (gstAttacking or gstAttacked or gstMoving)); - HHGear^.Message := HHGear^.Message and (not gmAttack); - - Gear^.doStep := @doStepParachuteWork; - - Gear^.Message := HHGear^.Message; - doStepParachuteWork(Gear) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepAirAttackWork(Gear: PGear); -begin - AllInactive := 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: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtNapalmBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0); - 3: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtDrill, gsttmpFlag, cBombsSpeed * Gear^.Tag, _0, Gear^.Timer + 1); - //4: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtWaterMelon, 0, cBombsSpeed * - // Gear^.Tag, _0, 5000); - end; - Gear^.dX := Gear^.dX + int2hwFloat(30 * Gear^.Tag); - StopSoundChan(Gear^.SoundChannel, 4000); - end; - - if (GameTicks and $3F) = 0 then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); - - if (hwRound(Gear^.X) > (max(LAND_WIDTH,4096)+2048)) or (hwRound(Gear^.X) < -2048) then - begin - // avoid to play forever (is this necessary?) - StopSoundChan(Gear^.SoundChannel); - DeleteGear(Gear) - end; -end; - -procedure doStepAirAttack(Gear: PGear); -begin - AllInactive := false; - - if Gear^.X.QWordValue = 0 then - begin - Gear^.Tag := 1; - Gear^.X := -_2048; - end - else - begin - Gear^.Tag := -1; - Gear^.X := int2hwFloat(max(LAND_WIDTH,4096) + 2048); - end; - - Gear^.Y := int2hwFloat(topY-300); - Gear^.dX := int2hwFloat(Gear^.Target.X - 5 * Gear^.Tag * 15); - - // calcs for Napalm Strike, so that it will hit the target (without wind at least :P) - if (Gear^.State = 2) then - Gear^.dX := Gear^.dX - cBombsSpeed * Gear^.Tag * 900 - // calcs for regular falling gears - else if (int2hwFloat(Gear^.Target.Y) - Gear^.Y > _0) then - Gear^.dX := Gear^.dX - cBombsSpeed * hwSqrt((int2hwFloat(Gear^.Target.Y) - Gear^.Y) * 2 / - cGravity) * Gear^.Tag; - - Gear^.Health := 6; - Gear^.doStep := @doStepAirAttackWork; - Gear^.SoundChannel := LoopSound(sndPlane, 4000); - -end; - -//////////////////////////////////////////////////////////////////////////////// - -procedure doStepAirBomb(Gear: PGear); -begin - AllInactive := false; - doStepFallingGear(Gear); - if (Gear^.State and gstCollision) <> 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - with mobileRecord do - if (performRumble <> nil) and (not fastUntilLag) then - performRumble(kSystemSoundID_Vibrate); - exit - end; - if (GameTicks and $3F) = 0 then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) -end; - -//////////////////////////////////////////////////////////////////////////////// - -procedure doStepGirder(Gear: PGear); -var - HHGear: PGear; - x, y, tx, ty: hwFloat; -begin - AllInactive := false; - - HHGear := Gear^.Hedgehog^.Gear; - tx := int2hwFloat(Gear^.Target.X); - ty := int2hwFloat(Gear^.Target.Y); - x := HHGear^.X; - y := HHGear^.Y; - - if (Distance(tx - x, ty - y) > _256) - or (not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprAmGirder].Width div 2, Gear^.Target.Y - SpritesData[sprAmGirder].Height div 2, sprAmGirder, Gear^.State, true, false)) then - begin - PlaySound(sndDenied); - HHGear^.Message := HHGear^.Message and (not gmAttack); - HHGear^.State := HHGear^.State and (not gstAttacking); - HHGear^.State := HHGear^.State or gstHHChooseTarget; - isCursorVisible := true; - DeleteGear(Gear) - end - else - begin - PlaySound(sndPlaced); - DeleteGear(Gear); - AfterAttack; - end; - - HHGear^.State := HHGear^.State and (not (gstAttacking or gstAttacked)); - HHGear^.Message := HHGear^.Message and (not gmAttack); -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepTeleportAfter(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - doStepHedgehogMoving(HHGear); - // if not infattack mode wait for hedgehog finish falling to collect cases - if ((GameFlags and gfInfAttack) <> 0) - or ((HHGear^.State and gstMoving) = 0) - or (Gear^.Hedgehog^.Gear^.Damage > 0) - or ((HHGear^.State and gstDrowning) = 1) then - begin - DeleteGear(Gear); - AfterAttack - end -end; - -procedure doStepTeleportAnim(Gear: PGear); -begin - if (Gear^.Hedgehog^.Gear^.Damage > 0) then - begin - DeleteGear(Gear); - AfterAttack; - end; - inc(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; -begin - AllInactive := false; - - HHGear := Gear^.Hedgehog^.Gear; - if not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprHHTelepMask].Width div 2, - Gear^.Target.Y - SpritesData[sprHHTelepMask].Height div 2, - sprHHTelepMask, 0, false, false) then - begin - HHGear^.Message := HHGear^.Message and (not gmAttack); - 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(Gear^.Target.X); - HHGear^.Y := int2hwFloat(Gear^.Target.Y); - HHGear^.State := HHGear^.State or gstMoving; - Gear^.Hedgehog^.Unplaced := false; - isCursorVisible := false; - playSound(sndWarp) - end; - Gear^.Target.X:= NoPointX -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSwitcherWork(Gear: PGear); -var - HHGear: PGear; - hedgehog: PHedgehog; - State: Longword; -begin - AllInactive := false; - - if ((Gear^.Message and (not gmSwitch)) <> 0) or (TurnTimeLeft = 0) then - begin - hedgehog := Gear^.Hedgehog; - //Msg := Gear^.Message and (not gmSwitch); - DeleteGear(Gear); - ApplyAmmoChanges(hedgehog^); - - HHGear := CurrentHedgehog^.Gear; - ApplyAmmoChanges(HHGear^.Hedgehog^); - //HHGear^.Message := Msg; - exit - end; - - if (Gear^.Message and gmSwitch) <> 0 then - begin - HHGear := CurrentHedgehog^.Gear; - HHGear^.Message := HHGear^.Message and (not gmSwitch); - Gear^.Message := Gear^.Message and (not gmSwitch); - State := HHGear^.State; - HHGear^.State := 0; - HHGear^.Z := cHHZ; - HHGear^.Active := false; - HHGear^.Message:= HHGear^.Message or gmRemoveFromList or gmAddToList; - - PlaySound(sndSwitchHog); - - repeat - CurrentTeam^.CurrHedgehog := Succ(CurrentTeam^.CurrHedgehog) mod (CurrentTeam^.HedgehogsNumber); - until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil) and - (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear^.Damage = 0) and - (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Effects[heFrozen]=0); - - SwitchCurrentHedgehog(@CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog]); - AmmoMenuInvalidated:= true; - - HHGear := CurrentHedgehog^.Gear; - HHGear^.State := State; - HHGear^.Active := true; - FollowGear := HHGear; - HHGear^.Z := cCurrHHZ; - HHGear^.Message:= HHGear^.Message or gmRemoveFromList or gmAddToList; - Gear^.X := HHGear^.X; - Gear^.Y := HHGear^.Y - end; -end; - -procedure doStepSwitcher(Gear: PGear); -var - HHGear: PGear; -begin - Gear^.doStep := @doStepSwitcherWork; - - HHGear := Gear^.Hedgehog^.Gear; - OnUsedAmmo(HHGear^.Hedgehog^); - with HHGear^ do - begin - State := State and (not gstAttacking); - Message := Message and (not gmAttack) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepMortar(Gear: PGear); -var - dX, dY, gdX, gdY: hwFloat; - i: LongInt; -begin - AllInactive := false; - gdX := Gear^.dX; - gdY := Gear^.dY; - - doStepFallingGear(Gear); - if (Gear^.State and gstCollision) <> 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound); - gdX.isNegative := not gdX.isNegative; - gdY.isNegative := not gdY.isNegative; - gdX:= gdX*_0_2; - gdY:= gdY*_0_2; - - for i:= 0 to 4 do - begin - dX := gdX + rndSign(GetRandomf) * _0_03; - dY := gdY + rndSign(GetRandomf) * _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 - begin - if hwRound(Gear^.Y) > cWaterLine then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) - else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepKamikazeWork(Gear: PGear); -var - i: LongWord; - HHGear: PGear; - sparkles: PVisualGear; - hasWishes: boolean; -begin - AllInactive := false; - hasWishes:= ((Gear^.Message and (gmPrecise or gmSwitch)) = (gmPrecise or gmSwitch)); - if hasWishes then - Gear^.AdvBounce:= 1; - - HHGear := Gear^.Hedgehog^.Gear; - if HHGear = nil then - begin - DeleteGear(Gear); - exit - end; - - HHGear^.State := HHGear^.State or gstNoDamage; - DeleteCI(HHGear); - - Gear^.X := HHGear^.X; - Gear^.Y := HHGear^.Y; - if (GameTicks mod 2 = 0) and hasWishes then - begin - sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust, 1); - if sparkles <> nil then - begin - sparkles^.Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF; - sparkles^.Angle:= random(360); - end - end; - - 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 - begin - if Gear^.AdvBounce <> 0 then - Gear^.Pos := 3 - else - Gear^.Pos := 2; - end; - - 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 + 7); - - upd := 0 - end; - - if Gear^.Health < Gear^.Damage then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); - if hasWishes then - for i:= 0 to 31 do - begin - sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot); - if sparkles <> nil then - with sparkles^ do - begin - Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF; - Angle:= random(360); - dx:= 0.001 * (random(200)); - dy:= 0.001 * (random(200)); - if random(2) = 0 then - dx := -dx; - if random(2) = 0 then - dy := -dy; - FrameTicks:= random(400) + 250 - end - end; - AfterAttack; - HHGear^.Message:= HHGear^.Message or gmDestroy; - DeleteGear(Gear); - end - else - begin - dec(Gear^.Health, Gear^.Damage); - Gear^.Damage := 0 - end -end; - -procedure doStepKamikazeIdle(Gear: PGear); -begin - AllInactive := false; - dec(Gear^.Timer); - if Gear^.Timer = 0 then - begin - Gear^.Pos := 1; - PlaySoundV(sndKamikaze, Gear^.Hedgehog^.Team^.voicepack); - Gear^.doStep := @doStepKamikazeWork - end -end; - -procedure doStepKamikaze(Gear: PGear); -var - HHGear: PGear; -begin - AllInactive := false; - - HHGear := 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 := @doStepKamikazeIdle -end; - -//////////////////////////////////////////////////////////////////////////////// - -const cakeh = 27; -var - CakePoints: array[0..Pred(cakeh)] of record - x, y: hwFloat; - end; - CakeI: Longword; - -procedure doStepCakeExpl(Gear: PGear); -begin - AllInactive := false; - - inc(Gear^.Tag); - if Gear^.Tag < 2250 then - exit; - - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg, Gear^.Hedgehog, EXPLAutoSound); - AfterAttack; - DeleteGear(Gear) -end; - -procedure doStepCakeDown(Gear: PGear); -var - gi: PGear; - dmg, dmgBase: LongInt; - fX, fY, tdX, tdY: hwFloat; -begin - AllInactive := false; - - inc(Gear^.Tag); - if Gear^.Tag < 100 then - exit; - Gear^.Tag := 0; - - if Gear^.Pos = 0 then - begin -///////////// adapted from doMakeExplosion /////////////////////////// - //fX:= Gear^.X; - //fY:= Gear^.Y; - //fX.QWordValue:= fX.QWordValue and $FFFFFFFF00000000; - //fY.QWordValue:= fY.QWordValue and $FFFFFFFF00000000; - fX:= int2hwFloat(hwRound(Gear^.X)); - fY:= int2hwFloat(hwRound(Gear^.Y)); - dmgBase:= cakeDmg shl 1 + cHHRadius div 2; - gi := GearsList; - while gi <> nil do - begin - if gi^.Kind = gtHedgehog then - begin - dmg:= 0; - tdX:= gi^.X-fX; - tdY:= gi^.Y-fY; - if hwRound(hwAbs(tdX)+hwAbs(tdY)) < dmgBase then - dmg:= dmgBase - max(hwRound(Distance(tdX, tdY)),gi^.Radius); - if (dmg > 1) then dmg:= ModifyDamage(min(dmg div 2, cakeDmg), gi); - if (dmg > 1) then - if (CurrentHedgehog^.Gear = gi) and (not gi^.Invulnerable) then - gi^.State := gi^.State or gstLoser - else - gi^.State := gi^.State or gstWinner; - end; - gi := gi^.NextGear - end; -////////////////////////////////////////////////////////////////////// - Gear^.doStep := @doStepCakeExpl; - PlaySound(sndCake) - end - else dec(Gear^.Pos) -end; - - -procedure doStepCakeWork(Gear: PGear); -var - tdx, tdy: hwFloat; -begin - AllInactive := false; - - inc(Gear^.Tag); - if Gear^.Tag < 7 then - exit; - - dec(Gear^.Health); - Gear^.Timer := Gear^.Health*10; - if Gear^.Health mod 100 = 0 then - Gear^.PortalCounter:= 0; - // This is not seconds, but at least it is *some* feedback - if (Gear^.Health = 0) or ((Gear^.Message and gmAttack) <> 0) then - begin - FollowGear := Gear; - Gear^.RenderTimer := false; - Gear^.doStep := @doStepCakeDown; - exit - end; - - cakeStep(Gear); - - 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; -end; - -procedure doStepCakeUp(Gear: PGear); -var - i: Longword; -begin - AllInactive := 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); -begin - AllInactive := false; - - Gear^.dY := Gear^.dY + cGravity; - if TestCollisionYwithGear(Gear, 1) <> 0 then - Gear^.doStep := @doStepCakeUp - else - begin - Gear^.Y := Gear^.Y + Gear^.dY; - if CheckGearDrowning(Gear) then - AfterAttack - end -end; - -procedure doStepCake(Gear: PGear); -var - HHGear: PGear; -begin - AllInactive := false; - - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.Message := HHGear^.Message and (not gmAttack); - Gear^.CollisionMask:= lfNotCurrentMask; - - FollowGear := Gear; - - Gear^.doStep := @doStepCakeFall -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSeductionWork(Gear: PGear); -var i: LongInt; - hogs: PGearArrayS; -begin - AllInactive := false; - hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Radius); - if hogs.size > 0 then - begin - for i:= 0 to hogs.size - 1 do - with hogs.ar^[i]^ do - begin - if hogs.ar^[i] <> CurrentHedgehog^.Gear then - begin - dX:= _50 * cGravity * (Gear^.X - X) / _25; - dY:= -_450 * cGravity; - Active:= true; - end - end; - end ; - AfterAttack; - DeleteGear(Gear); -(* - 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 - else - else - begin - AfterAttack; - DeleteGear(Gear) - end*) -end; - -procedure doStepSeductionWear(Gear: PGear); -var heart: PVisualGear; -begin - AllInactive := false; - inc(Gear^.Timer); - if Gear^.Timer > 250 then - begin - Gear^.Timer := 0; - inc(Gear^.Pos); - if Gear^.Pos = 5 then - PlaySoundV(sndYoohoo, Gear^.Hedgehog^.Team^.voicepack) - end; - - if (Gear^.Pos = 14) and (RealTicks and $3 = 0) then - begin - heart:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot); - if heart <> nil then - with heart^ do - begin - dx:= 0.001 * (random(200)); - dy:= 0.001 * (random(200)); - if random(2) = 0 then - dx := -dx; - if random(2) = 0 then - dy := -dy; - FrameTicks:= random(750) + 1000; - State:= ord(sprSeduction) - end; - end; - - if Gear^.Pos = 15 then - Gear^.doStep := @doStepSeductionWork -end; - -procedure doStepSeduction(Gear: PGear); -begin - AllInactive := false; - //DeleteCI(Gear^.Hedgehog^.Gear); - Gear^.doStep := @doStepSeductionWear -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepWaterUp(Gear: PGear); -var - i: LongWord; -begin - if (Gear^.Tag = 0) - or (cWaterLine = 0) then - begin - DeleteGear(Gear); - exit - end; - - AllInactive := false; - - inc(Gear^.Timer); - if Gear^.Timer = 17 then - Gear^.Timer := 0 - else - exit; - - if cWaterLine > 0 then - begin - dec(cWaterLine); - for i:= 0 to LAND_WIDTH - 1 do - Land[cWaterLine, i] := 0; - SetAllToActive - end; - - dec(Gear^.Tag); -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepDrill(Gear: PGear); -forward; - -procedure doStepDrillDrilling(Gear: PGear); -var - t: PGearArray; - tempColl: Word; -begin - AllInactive := false; - if (Gear^.Timer > 0) and (Gear^.Timer mod 10 <> 0) then - begin - dec(Gear^.Timer); - exit; - end; - - DrawTunnel(Gear^.X, Gear^.Y, Gear^.dX, Gear^.dY, 2, 6); - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - if (Gear^.Timer mod 30) = 0 then - AddVisualGear(hwRound(Gear^.X + _20 * Gear^.dX), hwRound(Gear^.Y + _20 * Gear^.dY), vgtDust); - if (CheckGearDrowning(Gear)) then - begin - StopSoundChan(Gear^.SoundChannel); - exit - end; - - tempColl:= Gear^.CollisionMask; - Gear^.CollisionMask:= $007F; - if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) <> 0) or TestCollisionXWithGear(Gear, hwSign(Gear^.dX)) or (GameTicks > Gear^.FlightTime) then - t := CheckGearsCollision(Gear) - else t := nil; - Gear^.CollisionMask:= tempColl; - //fixes drill not exploding when touching HH bug - - if (Gear^.Timer = 0) or ((t <> nil) and (t^.Count <> 0)) - or ( ((Gear^.State and gsttmpFlag) = 0) and (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and (not TestCollisionXWithGear(Gear, hwSign(Gear^.dX)))) -// CheckLandValue returns true if the type isn't matched - or (not CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y), lfIndestructible)) then - begin - //out of time or exited ground - StopSoundChan(Gear^.SoundChannel); - if (Gear^.State and gsttmpFlag) <> 0 then - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound) - else - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end - - else if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and (not TestCollisionXWithGear(Gear, hwSign(Gear^.dX))) then - begin - StopSoundChan(Gear^.SoundChannel); - Gear^.Tag := 1; - Gear^.doStep := @doStepDrill - end; - - dec(Gear^.Timer); -end; - -procedure doStepDrill(Gear: PGear); -var - t: PGearArray; - oldDx, oldDy: hwFloat; - t2: hwFloat; -begin - AllInactive := false; - - if (Gear^.State and gsttmpFlag) = 0 then - Gear^.dX := Gear^.dX + cWindSpeed; - - oldDx := Gear^.dX; - oldDy := Gear^.dY; - - doStepFallingGear(Gear); - - if (GameTicks and $3F) = 0 then - begin - if hwRound(Gear^.Y) > cWaterLine then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) - else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) - end; - - if ((Gear^.State and gstCollision) <> 0) then - begin - //hit - Gear^.dX := oldDx; - Gear^.dY := oldDy; - - if GameTicks > Gear^.FlightTime then - t := CheckGearsCollision(Gear) - else - t := nil; - if (t = nil) or (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 if (t <> nil) then - begin - //explode right on contact with HH - if (Gear^.State and gsttmpFlag) <> 0 then - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound) - else - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit; - end; - - Gear^.SoundChannel := LoopSound(sndDrillRocket); - Gear^.doStep := @doStepDrillDrilling; - - if (Gear^.State and gsttmpFlag) <> 0 then - gear^.RenderTimer:= true; - if Gear^.Timer > 0 then dec(Gear^.Timer) - end - else if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Tag <> 0) then - begin - if Gear^.Timer > 0 then - dec(Gear^.Timer) - else - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - end - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepBallgunWork(Gear: PGear); -var - HHGear, ball: PGear; - rx, ry: hwFloat; - gX, gY: LongInt; -begin - AllInactive := false; - dec(Gear^.Timer); - HHGear := Gear^.Hedgehog^.Gear; - HedgehogChAngle(HHGear); - gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); - gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); - if (Gear^.Timer mod 100) = 0 then - begin - rx := rndSign(getRandomf * _0_1); - ry := rndSign(getRandomf * _0_1); - - ball:= AddGear(gx, gy, gtBall, 0, SignAs(AngleSin(HHGear^.Angle) * _0_8, HHGear^.dX) + rx, AngleCos(HHGear^.Angle) * ( - _0_8) + ry, 0); - ball^.CollisionMask:= lfNotCurrentMask; - - PlaySound(sndGun); - end; - - if (Gear^.Timer = 0) or ((HHGear^.State and gstHHDriven) = 0) then - begin - DeleteGear(Gear); - AfterAttack - end -end; - -procedure doStepBallgun(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown)); - HHGear^.State := HHGear^.State or gstNotKickable; - Gear^.doStep := @doStepBallgunWork -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepRCPlaneWork(Gear: PGear); - -const cAngleSpeed = 3; -var - HHGear: PGear; - i: LongInt; - dX, dY: hwFloat; - fChanged: boolean; - trueAngle: Longword; - t: PGear; -begin - AllInactive := false; - - HHGear := Gear^.Hedgehog^.Gear; - FollowGear := Gear; - - if Gear^.Timer > 0 then - dec(Gear^.Timer); - - 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 - end - else - begin - if ((Gear^.Message and gmLeft) <> 0) then - begin - fChanged := true; - Gear^.Angle := (Gear^.Angle + (4096 - cAngleSpeed)) mod 4096 - end; - - if ((Gear^.Message and gmRight) <> 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 (GameTicks and $FF) = 0 then - if Gear^.Timer < 3500 then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace) - else - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); - - if ((HHGear^.Message and gmAttack) <> 0) and (Gear^.Health <> 0) then - begin - HHGear^.Message := HHGear^.Message and (not gmAttack); - 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 gmLJump) <> 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); - - CheckCollision(Gear); - - if ((Gear^.State and gstCollision) <> 0) or CheckGearDrowning(Gear) then - begin - StopSoundChan(Gear^.SoundChannel); - StopSound(sndRideOfTheValkyries); - ResumeMusic; - - if ((Gear^.State and gstCollision) <> 0) then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, Gear^.Hedgehog, EXPLAutoSound); - for i:= 0 to 15 do - begin - dX := AngleCos(i * 64) * _0_5 * (GetRandomf + _1); - dY := AngleSin(i * 64) * _0_5 * (GetRandomf + _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; - if (GameFlags and gfInfAttack) = 0 then - begin - if TagTurnTimeLeft = 0 then - TagTurnTimeLeft:= TurnTimeLeft; - - TurnTimeLeft:= 14 * 125; - end; - - HHGear^.Message := 0; - ParseCommand('/taunt ' + #1, true) - end -end; - -procedure doStepRCPlane(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := 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 := @doStepRCPlaneWork -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepJetpackWork(Gear: PGear); -var - HHGear: PGear; - fuel, i: LongInt; - move: hwFloat; - isUnderwater: Boolean; - bubble: PVisualGear; -begin - isUnderwater:= cWaterLine < hwRound(Gear^.Y) + Gear^.Radius; - if Gear^.Pos > 0 then - dec(Gear^.Pos); - AllInactive := false; - HHGear := Gear^.Hedgehog^.Gear; - //dec(Gear^.Timer); - move := _0_2; - fuel := 50; -(*if (HHGear^.Message and gmPrecise) <> 0 then - begin - move:= _0_02; - fuel:= 5; - end;*) - if HHGear^.Message and gmPrecise <> 0 then - HedgehogChAngle(HHGear) - else if Gear^.Health > 0 then - begin - if HHGear^.Message and gmUp <> 0 then - begin - if (not HHGear^.dY.isNegative) or (HHGear^.Y > -_256) then - begin - if isUnderwater then - begin - HHGear^.dY := HHGear^.dY - (move * _0_7); - for i:= random(10)+10 downto 0 do - begin - bubble := AddVisualGear(hwRound(HHGear^.X) - 8 + random(16), hwRound(HHGear^.Y) + 16 + random(8), vgtBubble); - if bubble <> nil then - bubble^.dY:= random(20)/10+0.1; - end - end - else HHGear^.dY := HHGear^.dY - move; - end; - dec(Gear^.Health, fuel); - Gear^.MsgParam := Gear^.MsgParam or gmUp; - Gear^.Timer := GameTicks - end; - move.isNegative := (HHGear^.Message and gmLeft) <> 0; - if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then - begin - HHGear^.dX := HHGear^.dX + (move * _0_1); - if isUnderwater then - begin - for i:= random(5)+5 downto 0 do - begin - bubble := AddVisualGear(hwRound(HHGear^.X)+random(8), hwRound(HHGear^.Y) - 8 + random(16), vgtBubble); - if bubble <> nil then - begin - bubble^.dX:= (random(10)/10 + 0.02) * -1; - if (move.isNegative) then - begin - bubble^.X := bubble^.X + 28; - bubble^.dX:= bubble^.dX * (-1) - end - else bubble^.X := bubble^.X - 28; - end; - end - end; - dec(Gear^.Health, fuel div 5); - Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight)); - Gear^.Timer := GameTicks - end - 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; - - i:= Gear^.Health div 20; - - if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then - begin - Gear^.Damage:= i; - //AddCaption('Fuel: '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate); - FreeTexture(Gear^.Tex); - Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + '%', cWhiteColor, fntSmall) - end; - - if (HHGear^.Message and (gmAttack or gmUp or gmLeft or gmRight) <> 0) and - (HHGear^.Message and gmPrecise = 0) then - Gear^.State := Gear^.State and (not gsttmpFlag); - - if HHGear^.Message and gmPrecise = 0 then - HHGear^.Message := HHGear^.Message and (not (gmUp or gmLeft or gmRight)); - HHGear^.State := HHGear^.State or gstMoving; - - Gear^.X := HHGear^.X; - Gear^.Y := HHGear^.Y; - - if not isUnderWater and hasBorder and ((HHGear^.X < _0) - or (hwRound(HHGear^.X) > LAND_WIDTH)) then - HHGear^.dY.isNegative:= false; - - if ((Gear^.State and gsttmpFlag) = 0) - or (HHGear^.dY < _0) then - doStepHedgehogMoving(HHGear); - - if // (Gear^.Health = 0) - (HHGear^.Damage <> 0) - //or CheckGearDrowning(HHGear) - or (cWaterLine + cVisibleWater * 4 < hwRound(HHGear^.Y)) - 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) <> 0)) - or ((Gear^.Message and gmAttack) <> 0) then - begin - with HHGear^ do - begin - Message := 0; - Active := true; - State := State or gstMoving - end; - DeleteGear(Gear); - isCursorVisible := false; - ApplyAmmoChanges(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); - end -end; - -procedure doStepJetpack(Gear: PGear); -var - HHGear: PGear; -begin - Gear^.Pos:= 0; - Gear^.doStep := @doStepJetpackWork; - - HHGear := Gear^.Hedgehog^.Gear; - FollowGear := HHGear; - AfterAttack; - with HHGear^ do - begin - State := State and (not gstAttacking); - Message := Message and (not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight)); - - if (dY < _0_1) and (dY > -_0_1) then - begin - Gear^.State := Gear^.State or gsttmpFlag; - dY := dY - _0_2 - end - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepBirdyDisappear(Gear: PGear); -begin - AllInactive := 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; -begin - HHGear := Gear^.Hedgehog^.Gear; - if HHGear = nil then - begin - DeleteGear(Gear); - exit - end; - - move := _0_2; - fuel := 50; - - if Gear^.Pos > 0 then - dec(Gear^.Pos, 1) - else if (HHGear^.Message and (gmLeft or gmRight or gmUp)) <> 0 then - Gear^.Pos := 500; - - if HHGear^.dX.isNegative then - Gear^.Tag := -1 - else - Gear^.Tag := 1; - - if (HHGear^.Message and gmUp) <> 0 then - begin - if (not HHGear^.dY.isNegative) - or (HHGear^.Y > -_256) then - HHGear^.dY := HHGear^.dY - move; - - dec(Gear^.Health, fuel); - Gear^.MsgParam := Gear^.MsgParam or gmUp; - end; - - if (HHGear^.Message and gmLeft) <> 0 then move.isNegative := true; - if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then - begin - HHGear^.dX := HHGear^.dX + (move * _0_1); - dec(Gear^.Health, fuel div 5); - Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight)); - 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 gmAttack <> 0) then - begin - HHGear^.Message := HHGear^.Message and (not gmAttack); - 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 (gmUp or gmPrecise or gmLeft or gmRight) <> 0 then - Gear^.State := Gear^.State and (not gsttmpFlag); - - HHGear^.Message := HHGear^.Message and (not (gmUp or gmPrecise or gmLeft or gmRight)); - 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. - // this is probably not needed anymore - if not CurrentTeam^.ExtDriven 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) <> 0)) - or ((Gear^.Message and gmAttack) <> 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; - end -end; - -procedure doStepBirdyDescend(Gear: PGear); -var - HHGear: PGear; -begin - if Gear^.Timer > 0 then - dec(Gear^.Timer, 1) - else if Gear^.Hedgehog^.Gear = nil then - begin - DeleteGear(Gear); - AfterAttack; - exit - end; - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.Message := HHGear^.Message and (not (gmUp or gmPrecise or gmLeft or gmRight)); - if abs(hwRound(HHGear^.Y - Gear^.Y)) > 32 then - begin - if Gear^.Timer = 0 then - Gear^.Y := Gear^.Y + _0_1 - end - else if Gear^.Timer = 0 then - begin - Gear^.doStep := @doStepBirdyFly; - HHGear^.dY := -_0_2 - end -end; - -procedure doStepBirdyAppear(Gear: PGear); -begin - Gear^.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; - end -end; - -procedure doStepBirdy(Gear: PGear); -var - HHGear: PGear; -begin - gear^.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 := -1 - else - Gear^.Tag := 1; - Gear^.Pos := 0; - AllInactive := false; - FollowGear := HHGear; - with HHGear^ do - begin - State := State and (not gstAttacking); - Message := Message and (not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight)) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -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, Gear^.Hedgehog, EXPLPoisoned, $C0E0FFE0); - 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.QWordValue / 21474836480); - end; - - DeleteGear(Gear); - exit - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doPortalColorSwitch(); -var CurWeapon: PAmmo; -begin - if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and ((CurrentHedgehog^.Gear^.Message and gmSwitch) <> 0) then - with CurrentHedgehog^ do - if (CurAmmoType = amPortalGun) then - begin - CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and (not gmSwitch); - - CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^); - if CurWeapon^.Pos <> 0 then - CurWeapon^.Pos := 0 - - else - CurWeapon^.Pos := 1; - end; -end; - -procedure doStepPortal(Gear: PGear); -var - iterator, conPortal: PGear; - s, r, nx, ny, ox, oy, poffs, noffs, pspeed, nspeed, - resetx, resety, resetdx, resetdy: hwFloat; - sx, sy, rh, resetr: LongInt; - hasdxy, isbullet, iscake, isCollision: Boolean; -begin - doPortalColorSwitch(); - - // destroy portal if ground it was attached too is gone - if (Land[hwRound(Gear^.Y), hwRound(Gear^.X)] <= lfAllObjMask) - or (Gear^.Timer < 1) - or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team) - or (hwRound(Gear^.Y) > cWaterLine) then - begin - deleteGear(Gear); - EXIT; - end; - - if (TurnTimeLeft < 1) - or (Gear^.Health < 1) then - dec(Gear^.Timer); - - if Gear^.Timer < 10000 then - gear^.RenderTimer := true; - - // abort if there is no other portal connected to this one - if (Gear^.LinkedGear = nil) then - exit; - if ((Gear^.LinkedGear^.Tag and 1) = 0) then // or if it's still moving; - exit; - - conPortal := Gear^.LinkedGear; - - // check all gears for stuff to port through - iterator := nil; - while true do - begin - - // iterate through GearsList - if iterator = nil then - iterator := GearsList - else - iterator := iterator^.NextGear; - - // end of list? - if iterator = nil then - break; - - // don't port portals or other gear that wouldn't make sense - if (iterator^.Kind in [gtPortal, gtRope, gtAirAttack, gtIceGun]) - or (iterator^.PortalCounter > 32) then - continue; - - // don't port hogs on rope - // TODO: this will also prevent hogs while falling after rope use from - // falling through portals... fix that! - - // check if gear fits through portal - if (iterator^.Radius > Gear^.Radius) then - continue; - - // this is the max range we accept incoming gears in - r := Int2hwFloat(iterator^.Radius+Gear^.Radius); - - // too far away? - if (iterator^.X < Gear^.X - r) - or (iterator^.X > Gear^.X + r) - or (iterator^.Y < Gear^.Y - r) - or (iterator^.Y > Gear^.Y + r) then - continue; - - hasdxy := (((iterator^.dX.QWordValue <> 0) or (iterator^.dY.QWordValue <> 0)) or ((iterator^.State or gstMoving) = 0)); - - // in case the object is not moving, let's asume it's falling towards the portal - if not hasdxy then - begin - if Gear^.Y < iterator^.Y then - continue; - ox:= Gear^.X - iterator^.X; - oy:= Gear^.Y - iterator^.Y; - end - else - begin - ox:= iterator^.dX; - oy:= iterator^.dY; - end; - - // cake will need extra treatment... it's so delicious and moist! - iscake:= (iterator^.Kind = gtCake); - - // won't port stuff that does not move towards the front/portal entrance - if iscake then - begin - if not (((iterator^.X - Gear^.X)*ox + (iterator^.Y - Gear^.Y)*oy).isNegative) then - continue; - end - else - if not ((Gear^.dX*ox + Gear^.dY*oy).isNegative) then - continue; - - isbullet:= (iterator^.Kind in [gtShotgunShot, gtDEagleShot, gtSniperRifleShot, gtSineGunShot]); - - r:= int2hwFloat(iterator^.Radius); - - if not (isbullet or iscake) then - begin - // wow! good candidate there, let's see if the distance and direction is okay! - if hasdxy then - begin - s := Distance(iterator^.dX, iterator^.dY); - // if the resulting distance is 0 skip this gear - if s.QWordValue = 0 then - continue; - s := r / s; - ox:= iterator^.X + s * iterator^.dX; - oy:= iterator^.Y + s * iterator^.dY; - end - else - begin - ox:= iterator^.X; - oy:= iterator^.Y + r; - end; - - if (hwRound(Distance(Gear^.X-ox,Gear^.Y-oy)) > Gear^.Radius + 1 ) then - continue; - end; - - // draw bullet trail - if isbullet then - spawnBulletTrail(iterator); - - // calc gear offset in portal vector direction - ox := (iterator^.X - Gear^.X); - oy := (iterator^.Y - Gear^.Y); - poffs:= (Gear^.dX * ox + Gear^.dY * oy); - - if not isBullet and poffs.isNegative then - continue; - - // only port bullets close to the portal - if isBullet and (not (hwAbs(poffs) < _3)) then - continue; - - // - // gears that make it till here will definately be ported - // - // (but old position/movement vector might be restored in case there's - // not enough space on the other side) - // - - resetr := iterator^.Radius; - resetx := iterator^.X; - resety := iterator^.Y; - resetdx := iterator^.dX; - resetdy := iterator^.dY; - - // create a normal of the portal vector, but ... - nx := Gear^.dY; - ny := Gear^.dX; - // ... decide where the top is based on the hog's direction when firing the portal - if Gear^.Elasticity.isNegative then - nx.isNegative := (not nx.isNegative) - else - ny.isNegative := not ny.isNegative; - - // calc gear offset in portal normal vector direction - noffs:= (nx * ox + ny * oy); - - if isBullet and (noffs.Round >= Longword(Gear^.Radius)) then - continue; - - // avoid gravity related loops of not really moving gear - if not (iscake or isbullet) - and (Gear^.dY.isNegative) - and (conPortal^.dY.isNegative) - and ((iterator^.dX.QWordValue + iterator^.dY.QWordValue) < _0_08.QWordValue) - and (iterator^.PortalCounter > 0) then - continue; - - // calc gear speed along to the vector and the normal vector of the portal - if hasdxy then - begin - pspeed:= (Gear^.dX * iterator^.dX + Gear^.dY * iterator^.dY); - nspeed:= (nx * iterator^.dX + ny * iterator^.dY); - end - else - begin - pspeed:= hwAbs(cGravity * oy); - nspeed:= _0; - end; - - // creating normal vector of connected (exit) portal - nx := conPortal^.dY; - ny := conPortal^.dX; - if conPortal^.Elasticity.isNegative then - nx.isNegative := (not nx.isNegative) - else - ny.isNegative := not ny.isNegative; - - // inverse cake's normal movement direction, - // as if it just walked through a hole - //if iscake then nspeed.isNegative:= not nspeed.isNegative; - -//AddFileLog('poffs:'+cstr(poffs)+' noffs:'+cstr(noffs)+' pspeed:'+cstr(pspeed)+' nspeed:'+cstr(nspeed)); - iterator^.dX := -pspeed * conPortal^.dX + nspeed * nx; - iterator^.dY := -pspeed * conPortal^.dY + nspeed * ny; - - // make the gear's exit position close to the portal while - // still respecting the movement direction - - // determine the distance (in exit vector direction) - // that we want the gear at - if iscake then - ox:= (r - _0_7) - else - ox:= (r * _1_5); - s:= ox / poffs; - poffs:= ox; - if (nspeed.QWordValue <> 0) - and (pspeed > _0) then - noffs:= noffs * s * (nspeed / pspeed); - - // move stuff with high normal offset closer to the portal's center - if not isbullet then - begin - s := hwAbs(noffs) + r - int2hwFloat(Gear^.Radius); - if s > _0 then - noffs:= noffs - SignAs(s,noffs) - end; - - iterator^.X := conPortal^.X + poffs * conPortal^.dX + noffs * nx; - iterator^.Y := conPortal^.Y + poffs * conPortal^.dY + noffs * ny; - - if not hasdxy and (not (conPortal^.dY.isNegative)) then - begin - iterator^.dY:= iterator^.dY + hwAbs(cGravity * (iterator^.Y - conPortal^.Y)) - end; - - // see if the space on the exit side actually is enough - - if not (isBullet or isCake) then - begin - // TestCollisionXwithXYShift requires a hwFloat for xShift - ox.QWordValue := _1.QWordValue; - ox.isNegative := not iterator^.dX.isNegative; - - sx := hwSign(iterator^.dX); - sy := hwSign(iterator^.dY); - - if iterator^.Radius > 1 then - iterator^.Radius := iterator^.Radius - 1; - - // check front - isCollision := TestCollisionY(iterator, sy) - or TestCollisionX(iterator, sx); - - if not isCollision then - begin - // check center area (with half the radius so that the - // the square check won't check more pixels than we want to) - iterator^.Radius := 1 + resetr div 2; - rh := resetr div 4; - isCollision := TestCollisionYwithXYShift(iterator, 0, -sy * rh, sy, false) - or TestCollisionXwithXYShift(iterator, ox * rh, 0, sx, false); - end; - - iterator^.Radius := resetr; - - if isCollision then - begin - // collision! oh crap! go back! - iterator^.X := resetx; - iterator^.Y := resety; - iterator^.dX := resetdx; - iterator^.dY := resetdy; - continue; - end; - end; - - // - // You're now officially portaled! - // - - // Until loops are reliably broken - if iscake then - iterator^.PortalCounter:= 33 - else - begin - inc(iterator^.PortalCounter); - iterator^.Active:= true; - iterator^.State:= iterator^.State and (not gstHHHJump) or gstMoving; - end; - - // is it worth adding an arcsin table? Just how often would we end up doing something like this? - // SYNCED ANGLE UPDATE - if iterator^.Kind = gtRCPlane then - begin - // recycling as temp vars - resety.isNegative:= false; - resety.QWordValue:= 4294967296 * 112; - resetx.isNegative:= false; - resetx.QWordValue:= 4294967296 * 35; - resetdx.isNegative:= false; - resetdx.QWordValue:= 4294967296 * 1152; - - resetdy:=hwAbs(iterator^.dX*4); - resetdy:= resetdy + hwPow(resetdy,3)/_6 + _3 * hwPow(resetdy,5) / _40 + _5 * hwPow(resetdy,7) / resety + resetx * hwPow(resetdy,9) / resetdx; - iterator^.Angle:= hwRound(resetdy*_2048 / _PI); - if not iterator^.dY.isNegative then iterator^.Angle:= 2048-iterator^.Angle; - if iterator^.dX.isNegative then iterator^.Angle:= 4096-iterator^.Angle; - end - // VISUAL USE OF ANGLE ONLY - else if (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtKamikaze) and (CurAmmoGear^.Hedgehog = iterator^.Hedgehog) then - begin - iterator^.Angle:= DxDy2AttackAngle(iterator^.dX, iterator^.dY); - iterator^.Angle:= 2048-iterator^.Angle; - if iterator^.dX.isNegative then iterator^.Angle:= 4096-iterator^.Angle; - end; - - if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) - and (iterator = CurrentHedgehog^.Gear) - and (CurAmmoGear <> nil) - and (CurAmmoGear^.Kind =gtRope) then - CurAmmoGear^.PortalCounter:= 1; - - if not isbullet and (iterator^.State and gstInvisible = 0) - and (iterator^.Kind <> gtFlake) then - FollowGear := iterator; - - // store X/Y values of exit for net bullet trail - if isbullet then - begin - iterator^.Elasticity:= iterator^.X; - iterator^.Friction := iterator^.Y; - end; - - if Gear^.Health > 1 then - dec(Gear^.Health); - end; -end; - - - -procedure loadNewPortalBall(oldPortal: PGear; destroyGear: Boolean); -var - CurWeapon: PAmmo; -begin - if CurrentHedgehog <> nil then - with CurrentHedgehog^ do - begin - CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^); - if (CurAmmoType = amPortalGun) then - begin - if not destroyGear then - begin - // switch color of ball to opposite of oldPortal - if (oldPortal^.Tag and 2) = 0 then - CurWeapon^.Pos:= 1 - else - CurWeapon^.Pos:= 0; - end; - - // make the ball visible - CurWeapon^.Timer := 0; - end - end; - if destroyGear then - oldPortal^.Timer:= 0; -end; - -procedure doStepMovingPortal_real(Gear: PGear); -var - x, y, tx, ty: LongInt; - s: hwFloat; -begin - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - tx := 0; - ty := 0; - // avoid compiler hints - - if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] > 255) then - begin - Gear^.State := Gear^.State or gstCollision; - Gear^.State := Gear^.State and (not gstMoving); - - if (Land[y, x] and lfBouncy <> 0) - or (not CalcSlopeTangent(Gear, x, y, tx, ty, 255)) - or (DistanceI(tx,ty) < _12) then // reject shots at too irregular terrain - begin - loadNewPortalBall(Gear, true); - EXIT; - end; - - // making a normalized normal vector - s := _1/DistanceI(tx,ty); - Gear^.dX := s * ty; - Gear^.dY := -s * tx; - - Gear^.DirAngle := DxDy2Angle(-Gear^.dY,Gear^.dX); - if not Gear^.dX.isNegative then - Gear^.DirAngle := 180-Gear^.DirAngle; - - if ((Gear^.LinkedGear = nil) - or (hwRound(Distance(Gear^.X - Gear^.LinkedGear^.X,Gear^.Y-Gear^.LinkedGear^.Y)) >=Gear^.Radius*2)) then - begin - loadNewPortalBall(Gear, false); - inc(Gear^.Tag); - Gear^.doStep := @doStepPortal; - end - else - loadNewPortalBall(Gear, true); - end - - else if (y > cWaterLine) - or (y < -max(LAND_WIDTH,4096)) - or (x > 2*max(LAND_WIDTH,4096)) - or (x < -max(LAND_WIDTH,4096)) then - loadNewPortalBall(Gear, true); -end; - -procedure doStepMovingPortal(Gear: PGear); -begin - doPortalColorSwitch(); - doStepPerPixel(Gear, @doStepMovingPortal_real, true); - if (Gear^.Timer < 1) - or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team) then - deleteGear(Gear); -end; - -procedure doStepPortalShot(newPortal: PGear); -var - iterator: PGear; - s: hwFloat; - CurWeapon: PAmmo; -begin - s:= Distance (newPortal^.dX, newPortal^.dY); - - // Adds the hog speed (only that part in/directly against shot direction) - // to the shot speed (which we triple previously btw) - // (This is done my projecting the hog movement vector onto the shot movement vector and then adding the resulting length - // to the scaler) - s := (_2 * s + (newPortal^.dX * CurrentHedgehog^.Gear^.dX + newPortal^.dY * CurrentHedgehog^.Gear^.dY ) / s) / s; - newPortal^.dX := newPortal^.dX * s; - newPortal^.dY := newPortal^.dY * s; - - newPortal^.LinkedGear := nil; - - if CurrentHedgehog <> nil then - with CurrentHedgehog^ do - begin - CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^); - // let's save the HH's dX's direction so we can decide where the "top" of the portal hole - newPortal^.Elasticity.isNegative := CurrentHedgehog^.Gear^.dX.isNegative; - // when doing a backjump the dx is the opposite of the facing direction - if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) then - newPortal^.Elasticity.isNegative := not newPortal^.Elasticity.isNegative; - - // make portal gun look unloaded - if (CurWeapon <> nil) and (CurAmmoType = amPortalGun) then - CurWeapon^.Timer := CurWeapon^.Timer or 2; - - iterator := GearsList; - while iterator <> nil do - begin - if (iterator^.Kind = gtPortal) then - if (iterator <> newPortal) and (iterator^.Timer > 0) and (iterator^.Hedgehog = CurrentHedgehog) then - begin - if ((iterator^.Tag and 2) = (newPortal^.Tag and 2)) then - begin - iterator^.Timer:= 0; - end - else - begin - // link portals with each other - newPortal^.LinkedGear := iterator; - iterator^.LinkedGear := newPortal; - iterator^.Health := newPortal^.Health; - end; - end; - iterator^.PortalCounter:= 0; - iterator := iterator^.NextGear - end; - - if newPortal^.LinkedGear <> nil then - begin - // This jiggles gears, to ensure a portal connection just placed under a gear takes effect. - iterator:= GearsList; - while iterator <> nil do - begin - if not (iterator^.Kind in [gtPortal, gtAirAttack, gtKnife]) and ((iterator^.Hedgehog <> CurrentHedgehog) - or ((iterator^.Message and gmAllStoppable) = 0)) then - begin - iterator^.Active:= true; - if iterator^.dY.QWordValue = 0 then - iterator^.dY.isNegative:= false; - iterator^.State:= iterator^.State or gstMoving; - DeleteCI(iterator); - //inc(iterator^.dY.QWordValue,10); - end; - iterator:= iterator^.NextGear - end - end - end; - newPortal^.State := newPortal^.State and (not gstCollision); - newPortal^.State := newPortal^.State or gstMoving; - newPortal^.doStep := @doStepMovingPortal; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepPiano(Gear: PGear); -var - r0, r1: LongInt; - odY: hwFloat; -begin - AllInactive := false; - if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and - ((CurrentHedgehog^.Gear^.Message and gmSlot) <> 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; - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote); - CurrentHedgehog^.Gear^.MsgParam := 0; - CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and (not gmSlot); - end; - - if (*((Gear^.Pos = 3) and ((GameFlags and gfSolidLand) <> 0)) or*) (Gear^.Pos = 5) then - begin - Gear^.dY := Gear^.dY + cGravity * 2; - Gear^.Y := Gear^.Y + Gear^.dY; - if CheckGearDrowning(Gear) then - begin - Gear^.Y:= Gear^.Y + _50; - OnUsedAmmo(CurrentHedgehog^); - if CurrentHedgehog^.Gear <> nil then - begin - // Drown the hedgehog. Could also just delete it, but hey, this gets a caption - CurrentHedgehog^.Gear^.Active := true; - CurrentHedgehog^.Gear^.X := Gear^.X; - CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128; - CurrentHedgehog^.Unplaced := false; - if TagTurnTimeLeft = 0 then - TagTurnTimeLeft:= TurnTimeLeft; - TurnTimeLeft:= 0 - end; - ResumeMusic - end; - exit - end; - - odY:= Gear^.dY; - doStepFallingGear(Gear); - - if (Gear^.State and gstDrowning) <> 0 then - begin - Gear^.Y:= Gear^.Y + _50; - OnUsedAmmo(CurrentHedgehog^); - if CurrentHedgehog^.Gear <> nil then - begin - // Drown the hedgehog. Could also just delete it, but hey, this gets a caption - CurrentHedgehog^.Gear^.Active := true; - CurrentHedgehog^.Gear^.X := Gear^.X; - CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128; - CurrentHedgehog^.Unplaced := false; - if TagTurnTimeLeft = 0 then - TagTurnTimeLeft:= TurnTimeLeft; - TurnTimeLeft:= 0 - end; - ResumeMusic - end - else if (Gear^.State and gstCollision) <> 0 then - begin - r0 := GetRandom(21); - r1 := GetRandom(21); - doMakeExplosion(hwRound(Gear^.X) - 30 - r0, hwRound(Gear^.Y) + 40, 40 + r1, Gear^.Hedgehog, 0); - doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, 40 + r0, Gear^.Hedgehog, 0); - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 80 + r0, Gear^.Hedgehog, EXPLAutoSound); - for r0:= 0 to 4 do - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote); - Gear^.dY := cGravity * 2 - odY; - Gear^.Pos := Gear^.Pos + 1; - end - else - Gear^.dY := Gear^.dY + cGravity * 2; - // let it fall faster so itdoesn't take too long for the whole attack -end; - - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSineGunShotWork(Gear: PGear); -var - x, y, rX, rY, t, tmp, initHealth: LongInt; - oX, oY, ldX, ldY, sdX, sdY, sine, lx, ly, amp: hwFloat; - justCollided: boolean; -begin - AllInactive := 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 GameTicks - t := getRandom(4096); - - - // used for a work-around detection of area that is within land array, but outside borders - justCollided := 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 - begin - tmp:= GetRandom(2 * Gear^.Radius); - AddGear(x - Gear^.Radius + tmp, y - GetRandom(Gear^.Radius + 1), gtFlame, gsttmpFlag, _0, _0, 0) - end - end; - - if random(100) = 0 then - AddVisualGear(x, y, vgtSmokeTrace); - end - else dec(Gear^.Health, 5); // if underwater get additional damage - 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; -begin - PlaySound(sndSineGun); - - // push the shooting Hedgehog back - HHGear := 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 := @doStepSineGunShotWork; - with mobileRecord do - if (performRumble <> nil) and (not fastUntilLag) then - performRumble(kSystemSoundID_Vibrate); -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepFlamethrowerWork(Gear: PGear); -var - HHGear, flame: PGear; - rx, ry, speed: hwFloat; - i, gX, gY: LongInt; -begin - AllInactive := false; - HHGear := Gear^.Hedgehog^.Gear; - HedgehogChAngle(HHGear); - gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); - gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); - - if (GameTicks and $FF) = 0 then - begin - if (HHGear^.Message and gmRight) <> 0 then - begin - if HHGear^.dX.isNegative and (Gear^.Tag < 20) then - inc(Gear^.Tag) - else if Gear^.Tag > 5 then - dec(Gear^.Tag); - end - else if (HHGear^.Message and gmLeft) <> 0 then - begin - if HHGear^.dX.isNegative and (Gear^.Tag > 5) then - dec(Gear^.Tag) - else if Gear^.Tag < 20 then - inc(Gear^.Tag); - end - end; - - dec(Gear^.Timer); - if Gear^.Timer = 0 then - begin - dec(Gear^.Health); - if (Gear^.Health mod 5) = 0 then - begin - rx := rndSign(getRandomf * _0_1); - ry := rndSign(getRandomf * _0_1); - speed := _0_5 * (_10 / Gear^.Tag); - - flame:= AddGear(gx, gy, gtFlame, gstTmpFlag, - SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx, - AngleCos(HHGear^.Angle) * ( - speed) + ry, 0); - flame^.CollisionMask:= lfNotCurrentMask; - - if (Gear^.Health mod 30) = 0 then - begin - flame:= AddGear(gx, gy, gtFlame, 0, - SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx, - AngleCos(HHGear^.Angle) * ( - speed) + ry, 0); - flame^.CollisionMask:= lfNotCurrentMask; - end - end; - Gear^.Timer:= Gear^.Tag - end; - - if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) then - begin - DeleteGear(Gear); - AfterAttack - end - else - begin - i:= Gear^.Health div 5; - if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then - begin - Gear^.Damage:= i; - FreeTexture(Gear^.Tex); - Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + - '%', cWhiteColor, fntSmall) - end - end -end; - -procedure doStepFlamethrower(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown or gmLeft or gmRight)); - HHGear^.State := HHGear^.State or gstNotKickable; - Gear^.doStep := @doStepFlamethrowerWork -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepLandGunWork(Gear: PGear); -var - HHGear, land: PGear; - rx, ry, speed: hwFloat; - i, gX, gY: LongInt; -begin - AllInactive := false; - HHGear := Gear^.Hedgehog^.Gear; - HedgehogChAngle(HHGear); - gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); - gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); - - if (GameTicks and $FF) = 0 then - begin - if (HHGear^.Message and gmRight) <> 0 then - begin - if HHGear^.dX.isNegative and (Gear^.Tag < 20) then - inc(Gear^.Tag) - else if Gear^.Tag > 5 then - dec(Gear^.Tag); - end - else if (HHGear^.Message and gmLeft) <> 0 then - begin - if HHGear^.dX.isNegative and (Gear^.Tag > 5) then - dec(Gear^.Tag) - else if Gear^.Tag < 20 then - inc(Gear^.Tag); - end - end; - - dec(Gear^.Timer); - if Gear^.Timer = 0 then - begin - dec(Gear^.Health); - - rx := rndSign(getRandomf * _0_1); - ry := rndSign(getRandomf * _0_1); - speed := (_3 / Gear^.Tag); - - land:= AddGear(gx, gy, gtFlake, gstTmpFlag, - SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx, - AngleCos(HHGear^.Angle) * ( - speed) + ry, 0); - land^.CollisionMask:= lfNotCurrentMask; - - Gear^.Timer:= Gear^.Tag - end; - - if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) or ((HHGear^.Message and gmAttack) <> 0) then - begin - HHGear^.Message:= HHGear^.Message and (not gmAttack); - DeleteGear(Gear); - AfterAttack - end - else - begin - i:= Gear^.Health div 10; - if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then - begin - Gear^.Damage:= i; - FreeTexture(Gear^.Tex); - Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + - '%', cWhiteColor, fntSmall) - end - end -end; - -procedure doStepLandGun(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown or gmLeft or gmRight or gmAttack)); - HHGear^.State := HHGear^.State or gstNotKickable; - Gear^.doStep := @doStepLandGunWork -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepPoisonCloud(Gear: PGear); -begin - if Gear^.Timer = 0 then - begin - DeleteGear(Gear); - exit - end; - dec(Gear^.Timer); - Gear^.X:= Gear^.X + Gear^.dX; - Gear^.Y:= Gear^.Y + Gear^.dY; - Gear^.dX := Gear^.dX + cWindSpeed / 4; - Gear^.dY := Gear^.dY + cGravity / 100; - if (GameTicks and $FF) = 0 then - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx or EXPLNoDamage or EXPLDoNotTouchAny or EXPLPoisoned); - AllInactive:= false; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepHammer(Gear: PGear); -var HHGear, tmp, tmp2: PGear; - t: PGearArray; - i: LongInt; -begin -HHGear:= Gear^.Hedgehog^.Gear; -HHGear^.State:= HHGear^.State or gstNoDamage; -DeleteCI(HHGear); - -t:= CheckGearsCollision(Gear); - -for i:= 5 downto 0 do - AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); - -i:= t^.Count; -while i > 0 do - begin - dec(i); - tmp:= t^.ar[i]; - if (tmp^.State and gstNoDamage) = 0 then - if (tmp^.Kind = gtHedgehog) or (tmp^.Kind = gtMine) or (tmp^.Kind = gtExplosives) then - begin - //tmp^.State:= tmp^.State or gstFlatened; - if not tmp^.Invulnerable then - ApplyDamage(tmp, CurrentHedgehog, tmp^.Health div 3, dsUnknown); - //DrawTunnel(tmp^.X, tmp^.Y - _1, _0, _0_5, cHHRadius * 6, cHHRadius * 3); - tmp2:= AddGear(hwRound(tmp^.X), hwRound(tmp^.Y), gtHammerHit, 0, _0, _0, 0); - tmp2^.LinkedGear:= tmp; - SetAllToActive - end - else - begin - end - end; - -HHGear^.State:= HHGear^.State and (not gstNoDamage); -Gear^.Timer:= 250; -Gear^.doStep:= @doStepIdle -end; - -procedure doStepHammerHitWork(Gear: PGear); -var - i, j, ei: LongInt; - HitGear: PGear; -begin - AllInactive := false; - HitGear := Gear^.LinkedGear; - dec(Gear^.Timer); - if (HitGear = nil) or (Gear^.Timer = 0) or ((Gear^.Message and gmDestroy) <> 0) then - begin - DeleteGear(Gear); - exit - end; - - if (Gear^.Timer mod 5) = 0 then - begin - AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); - - i := hwRound(Gear^.X) - HitGear^.Radius + 2; - ei := hwRound(Gear^.X) + HitGear^.Radius - 2; - for j := 1 to 4 do DrawExplosion(i - GetRandom(5), hwRound(Gear^.Y) + 6*j, 3); - for j := 1 to 4 do DrawExplosion(ei + LongInt(GetRandom(5)), hwRound(Gear^.Y) + 6*j, 3); - while i <= ei do - begin - for j := 1 to 11 do DrawExplosion(i, hwRound(Gear^.Y) + 3*j, 3); - inc(i, 1) - end; - - if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9) - , lfIndestructible) then - begin - //Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + _1_9 - end; - end; - if TestCollisionYwithGear(Gear, 1) <> 0 then - begin - Gear^.dY := _0; - SetLittle(HitGear^.dX); - HitGear^.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 + HitGear^.dX; - HitGear^.X := Gear^.X; - HitGear^.Y := Gear^.Y; - SetLittle(HitGear^.dY); - HitGear^.Active:= true; -end; - -procedure doStepHammerHit(Gear: PGear); -var - i, y: LongInt; - ar: TRangeArray; - HHGear: PGear; -begin - i := 0; - HHGear := 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); - - doStepHammerHitWork(Gear); - Gear^.doStep := @doStepHammerHitWork -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepResurrectorWork(Gear: PGear); -var - graves: PGearArrayS; - resgear: PGear; - hh: PHedgehog; - i: LongInt; -begin - if (TurnTimeLeft > 0) then - dec(TurnTimeLeft); - - AllInactive := false; - hh := Gear^.Hedgehog; - - // no, you can't do that here - {DrawCentered(hwRound(hh^.Gear^.X) + WorldDx, hwRound(hh^.Gear^.Y) + WorldDy - - cHHRadius - 14 - hh^.HealthTagTex^.h, hh^.HealthTagTex); - } - (*DrawCircle(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, 1.5, 0, 0, $FF, - $FF);*) - - if ((Gear^.Message and gmUp) <> 0) then - begin - if (GameTicks and $F) <> 0 then - exit; - end - else if (GameTicks and $1FF) <> 0 then - exit; - - if Gear^.Power < 45 then - begin - inc(Gear^.Power); - if TestCollisionYwithGear(hh^.Gear, -1) = 0 then - hh^.Gear^.Y := hh^.Gear^.Y - _1; - end; - - graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius); - - if graves.size = 0 then - begin - StopSoundChan(Gear^.SoundChannel); - Gear^.Timer := 250; - Gear^.doStep := @doStepIdle; - exit; - end; - - if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then - begin - if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0; - dec(hh^.Gear^.Health); - if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then - hh^.Gear^.Damage:= 1; - RenderHealth(hh^); - RecountTeamHealth(hh^.Team); - inc(graves.ar^[Gear^.Tag]^.Health); - inc(Gear^.Tag) -{-for i:= 0 to High(graves) do begin - if hh^.Gear^.Health > 0 then begin - dec(hh^.Gear^.Health); - inc(graves[i]^.Health); - end; - end; -} - end - else - begin - // now really resurrect the hogs with the hp saved in the graves - for i:= 0 to graves.size - 1 do - if graves.ar^[i]^.Health > 0 then - begin - resgear := AddGear(hwRound(graves.ar^[i]^.X), hwRound(graves.ar^[i]^.Y), gtHedgehog, gstWait, _0, _0, 0); - resgear^.Hedgehog := graves.ar^[i]^.Hedgehog; - resgear^.Health := graves.ar^[i]^.Health; - PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := resgear; - graves.ar^[i]^.Message:= graves.ar^[i]^.Message or gmDestroy; - graves.ar^[i]^.Active:= true; - RenderHealth(resgear^.Hedgehog^); - RecountTeamHealth(resgear^.Hedgehog^.Team); - resgear^.Hedgehog^.Effects[heResurrected]:= 1; - // only make hat-less hedgehogs look like zombies, preserve existing hats - - if resgear^.Hedgehog^.Hat = 'NoHat' then - LoadHedgehogHat(resgear^.Hedgehog^, 'Reserved/Zombie'); - end; - - hh^.Gear^.dY := _0; - hh^.Gear^.dX := _0; - doStepHedgehogMoving(hh^.Gear); - StopSoundChan(Gear^.SoundChannel); - Gear^.Timer := 250; - Gear^.doStep := @doStepIdle; - end - //if hh^.Gear^.Health = 0 then doStepHedgehogFree(hh^.Gear); -end; - -procedure doStepResurrector(Gear: PGear); -var - graves: PGearArrayS; - hh: PHedgehog; - i: LongInt; -begin - AllInactive := false; - graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius); - - if graves.size > 0 then - begin - hh := Gear^.Hedgehog; - for i:= 0 to graves.size - 1 do - begin - PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := nil; - graves.ar^[i]^.Health := 0; - end; - Gear^.doStep := @doStepResurrectorWork; - if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then - begin - if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0; - dec(hh^.Gear^.Health); - if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then - hh^.Gear^.Damage:= 1; - RenderHealth(hh^); - RecountTeamHealth(hh^.Team); - inc(graves.ar^[Gear^.Tag]^.Health); - inc(Gear^.Tag) - end - end - else - begin - StopSoundChan(Gear^.SoundChannel); - Gear^.Timer := 250; - Gear^.doStep := @doStepIdle; - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepNapalmBomb(Gear: PGear); -var - i, gX, gY: LongInt; - dX, dY: hwFloat; -begin - AllInactive := false; - doStepFallingGear(Gear); - if (Gear^.Timer > 0) and ((Gear^.State and gstCollision) <> 0) then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound); - gX := hwRound(Gear^.X); - gY := hwRound(Gear^.Y); - for i:= 0 to 10 do - begin - dX := AngleCos(i * 2) * ((_0_1*(i div 5))) * (GetRandomf + _1); - dY := AngleSin(i * 8) * _0_5 * (GetRandomf + _1); - AddGear(gX, gY, gtFlame, 0, dX, dY, 0); - AddGear(gX, gY, gtFlame, 0, dX, -dY, 0); - AddGear(gX, gY, gtFlame, 0, -dX, dY, 0); - AddGear(gX, gY, gtFlame, 0, -dX, -dY, 0); - end; - DeleteGear(Gear); - exit - end; - if (Gear^.Timer = 0) then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound); - for i:= -19 to 19 do - FollowGear := AddGear(hwRound(Gear^.X) + i div 3, hwRound(Gear^.Y), gtFlame, 0, _0_001 * i, _0, 0); - DeleteGear(Gear); - exit - end; - if (GameTicks and $3F) = 0 then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); - dec(Gear^.Timer) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepStructure(Gear: PGear); -var - x, y: LongInt; - HH: PHedgehog; - t: PGear; -begin - HH:= Gear^.Hedgehog; - - if (Gear^.State and gstMoving) <> 0 then - begin - AddGearCI(Gear); - Gear^.dX:= _0; - Gear^.dY:= _0; - Gear^.State:= Gear^.State and (not gstMoving); - end; - - dec(Gear^.Health, Gear^.Damage); - Gear^.Damage:= 0; - - if Gear^.Pos = 1 then - begin - AddGearCI(Gear); - AfterAttack; - if Gear = CurAmmoGear then - CurAmmoGear:= nil; - if HH^.Gear <> nil then - HideHog(HH); - Gear^.Pos:= 2 - end; - - if Gear^.Pos = 2 then - begin - if ((GameTicks mod 100) = 0) and (Gear^.Timer < 1000) then - begin - if (Gear^.Timer mod 10) = 0 then - begin - DeleteCI(Gear); - Gear^.Y:= Gear^.Y - _0_5; - AddGearCI(Gear); - end; - inc(Gear^.Timer); - end; - if Gear^.Tag <= TotalRounds then - Gear^.Pos:= 3; - end; - - if Gear^.Pos = 3 then - if Gear^.Timer < 1000 then - begin - if (Gear^.Timer mod 10) = 0 then - begin - DeleteCI(Gear); - Gear^.Y:= Gear^.Y - _0_5; - AddGearCI(Gear); - end; - inc(Gear^.Timer); - end - else - begin - if HH^.GearHidden <> nil then - RestoreHog(HH); - Gear^.Pos:= 4; - end; - - if Gear^.Pos = 4 then - if ((GameTicks mod 1000) = 0) and ((GameFlags and gfInvulnerable) = 0) then - begin - t:= GearsList; - while t <> nil do - begin - if (t^.Kind = gtHedgehog) and (t^.Hedgehog^.Team^.Clan = HH^.Team^.Clan) then - t^.Invulnerable:= true; - t:= t^.NextGear; - end; - end; - - if Gear^.Health <= 0 then - begin - if HH^.GearHidden <> nil then - RestoreHog(HH); - - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - - DeleteCI(Gear); - DeleteGear(Gear); - - doMakeExplosion(x, y, 50, CurrentHedgehog, EXPLAutoSound); - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -(* - TARDIS needs - Warp in. Pos = 1 - Pause. Pos = 2 - Hide gear (TARDIS hedgehog was nil) - Warp out. Pos = 3 - ... idle active for some time period ... Pos = 4 - Warp in. Pos = 1 - Pause. Pos = 2 - Restore gear (TARDIS hedgehog was not nil) - Warp out. Pos = 3 -*) - -procedure doStepTardisWarp(Gear: PGear); -var HH: PHedgehog; - i,j,cnt: LongWord; -begin -HH:= Gear^.Hedgehog; -if Gear^.Pos = 2 then - begin - StopSoundChan(Gear^.SoundChannel); - if (Gear^.Timer = 0) then - begin - if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible = 0) then - begin - AfterAttack; - if Gear = CurAmmoGear then CurAmmoGear := nil; - if (HH^.Gear^.Damage = 0) and (HH^.Gear^.Health > 0) and - ((Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then - HideHog(HH) - end - //else if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible <> 0) then - else if (HH^.GearHidden <> nil) then// and (HH^.Gear^.State and gstInvisible <> 0) then - RestoreHog(HH) - end; - - inc(Gear^.Timer); - if (Gear^.Timer > 2000) and ((GameTicks mod 2000) = 1000) then - begin - Gear^.SoundChannel := LoopSound(sndTardis); - Gear^.Pos:= 3 - end - end; - -if (Gear^.Pos = 1) and (GameTicks and $1F = 0) and (Gear^.Power < 255) then - begin - inc(Gear^.Power); - if (Gear^.Power = 172) and (HH^.Gear <> nil) and - (HH^.Gear^.Damage = 0) and (HH^.Gear^.Health > 0) and - ((HH^.Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then - with HH^.Gear^ do - begin - State:= State or gstAnimation; - Tag:= 2; - Timer:= 0; - Pos:= 0 - end - end; -if (Gear^.Pos = 3) and (GameTicks and $1F = 0) and (Gear^.Power > 0) then - dec(Gear^.Power); -if (Gear^.Pos = 1) and (Gear^.Power = 255) and ((GameTicks mod 2000) = 1000) then - Gear^.Pos:= 2; -if (Gear^.Pos = 3) and (Gear^.Power = 0) then - begin - StopSoundChan(Gear^.SoundChannel); - if HH^.GearHidden = nil then - begin - DeleteGear(Gear); - exit - end; - Gear^.Pos:= 4; - // This condition might need tweaking - Gear^.Timer:= GetRandom(cHedgehogTurnTime*TeamsCount)+cHedgehogTurnTime - end; - -if (Gear^.Pos = 4) then - begin - cnt:= 0; - for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do - for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do - if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil) - and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0) - and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then - inc(cnt); - if (cnt = 0) or SuddenDeathDmg or (Gear^.Timer = 0) then - begin - if HH^.GearHidden <> nil then - FindPlace(HH^.GearHidden, false, 0, LAND_WIDTH,true); - - if HH^.GearHidden <> nil then - begin - Gear^.X:= HH^.GearHidden^.X; - Gear^.Y:= HH^.GearHidden^.Y; - end; - Gear^.Timer:= 0; - - if (HH^.GearHidden <> nil) and (cnt = 0) then // do an emergency jump back in this case. the team needs you! - begin - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtExplosion); - Gear^.Pos:= 2; - Gear^.Power:= 255; - end - else begin - Gear^.SoundChannel := LoopSound(sndTardis); - Gear^.Pos:= 1; - Gear^.Power:= 0; - end - end - else if (CurrentHedgehog^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan) then dec(Gear^.Timer) - end; - -end; - -procedure doStepTardis(Gear: PGear); -var i,j,cnt: LongWord; - HH: PHedgehog; -begin -(* - Conditions for not activating. - 1. Hog is last of his clan - 2. Sudden Death is in play - 3. Hog is a king -*) - HH:= Gear^.Hedgehog; - if HH^.Gear <> nil then - if (HH^.Gear = nil) or (HH^.King) or (SuddenDeathDmg) then - begin - if HH^.Gear <> nil then - begin - HH^.Gear^.Message := HH^.Gear^.Message and (not gmAttack); - HH^.Gear^.State:= HH^.Gear^.State and (not gstAttacking); - end; - PlaySound(sndDenied); - DeleteGear(gear); - exit - end; - cnt:= 0; - for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do - for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do - if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil) - and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0) - and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then - inc(cnt); - if cnt < 2 then - begin - if HH^.Gear <> nil then - begin - HH^.Gear^.Message := HH^.Gear^.Message and (not gmAttack); - HH^.Gear^.State:= HH^.Gear^.State and (not gstAttacking); - end; - PlaySound(sndDenied); - DeleteGear(gear); - exit - end; - Gear^.SoundChannel := LoopSound(sndTardis); - Gear^.doStep:= @doStepTardisWarp -end; - -//////////////////////////////////////////////////////////////////////////////// - -(* -WIP. The ice gun will have the following effects. It has been proposed by sheepluva that it take the appearance of a large freezer -spewing ice cubes. The cubes will be visual gears only. The scatter from them and the impact snow dust should help hide imprecisions in things like the GearsNear effect. -For now we assume a "ray" like a deagle projected out from the gun. -All these effects assume the ray's angle is not changed and that the target type was unchanged over a number of ticks. This is a simplifying assumption for "gun was applying freezing effect to the same target". - * When fired at water a layer of ice textured land is added above the water. - * When fired at non-ice land (land and lfLandMask and not lfIce) the land is overlaid with a thin layer of ice textured land around that point (say, 1 or 2px into land, 1px above). For attractiveness, a slope would probably be needed. - * When fired at a hog (land and $00FF <> 0), while the hog is targetted, the hog's state is set to frozen. As long as the gun is on the hog, a frozen hog sprite creeps up from the feet to the head. If the effect is interrupted before reaching the top, the freezing state is cleared. -A frozen hog will animate differently. To be decided, but possibly in a similar fashion to a grave when it comes to explosions. The hog might (possibly) not be damaged by explosions. This might make freezing potentially useful for friendlies in a bad position. It might be better to allow damage though. -A frozen hog stays frozen for a certain number of turns. Each turn the frozen overlay becomes fainter, until it fades and the hog animates normally again. -*) - - -procedure updateFuel(Gear: PGear); -var - t:LongInt; -begin - t:= Gear^.Health div 10; - if (t <> Gear^.Damage) and ((GameTicks and $3F) = 0) then - begin - Gear^.Damage:= t; - FreeTexture(Gear^.Tex); - Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(t) + - '%', cWhiteColor, fntSmall) - end; - if Gear^.Message and (gmUp or gmDown) <> 0 then - begin - StopSoundChan(Gear^.SoundChannel); - Gear^.SoundChannel:= -1; - if GameTicks mod 40 = 0 then dec(Gear^.Health) - end - else - begin - if Gear^.SoundChannel = -1 then - Gear^.SoundChannel := LoopSound(sndIceBeam); - if GameTicks mod 10 = 0 then dec(Gear^.Health) - end -end; - - -procedure updateTarget(Gear:PGear; newX, newY:HWFloat); -// var -// iter:PGear; -begin - with Gear^ do - begin - dX:= newX; - dY:= newY; - Pos:= 0; - Target.X:= NoPointX; - LastDamage:= nil; - X:= Hedgehog^.Gear^.X; - Y:= Hedgehog^.Gear^.Y; - end; -end; - -procedure doStepIceGun(Gear: PGear); -const iceWaitCollision = 0; -const iceCollideWithGround = 1; -//const iceWaitNextTarget:Longint = 2; -//const iceCollideWithHog:Longint = 4; -const iceCollideWithWater = 5; -//const waterFreezingTime:Longint = 500; -const groundFreezingTime = 1000; -const iceRadius = 32; -const iceHeight = 40; -var - HHGear, iter: PGear; - landRect: TSDL_Rect; - ndX, ndY: hwFloat; - i, t, gX, gY: LongInt; - hogs: PGearArrayS; - vg: PVisualGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - if (Gear^.Message and gmAttack <> 0) or (Gear^.Health = 0) or (HHGear = nil) or (HHGear^.Damage <> 0) or (HHGear^.dX.QWordValue > 4294967) then - begin - StopSoundChan(Gear^.SoundChannel); - DeleteGear(Gear); - AfterAttack; - exit - end; - updateFuel(Gear); - - with Gear^ do - begin - HedgehogChAngle(HHGear); - ndX:= SignAs(AngleSin(HHGear^.Angle), HHGear^.dX) * _4; - ndY:= -AngleCos(HHGear^.Angle) * _4; - if (ndX <> dX) or (ndY <> dY) or - ((Target.X <> NoPointX) and (Target.X and LAND_WIDTH_MASK = 0) and - (Target.Y and LAND_HEIGHT_MASK = 0) and ((Land[Target.Y, Target.X] = 0))) then - begin - updateTarget(Gear, ndX, ndY); - Timer := iceWaitCollision; - end - else - begin - X:= X + dX; - Y:= Y + dY; - gX:= hwRound(X); - gY:= hwRound(Y); - if Target.X = NoPointX then t:= hwRound(hwSqr(X-HHGear^.X)+hwSqr(Y-HHGear^.Y)); - - if Target.X <> NoPointX then - begin - CheckCollision(Gear); - if (State and gstCollision) <> 0 then - begin - if Timer = iceWaitCollision then - begin - Timer := iceCollideWithGround; - Power := GameTicks; - end - end - else if (target.y >= cWaterLine) then - begin - if Timer = iceWaitCollision then - begin - Timer := iceCollideWithWater; - Power := GameTicks; - end; - end; - - if (abs(gX-Target.X) < 2) and (abs(gY-Target.Y) < 2) then - begin - X:= HHGear^.X; - Y:= HHGear^.Y - end; - - if (Timer = iceCollideWithGround) and ((GameTicks - Power) > groundFreezingTime) then - begin - FillRoundInLand(target.x, target.y, iceRadius, icePixel); - landRect.x := min(max(target.x - iceRadius, 0), LAND_WIDTH - 1); - landRect.y := min(max(target.y - iceRadius, 0), LAND_HEIGHT - 1); - landRect.w := min(2*iceRadius, LAND_WIDTH - landRect.x - 1); - landRect.h := min(2*iceRadius, LAND_HEIGHT - landRect.y - 1); - UpdateLandTexture(landRect.x, landRect.w, landRect.y, landRect.h, true); - - // Freeze nearby mines/explosives/cases too - iter := GearsList; - while iter <> nil do - begin - if (iter^.State and gstFrozen = 0) and - ((iter^.Kind = gtExplosives) or (iter^.Kind = gtCase) or (iter^.Kind = gtMine)) and - (abs(iter^.X.Round-target.x)+abs(iter^.Y.Round-target.y)+2<2*iceRadius) and (Distance(iter^.X-int2hwFloat(target.x),iter^.Y-int2hwFloat(target.y)) nil then - begin - i:= random(100) + 155; - vg^.Tint:= (i shl 24) or (i shl 16) or ($FF shl 8) or (random(200) + 55); - vg^.Angle:= random(360); - vg^.dx:= 0.001 * random(80); - vg^.dy:= 0.001 * random(80) - end - end; - PlaySound(sndHogFreeze); - if iter^.Kind = gtMine then // dud mine block - begin - iter^.State:= iter^.State or gstFrozen; - vg:= AddVisualGear(hwRound(iter^.X) - 4 + Random(8), hwRound(iter^.Y) - 4 - Random(4), vgtSmoke); - if vg <> nil then - vg^.Scale:= 0.5; - PlaySound(sndVaporize); - iter^.Health := 0; - iter^.Damage := 0; - iter^.State := iter^.State and (not gstAttacking) - end - else if iter^.Kind = gtCase then - begin - DeleteCI(iter); - iter^.State:= iter^.State or gstFrozen; - AddGearCI(iter) - end - else // gtExplosives - begin - iter^.State:= iter^.State or gstFrozen; - iter^.Health:= iter^.Health + cBarrelHealth - end - end; - iter:= iter^.NextGear - end; - - // FillRoundInLandWithIce(Target.X, Target.Y, iceRadius); - SetAllHHToActive; - Timer := iceWaitCollision; - end; - - if (Timer = iceCollideWithWater) and ((GameTicks - Power) > groundFreezingTime) then - begin - PlaySound(sndHogFreeze); - DrawIceBreak(Target.X, cWaterLine - iceHeight, iceRadius, iceHeight); - SetAllHHToActive; - Timer := iceWaitCollision; - end; -(* - Any ideas for something that would look good here? - if (Target.X <> NoPointX) and ((Timer = iceCollideWithGround) or (Timer = iceCollideWithWater)) and (GameTicks mod max((groundFreezingTime-((GameTicks - Power)*2)),2) = 0) then //and CheckLandValue(Target.X, Target.Y, lfIce) then - begin - vg:= AddVisualGear(Target.X+random(20)-10, Target.Y+random(40)-10, vgtDust, 1); - if vg <> nil then - begin - i:= random(100) + 155; - vg^.Tint:= IceColor or $FF; - vg^.Angle:= random(360); - vg^.dx:= 0.001 * random(80); - vg^.dy:= 0.001 * random(80) - end - end; -*) - -// freeze nearby hogs - hogs := GearsNear(int2hwFloat(Target.X), int2hwFloat(Target.Y), gtHedgehog, Gear^.Radius*2); - if hogs.size > 0 then - for i:= 0 to hogs.size - 1 do - if hogs.ar^[i] <> HHGear then - if GameTicks mod 5 = 0 then - begin - hogs.ar^[i]^.Active:= true; - if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] < 256 then - hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] := hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] + 1 - else if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] = 256 then - begin - hogs.ar^[i]^.Hedgehog^.Effects[heFrozen]:= 200000-1;//cHedgehogTurnTime + cReadyDelay - PlaySound(sndHogFreeze); - end; - end; - inc(Pos) - end - else if (t > 400) and ((gY > cWaterLine) or - (((gX and LAND_WIDTH_MASK = 0) and (gY and LAND_HEIGHT_MASK = 0)) - and (Land[gY, gX] <> 0))) then - begin - Target.X:= gX; - Target.Y:= gY; - X:= HHGear^.X; - Y:= HHGear^.Y - end; - if (gX > max(LAND_WIDTH,4096)*2) or - (gX < -max(LAND_WIDTH,4096)) or - (gY < -max(LAND_HEIGHT,4096)) or - (gY > max(LAND_HEIGHT,4096)+512) then - begin - //X:= HHGear^.X; - //Y:= HHGear^.Y - Target.X:= gX; - Target.Y:= gY; - end - end - end; -end; - -procedure doStepAddAmmo(Gear: PGear); -var a: TAmmoType; - gi: PGear; -begin -if Gear^.Timer > 0 then dec(Gear^.Timer) -else - begin - if Gear^.Pos = posCaseUtility then - a:= GetUtility(Gear^.Hedgehog) - else - a:= GetAmmo(Gear^.Hedgehog); - CheckSum:= CheckSum xor GameTicks; - gi := GearsList; - while gi <> nil do - begin - with gi^ do CheckSum:= CheckSum xor X.round xor X.frac xor dX.round xor dX.frac xor Y.round xor Y.frac xor dY.round xor dY.frac; - AddRandomness(CheckSum); - if gi^.Kind = gtGenericFaller then gi^.State:= gi^.State and not gstTmpFlag; - gi := gi^.NextGear - end; - AddPickup(Gear^.Hedgehog^, a, Gear^.Power, hwRound(Gear^.X), hwRound(Gear^.Y)); - DeleteGear(Gear) - end; -end; - -procedure doStepGenericFaller(Gear: PGear); -begin -if Gear^.Timer < $FFFFFFFF then - if Gear^.Timer > 0 then - dec(Gear^.Timer) - else - begin - DeleteGear(Gear); - exit - end; -if (Gear^.State and gstTmpFlag <> 0) or (GameTicks and $7 = 0) then - begin - doStepFallingGear(Gear); - if (Gear^.State and gstInvisible <> 0) and (GameTicks and $FF = 0) and (hwRound(Gear^.X) < LongInt(leftX)) or (hwRound(Gear^.X) > LongInt(rightX)) or (hwRound(Gear^.Y) < LongInt(topY)) then - begin - Gear^.X:= int2hwFloat(GetRandom(rightX-leftX)+leftX); - Gear^.Y:= int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY); - Gear^.dX:= _90-(GetRandomf*_360); - Gear^.dY:= _90-(GetRandomf*_360) - end; - end -end; - -procedure doStepCreeper(Gear: PGear); -var hogs: PGearArrayS; - HHGear: PGear; - tdX: hwFloat; - dir: LongInt; -begin -doStepFallingGear(Gear); -if Gear^.Timer > 0 then dec(Gear^.Timer); -// creeper sleep phase -if (Gear^.Hedgehog = nil) and (Gear^.Timer > 0) then exit; - -if Gear^.Hedgehog <> nil then HHGear:= Gear^.Hedgehog^.Gear -else HHGear:= nil; - -// creeper boom phase -if (Gear^.State and gstTmpFlag <> 0) then - begin - if (Gear^.Timer = 0) then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 300, CurrentHedgehog, EXPLAutoSound); - DeleteGear(Gear) - end; - // ssssss he essssscaped - if (Gear^.Timer > 250) and ((HHGear = nil) or - (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) > 180) and - (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > _180))) then - begin - Gear^.State:= Gear^.State and (not gstTmpFlag); - Gear^.Timer:= 0 - end; - exit - end; - -// Search out a new target, as target seek time has expired, target is dead, target is out of range, or we did not have a target -if (HHGear = nil) or (Gear^.Timer = 0) or - (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) > Gear^.Angle) and - (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > int2hwFloat(Gear^.Angle))) - then - begin - hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Angle); - if hogs.size > 1 then - Gear^.Hedgehog:= hogs.ar^[GetRandom(hogs.size)]^.Hedgehog - else if hogs.size = 1 then Gear^.Hedgehog:= hogs.ar^[0]^.Hedgehog - else Gear^.Hedgehog:= nil; - if Gear^.Hedgehog <> nil then Gear^.Timer:= 5000; - exit - end; - -// we have a target. move the creeper. -if HHGear <> nil then - begin - // GOTCHA - if ((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) < 50) and - (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) < _50) then - begin - // hisssssssssss - Gear^.State:= Gear^.State or gstTmpFlag; - Gear^.Timer:= 1500; - exit - end; - if (Gear^.State and gstMoving <> 0) then - begin - Gear^.dY:= _0; - Gear^.dX:= _0; - end - else if (GameTicks and $FF = 0) then - begin - tdX:= HHGear^.X-Gear^.X; - dir:= hwSign(tdX); - if not TestCollisionX(Gear, dir) then - Gear^.X:= Gear^.X + signAs(_1,tdX); - if TestCollisionXwithXYShift(Gear, signAs(_10,tdX), 0, dir) then - begin - Gear^.dX:= SignAs(_0_15, tdX); - Gear^.dY:= -_0_3; - Gear^.State:= Gear^.State or gstMoving - end - end; - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepKnife(Gear: PGear); -//var ox, oy: LongInt; -// la: hwFloat; -var a: real; -begin - // Gear is shrunk so it can actually escape the hog without carving into the terrain - if (Gear^.Radius = 4) and (Gear^.CollisionMask = $FFFF) then Gear^.Radius:= 7; - if Gear^.Damage > 100 then Gear^.CollisionMask:= 0 - else if Gear^.Damage > 30 then - if GetRandom(max(4,18-Gear^.Damage div 10)) < 3 then Gear^.CollisionMask:= 0; - Gear^.Damage:= 0; - if Gear^.Timer > 0 then dec(Gear^.Timer); - if (Gear^.State and gstMoving <> 0) and (Gear^.State and gstCollision = 0) then - begin - DeleteCI(Gear); - Gear^.Radius:= 7; - // used for damage and impact calc. needs balancing I think - Gear^.Health:= hwRound(hwSqr((hwAbs(Gear^.dY)+hwAbs(Gear^.dX))*_4)); - doStepFallingGear(Gear); - AllInactive := false; - a:= Gear^.DirAngle; - CalcRotationDirAngle(Gear); - Gear^.DirAngle:= a+(Gear^.DirAngle-a)*2*hwSign(Gear^.dX) // double rotation - end - else if (Gear^.CollisionIndex = -1) and (Gear^.Timer = 0) then - begin - (*ox:= 0; oy:= 0; - if TestCollisionYwithGear(Gear, -1) <> 0 then oy:= -1; - if TestCollisionXwithGear(Gear, 1) then ox:= 1; - if TestCollisionXwithGear(Gear, -1) then ox:= -1; - if TestCollisionYwithGear(Gear, 1) <> 0 then oy:= 1; - if Gear^.Health > 0 then - PlaySound(sndRopeAttach); - - la:= _10000; - if (ox <> 0) or (oy <> 0) then - la:= CalcSlopeNearGear(Gear, ox, oy); - if la = _10000 then - begin - // debug for when we couldn't get an angle - //AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite); -*) - Gear^.DirAngle:= DxDy2Angle(Gear^.dX, Gear^.dY) + (random(30)-15); - if (Gear^.dX.isNegative and Gear^.dY.isNegative) or - ((not Gear^.dX.isNegative) and (not Gear^.dY.isNegative)) then Gear^.DirAngle:= Gear^.DirAngle-90; - // end - // else Gear^.DirAngle:= hwFloat2Float(la)*90; // sheepluva's comment claims 45deg = 0.5 - yet orientation doesn't seem consistent? - // AddFileLog('la: '+floattostr(la)+' DirAngle: '+inttostr(round(Gear^.DirAngle))); - Gear^.dX:= _0; - Gear^.dY:= _0; - Gear^.State:= Gear^.State and (not gstMoving) or gstCollision; - Gear^.Radius:= 16; - if Gear^.Health > 0 then AmmoShove(Gear, Gear^.Health, 0); - Gear^.Health:= 0; - Gear^.Timer:= 500; - AddGearCI(Gear) - end - else if GameTicks and $3F = 0 then - begin - if (TestCollisionYwithGear(Gear, -1) = 0) - and (not TestCollisionXwithGear(Gear, 1)) - and (not TestCollisionXwithGear(Gear, -1)) - and (TestCollisionYwithGear(Gear, 1) = 0) then Gear^.State:= Gear^.State and (not gstCollision) or gstMoving; - end -end; -(* - This didn't end up getting used, but, who knows, might be reasonable for javellin or something -// Make the knife initial angle based on the hog attack angle, or is that too hard? -procedure doStepKnife(Gear: PGear); -var t, - gx, gy, ga, // gear x,y,angle - lx, ly, la, // land x,y,angle - ox, oy, // x,y offset - w, h, // wXh of clip area - tx, ty // tip position in sprite - : LongInt; - surf: PSDL_Surface; - s: hwFloat; - -begin - Gear^.dY := Gear^.dY + cGravity; - if (GameFlags and gfMoreWind) <> 0 then - Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density; - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - CheckGearDrowning(Gear); - gx:= hwRound(Gear^.X); - gy:= hwRound(Gear^.Y); - if Gear^.State and gstDrowning <> 0 then exit; - with Gear^ do - begin - if CheckLandValue(gx, gy, lfLandMask) then - begin - t:= Angle + hwRound((hwAbs(dX)+hwAbs(dY)) * _10); - - if t < 0 then inc(t, 4096) - else if 4095 < t then dec(t, 4096); - Angle:= t; - - DirAngle:= Angle / 4096 * 360 - end - else - begin -//This is the set of postions for the knife. -//Using FlipSurface and copyToXY the knife can be written to the LandPixels at 32 positions, and an appropriate line drawn in Land. - t:= Angle mod 1024; - case t div 128 of - 0: begin - ox:= 2; oy:= 5; - w := 25; h:= 5; - tx:= 0; ty:= 2 - end; - 1: begin - ox:= 2; oy:= 15; - w:= 24; h:= 8; - tx:= 0; ty:= 7 - end; - 2: begin - ox:= 2; oy:= 27; - w:= 23; h:= 12; - tx:= -12; ty:= -5 - end; - 3: begin - ox:= 2; oy:= 43; - w:= 21; h:= 15; - tx:= 0; ty:= 14 - end; - 4: begin - ox:= 29; oy:= 8; - w:= 19; h:= 19; - tx:= 0; ty:= 17 - end; - 5: begin - ox:= 29; oy:= 32; - w:= 15; h:= 21; - tx:= 0; ty:= 20 - end; - 6: begin - ox:= 51; oy:= 3; - w:= 11; h:= 23; - tx:= 0; ty:= 22 - end; - 7: begin - ox:= 51; oy:= 34; - w:= 7; h:= 24; - tx:= 0; ty:= 23 - end - end; - - surf:= SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, RMask, GMask, BMask, AMask); - copyToXYFromRect(SpritesData[sprKnife].Surface, surf, ox, oy, w, h, 0, 0); - // try to make the knife hit point first - lx := 0; - ly := 0; - if CalcSlopeTangent(Gear, gx, gy, lx, ly, 255) then - begin - la:= vector2Angle(int2hwFloat(lx), int2hwFloat(ly)); - ga:= vector2Angle(dX, dY); - AddFileLog('la: '+inttostr(la)+' ga: '+inttostr(ga)+' Angle: '+inttostr(Angle)); - // change to 0 to 4096 forced by LongWord in Gear - if la < 0 then la:= 4096+la; - if ga < 0 then ga:= 4096+ga; - if ((Angle > ga) and (Angle < la)) or ((Angle < ga) and (Angle > la)) then - begin - if Angle >= 2048 then dec(Angle, 2048) - else if Angle < 2048 then inc(Angle, 2048) - end; - AddFileLog('la: '+inttostr(la)+' ga: '+inttostr(ga)+' Angle: '+inttostr(Angle)) - end; - case Angle div 1024 of - 0: begin - flipSurface(surf, true); - flipSurface(surf, true); - BlitImageAndGenerateCollisionInfo(gx-(w-tx), gy-(h-ty), w, surf) - end; - 1: begin - flipSurface(surf, false); - BlitImageAndGenerateCollisionInfo(gx-(w-tx), gy-ty, w, surf) - end; - 2: begin // knife was actually drawn facing this way... - BlitImageAndGenerateCollisionInfo(gx-tx, gy-ty, w, surf) - end; - 3: begin - flipSurface(surf, true); - BlitImageAndGenerateCollisionInfo(gx-tx, gy-(h-ty), w, surf) - end - end; - SDL_FreeSurface(surf); - // this needs to calculate actual width/height + land clipping since update texture doesn't. - // i.e. this will crash if you fire near sides of map, but until I get the blit right, not going to put real values - UpdateLandTexture(hwRound(X)-32, 64, hwRound(Y)-32, 64, true); - DeleteGear(Gear); - exit - end - end; -end; -*) diff -r 76e68c136a11 -r 8e8b908970c2 hedgewars/uGears.pas --- a/hedgewars/uGears.pas Thu Jun 27 14:37:03 2013 +0400 +++ b/hedgewars/uGears.pas Thu Jun 27 15:33:02 2013 +0400 @@ -33,61 +33,37 @@ * effects are called "Visual Gears" and defined in the respective unit! *) interface -uses SDLh, uConsts, uFloat, uTypes; +uses uConsts, uFloat, uTypes; procedure initModule; procedure freeModule; function SpawnCustomCrateAt(x, y: LongInt; crate: TCrateType; content, cnt: Longword): PGear; function SpawnFakeCrateAt(x, y: LongInt; crate: TCrateType; explode: boolean; poison: boolean ): PGear; -function GetAmmo(Hedgehog: PHedgehog): TAmmoType; -function GetUtility(Hedgehog: PHedgehog): TAmmoType; -procedure HideHog(HH: PHedgehog); procedure ProcessGears; procedure EndTurnCleanup; -procedure SetAllToActive; -procedure SetAllHHToActive; inline; -procedure SetAllHHToActive(Ice: boolean); procedure DrawGears; procedure FreeGearsList; procedure AddMiscGears; procedure AssignHHCoords; function GearByUID(uid : Longword) : PGear; -procedure doStepDrowningGear(Gear: PGear); - implementation -uses uStore, uSound, uTeams, uRandom, uCollisions, uIO, uLandGraphics, {$IFDEF SDL13}uTouch,{$ENDIF} +uses uStore, uSound, uTeams, uRandom, uIO, uLandGraphics, {$IFDEF SDL13}uTouch,{$ENDIF} uLocale, uAI, uAmmos, uStats, uVisualGears, uScript, GLunit, uVariables, uCommands, uUtils, uTextures, uRenderUtils, uGearsRender, uCaptions, uDebug, uLandTexture, - uGearsHedgehog, uGearsUtils, uGearsList, uGearsHandlers, uGearsHandlersRope - , uVisualGearsList; + uGearsHedgehog, uGearsUtils, uGearsList, uGearsHandlersRope + , uVisualGearsList, uGearsHandlersMess; var skipFlag: boolean; -procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); forward; -//procedure AmmoFlameWork(Ammo: PGear); forward; -function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): PGearArrayS; forward; -procedure SpawnBoxOfSmth; forward; -procedure ShotgunShot(Gear: PGear); forward; -procedure doStepCase(Gear: PGear); forward; - - var delay: LongWord; delay2: LongWord; step: (stDelay, stChDmg, stSweep, stTurnReact, stAfterDelay, stChWin, stWater, stChWin2, stHealth, stSpawn, stNTurn); - upd: Longword; - snowLeft,snowRight: LongInt; NewTurnTick: LongWord; //SDMusic: shortstring; -// For better maintainability the step handlers of gears are stored in -// separate files. -// Note: step handlers of gears that are hedgehogs are in a different file -// than the handlers for all other gears. -{$INCLUDE "GSHandlers.inc"} - function CheckNoDamage: boolean; // returns TRUE in case of no damaged hhs var Gear: PGear; dmg: LongInt; @@ -551,39 +527,6 @@ RecountTeamHealth(TeamsArray[i]) end; -procedure SetAllToActive; -var t: PGear; -begin -AllInactive:= false; -t:= GearsList; -while t <> nil do - begin - t^.Active:= true; - t:= t^.NextGear - end -end; - -procedure SetAllHHToActive; inline; -begin -SetAllHHToActive(true) -end; - -procedure SetAllHHToActive(Ice: boolean); -var t: PGear; -begin -AllInactive:= false; -t:= GearsList; -while t <> nil do - begin - if (t^.Kind = gtHedgehog) or (t^.Kind = gtExplosives) then - begin - if (t^.Kind = gtHedgehog) and Ice then CheckIce(t); - t^.Active:= true - end; - t:= t^.NextGear - end -end; - procedure DrawGears; var Gear: PGear; x, y: LongInt; @@ -678,210 +621,6 @@ AddGear(LongInt(GetRandom(snowRight - snowLeft)) + snowLeft, LAND_HEIGHT + LongInt(GetRandom(750)) - 1300, gtFlake, 0, _0, _0, 0); end; - -procedure ShotgunShot(Gear: PGear); -var t: PGear; - dmg, r, dist: LongInt; - dx, dy: hwFloat; -begin -Gear^.Radius:= cShotgunRadius; -t:= GearsList; -while t <> nil do - begin - case t^.Kind of - gtHedgehog, - gtMine, - gtSMine, - gtKnife, - gtCase, - gtTarget, - gtExplosives: begin//, -// gtStructure: begin -//addFileLog('ShotgunShot radius: ' + inttostr(Gear^.Radius) + ', t^.Radius = ' + inttostr(t^.Radius) + ', distance = ' + inttostr(dist) + ', dmg = ' + inttostr(dmg)); - dmg:= 0; - r:= Gear^.Radius + t^.Radius; - dx:= Gear^.X-t^.X; - dx.isNegative:= false; - dy:= Gear^.Y-t^.Y; - dy.isNegative:= false; - if r-hwRound(dx+dy) > 0 then - begin - dist:= hwRound(Distance(dx, dy)); - dmg:= ModifyDamage(min(r - dist, 25), t); - end; - if dmg > 0 then - begin - if (not t^.Invulnerable) then - ApplyDamage(t, Gear^.Hedgehog, dmg, dsBullet) - else - Gear^.State:= Gear^.State or gstWinner; - - DeleteCI(t); - t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX); - t^.dY:= t^.dY + Gear^.dY * dmg * _0_01; - t^.State:= t^.State or gstMoving; - if t^.Kind = gtKnife then t^.State:= t^.State and (not gstCollision); - t^.Active:= true; - FollowGear:= t - end - end; - gtGrave: begin - dmg:= 0; - r:= Gear^.Radius + t^.Radius; - dx:= Gear^.X-t^.X; - dx.isNegative:= false; - dy:= Gear^.Y-t^.Y; - dy.isNegative:= false; - if r-hwRound(dx+dy) > 0 then - begin - dist:= hwRound(Distance(dx, dy)); - dmg:= ModifyDamage(min(r - dist, 25), t); - end; - if dmg > 0 then - begin - t^.dY:= - _0_1; - t^.Active:= true - end - end; - end; - t:= t^.NextGear - end; -if (GameFlags and gfSolidLand) = 0 then - DrawExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cShotgunRadius) -end; - -procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); -var t: PGearArray; - Gear: PGear; - i, j, tmpDmg: LongInt; - VGear: PVisualGear; -begin -t:= CheckGearsCollision(Ammo); -// Just to avoid hogs on rope dodging fire. -if (CurAmmoGear <> nil) and ((CurAmmoGear^.Kind = gtRope) or (CurAmmoGear^.Kind = gtJetpack) or (CurAmmoGear^.Kind = gtBirdy)) -and (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.CollisionIndex = -1) -and (sqr(hwRound(Ammo^.X) - hwRound(CurrentHedgehog^.Gear^.X)) + sqr(hwRound(Ammo^.Y) - hwRound(CurrentHedgehog^.Gear^.Y)) <= sqr(cHHRadius + Ammo^.Radius)) then - begin - t^.ar[t^.Count]:= CurrentHedgehog^.Gear; - inc(t^.Count) - end; - -i:= t^.Count; - -if (Ammo^.Kind = gtFlame) and (i > 0) then - Ammo^.Health:= 0; -while i > 0 do - begin - dec(i); - Gear:= t^.ar[i]; - 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 - begin - VGear := AddVisualGear(hwround(Ammo^.X), hwround(Ammo^.Y), vgtBulletHit); - if VGear <> nil then - VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY); - end; - - if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then - Gear^.FlightTime:= 1; - - - case Gear^.Kind of - gtHedgehog, - gtMine, - gtSMine, - gtKnife, - gtTarget, - gtCase, - gtExplosives: //, - //gtStructure: - begin - if (Ammo^.Kind = gtDrill) then - begin - Ammo^.Timer:= 0; - exit; - end; - if (not Gear^.Invulnerable) then - begin - if (Ammo^.Kind = gtKnife) and (tmpDmg > 0) then - for j:= 1 to max(1,min(3,tmpDmg div 5)) do - begin - VGear:= AddVisualGear(hwRound(Ammo^.X-((Ammo^.X-Gear^.X)/_2)), hwRound(Ammo^.Y-((Ammo^.Y-Gear^.Y)/_2)), vgtStraightShot); - if VGear <> nil then - with VGear^ do - begin - Tint:= $FFCC00FF; - Angle:= random(360); - dx:= 0.0005 * (random(100)); - dy:= 0.0005 * (random(100)); - if random(2) = 0 then - dx := -dx; - if random(2) = 0 then - dy := -dy; - FrameTicks:= 600+random(200); - State:= ord(sprStar) - end - end; - ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg, dsShove) - end - else - Gear^.State:= Gear^.State or gstWinner; - 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); - ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg * 100, dsUnknown); // crank up damage for explosives + blowtorch - end; - - if (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.King or (Gear^.Hedgehog^.Effects[heFrozen] > 0)) then - begin - Gear^.dX:= Ammo^.dX * Power * _0_005; - Gear^.dY:= Ammo^.dY * Power * _0_005 - end - else if ((Ammo^.Kind <> gtFlame) or (Gear^.Kind = gtHedgehog)) and (Power <> 0) then - begin - Gear^.dX:= Ammo^.dX * Power * _0_01; - Gear^.dY:= Ammo^.dY * Power * _0_01 - end; - - if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then - begin - Gear^.Active:= true; - DeleteCI(Gear); - Gear^.State:= Gear^.State or gstMoving; - if Gear^.Kind = gtKnife then Gear^.State:= Gear^.State and (not gstCollision); - // move the gear upwards a bit to throw it over tiny obstacles at start - if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then - begin - if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX)) - or (TestCollisionYwithGear(Gear, -1) <> 0)) then - Gear^.Y:= Gear^.Y - _1; - if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) - or (TestCollisionYwithGear(Gear, -1) <> 0)) then - Gear^.Y:= Gear^.Y - _1; - if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) - or (TestCollisionYwithGear(Gear, -1) <> 0)) then - Gear^.Y:= Gear^.Y - _1; - end - end; - - - if (Ammo^.Kind <> gtFlame) or ((Ammo^.State and gsttmpFlag) = 0) then - FollowGear:= Gear - end; - end - end; - end; -if i <> 0 then - SetAllToActive -end; - procedure AssignHHCoords; var i, t, p, j: LongInt; ar: array[0..Pred(cMaxHHs)] of PHedgehog; @@ -946,31 +685,6 @@ end end; -var GearsNearArray : TPGearArray; -function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): PGearArrayS; -var - t: PGear; - s: Longword; -begin - r:= r*r; - s:= 0; - SetLength(GearsNearArray, s); - t := GearsList; - while t <> nil do - begin - if (t^.Kind = Kind) - and ((X - t^.X)*(X - t^.X) + (Y - t^.Y)*(Y-t^.Y) < int2hwFloat(r)) then - begin - inc(s); - SetLength(GearsNearArray, s); - GearsNearArray[s - 1] := t; - end; - t := t^.NextGear; - end; - - GearsNear.size:= s; - GearsNear.ar:= @GearsNearArray -end; {procedure AmmoFlameWork(Ammo: PGear); var t: PGear; @@ -993,21 +707,6 @@ end;} -function CountGears(Kind: TGearType): Longword; -var t: PGear; - count: Longword = 0; -begin - -t:= GearsList; -while t <> nil do - begin - if t^.Kind = Kind then - inc(count); - t:= t^.NextGear - end; -CountGears:= count; -end; - function SpawnCustomCrateAt(x, y: LongInt; crate: TCrateType; content, cnt: Longword): PGear; begin FollowGear := AddGear(x, y, gtCase, 0, _0, _0, 0); @@ -1080,142 +779,6 @@ SpawnFakeCrateAt := FollowGear; end; -function GetAmmo(Hedgehog: PHedgehog): TAmmoType; -var t, aTot: LongInt; - i: TAmmoType; -begin -Hedgehog:= Hedgehog; // avoid hint - -aTot:= 0; -for i:= Low(TAmmoType) to High(TAmmoType) do - if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then - inc(aTot, Ammoz[i].Probability); - -t:= aTot; -i:= Low(TAmmoType); -if (t > 0) then - begin - t:= GetRandom(t); - while t >= 0 do - begin - inc(i); - if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then - dec(t, Ammoz[i].Probability) - end - end; -GetAmmo:= i -end; - -function GetUtility(Hedgehog: PHedgehog): TAmmoType; -var t, uTot: LongInt; - i: TAmmoType; -begin - -uTot:= 0; -for i:= Low(TAmmoType) to High(TAmmoType) do - if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) - and ((Hedgehog^.Team^.HedgehogsNumber > 1) or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then - inc(uTot, Ammoz[i].Probability); - -t:= uTot; -i:= Low(TAmmoType); -if (t > 0) then - begin - t:= GetRandom(t); - while t >= 0 do - begin - inc(i); - if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) and ((Hedgehog^.Team^.HedgehogsNumber > 1) - or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then - dec(t, Ammoz[i].Probability) - end - end; -GetUtility:= i -end; - - - -procedure SpawnBoxOfSmth; -var t, aTot, uTot, a, h: LongInt; - i: TAmmoType; -begin -if (PlacingHogs) or - (cCaseFactor = 0) - or (CountGears(gtCase) >= 5) - or (GetRandom(cCaseFactor) <> 0) then - exit; - -FollowGear:= nil; -aTot:= 0; -uTot:= 0; -for i:= Low(TAmmoType) to High(TAmmoType) do - if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then - inc(aTot, Ammoz[i].Probability) - else - inc(uTot, Ammoz[i].Probability); - -t:=0; -a:=aTot; -h:= 1; - -if (aTot+uTot) <> 0 then - if ((GameFlags and gfInvulnerable) = 0) then - begin - h:= cHealthCaseProb * 100; - t:= GetRandom(10000); - a:= (10000-h)*aTot div (aTot+uTot) - end - else - begin - t:= GetRandom(aTot+uTot); - h:= 0 - end; - - -if t 0) then - begin - FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); - t:= GetRandom(t); - i:= Low(TAmmoType); - FollowGear^.Pos:= posCaseAmmo; - FollowGear^.AmmoType:= i; - AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo); - end - end -else - begin - t:= uTot; - if (t > 0) then - begin - FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); - t:= GetRandom(t); - i:= Low(TAmmoType); - FollowGear^.Pos:= posCaseUtility; - FollowGear^.AmmoType:= i; - AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo); - end - end; - -// handles case of no ammo or utility crates - considered also placing booleans in uAmmos and altering probabilities -if (FollowGear <> nil) then - begin - FindPlace(FollowGear, true, 0, LAND_WIDTH); - - if (FollowGear <> nil) then - AddVoice(sndReinforce, CurrentTeam^.voicepack) - end -end; - function GearByUID(uid : Longword) : PGear; var gear: PGear; diff -r 76e68c136a11 -r 8e8b908970c2 hedgewars/uGearsHandlersMess.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uGearsHandlersMess.pas Thu Jun 27 15:33:02 2013 +0400 @@ -0,0 +1,5760 @@ +(* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2013 Andrey Korotaev + * + * 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 + *) + +(* + * This file contains the step handlers for gears. + * + * Important: Since gears change the course of the game, calculations that + * lead to different results for different clients/players/machines + * should NOT occur! + * Use safe functions and data types! (e.g. GetRandom() and hwFloat) + *) + + {$INCLUDE "options.inc"} + +unit uGearsHandlersMess; +interface +uses uTypes, uFloat; + +procedure doStepPerPixel(Gear: PGear; step: TGearStepProcedure; onlyCheckIfChanged: boolean); +procedure makeHogsWorry(x, y: hwFloat; r: LongInt); +procedure HideHog(HH: PHedgehog); +procedure doStepDrowningGear(Gear: PGear); +procedure doStepFallingGear(Gear: PGear); +procedure doStepBomb(Gear: PGear); +procedure doStepMolotov(Gear: PGear); +procedure doStepCluster(Gear: PGear); +procedure doStepShell(Gear: PGear); +procedure doStepSnowball(Gear: PGear); +procedure doStepSnowflake(Gear: PGear); +procedure doStepGrave(Gear: PGear); +procedure doStepBeeWork(Gear: PGear); +procedure doStepBee(Gear: PGear); +procedure doStepShotIdle(Gear: PGear); +procedure doStepShotgunShot(Gear: PGear); +procedure spawnBulletTrail(Bullet: PGear); +procedure doStepBulletWork(Gear: PGear); +procedure doStepDEagleShot(Gear: PGear); +procedure doStepSniperRifleShot(Gear: PGear); +procedure doStepActionTimer(Gear: PGear); +procedure doStepPickHammerWork(Gear: PGear); +procedure doStepPickHammer(Gear: PGear); +procedure doStepBlowTorchWork(Gear: PGear); +procedure doStepBlowTorch(Gear: PGear); +procedure doStepMine(Gear: PGear); +procedure doStepSMine(Gear: PGear); +procedure doStepDynamite(Gear: PGear); +procedure doStepRollingBarrel(Gear: PGear); +procedure doStepCase(Gear: PGear); +procedure doStepTarget(Gear: PGear); +procedure doStepIdle(Gear: PGear); +procedure doStepShover(Gear: PGear); +procedure doStepWhip(Gear: PGear); +procedure doStepFlame(Gear: PGear); +procedure doStepFirePunchWork(Gear: PGear); +procedure doStepFirePunch(Gear: PGear); +procedure doStepParachuteWork(Gear: PGear); +procedure doStepParachute(Gear: PGear); +procedure doStepAirAttackWork(Gear: PGear); +procedure doStepAirAttack(Gear: PGear); +procedure doStepAirBomb(Gear: PGear); +procedure doStepGirder(Gear: PGear); +procedure doStepTeleportAfter(Gear: PGear); +procedure doStepTeleportAnim(Gear: PGear); +procedure doStepTeleport(Gear: PGear); +procedure doStepSwitcherWork(Gear: PGear); +procedure doStepSwitcher(Gear: PGear); +procedure doStepMortar(Gear: PGear); +procedure doStepKamikazeWork(Gear: PGear); +procedure doStepKamikazeIdle(Gear: PGear); +procedure doStepKamikaze(Gear: PGear); +procedure doStepCakeExpl(Gear: PGear); +procedure doStepCakeDown(Gear: PGear); +procedure doStepCakeWork(Gear: PGear); +procedure doStepCakeUp(Gear: PGear); +procedure doStepCakeFall(Gear: PGear); +procedure doStepCake(Gear: PGear); +procedure doStepSeductionWork(Gear: PGear); +procedure doStepSeductionWear(Gear: PGear); +procedure doStepSeduction(Gear: PGear); +procedure doStepWaterUp(Gear: PGear); +procedure doStepDrillDrilling(Gear: PGear); +procedure doStepDrill(Gear: PGear); +procedure doStepBallgunWork(Gear: PGear); +procedure doStepBallgun(Gear: PGear); +procedure doStepRCPlaneWork(Gear: PGear); +procedure doStepRCPlane(Gear: PGear); +procedure doStepJetpackWork(Gear: PGear); +procedure doStepJetpack(Gear: PGear); +procedure doStepBirdyDisappear(Gear: PGear); +procedure doStepBirdyFly(Gear: PGear); +procedure doStepBirdyDescend(Gear: PGear); +procedure doStepBirdyAppear(Gear: PGear); +procedure doStepBirdy(Gear: PGear); +procedure doStepEggWork(Gear: PGear); +procedure doPortalColorSwitch(); +procedure doStepPortal(Gear: PGear); +procedure loadNewPortalBall(oldPortal: PGear; destroyGear: Boolean); +procedure doStepMovingPortal_real(Gear: PGear); +procedure doStepMovingPortal(Gear: PGear); +procedure doStepPortalShot(newPortal: PGear); +procedure doStepPiano(Gear: PGear); +procedure doStepSineGunShotWork(Gear: PGear); +procedure doStepSineGunShot(Gear: PGear); +procedure doStepFlamethrowerWork(Gear: PGear); +procedure doStepFlamethrower(Gear: PGear); +procedure doStepLandGunWork(Gear: PGear); +procedure doStepLandGun(Gear: PGear); +procedure doStepPoisonCloud(Gear: PGear); +procedure doStepHammer(Gear: PGear); +procedure doStepHammerHitWork(Gear: PGear); +procedure doStepHammerHit(Gear: PGear); +procedure doStepResurrectorWork(Gear: PGear); +procedure doStepResurrector(Gear: PGear); +procedure doStepNapalmBomb(Gear: PGear); +procedure doStepStructure(Gear: PGear); +procedure doStepTardisWarp(Gear: PGear); +procedure doStepTardis(Gear: PGear); +procedure updateFuel(Gear: PGear); +procedure updateTarget(Gear:PGear; newX, newY:HWFloat); +procedure doStepIceGun(Gear: PGear); +procedure doStepAddAmmo(Gear: PGear); +procedure doStepGenericFaller(Gear: PGear); +procedure doStepCreeper(Gear: PGear); +procedure doStepKnife(Gear: PGear); + +var + upd: Longword; + snowLeft,snowRight: LongInt; + +implementation +uses uConsts, uVariables, uVisualGearsList, uRandom, uCollisions, uGearsList, uUtils, uSound + , SDLh, uScript, uGearsHedgehog, uGearsUtils, uIO, uCaptions, uLandGraphics + , uGearsHandlers, uTextures, uRenderUtils, uAmmos, uTeams, uLandTexture, uCommands + , uStore, uAI, uStats; + +procedure doStepPerPixel(Gear: PGear; step: TGearStepProcedure; onlyCheckIfChanged: boolean); +var + dX, dY, sX, sY: hwFloat; + i, steps: LongWord; + caller: TGearStepProcedure; +begin + dX:= Gear^.dX; + dY:= Gear^.dY; + steps:= max(abs(hwRound(Gear^.X+dX)-hwRound(Gear^.X)), abs(hwRound(Gear^.Y+dY)-hwRound(Gear^.Y))); + + // Gear is still on the same Pixel it was before + if steps < 1 then + begin + if onlyCheckIfChanged then + begin + Gear^.X := Gear^.X + dX; + Gear^.Y := Gear^.Y + dY; + EXIT; + end + else + steps := 1; + end; + + if steps > 1 then + begin + sX:= dX / steps; + sY:= dY / steps; + end + + else + begin + sX:= dX; + sY:= dY; + end; + + caller:= Gear^.doStep; + + for i:= 1 to steps do + begin + Gear^.X := Gear^.X + sX; + Gear^.Y := Gear^.Y + sY; + step(Gear); + if (Gear^.doStep <> caller) + or ((Gear^.State and gstCollision) <> 0) + or ((Gear^.State and gstMoving) = 0) then + break; + end; +end; + +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 + PlaySoundV(sndOops, gi^.Hedgehog^.Team^.voicepack) + + else + begin + if ((gi^.State and gstMoving) = 0) and (gi^.Hedgehog^.Effects[heFrozen] = 0) then + begin + gi^.dX.isNegative:= X r div 2 then + PlaySoundV(sndNooo, gi^.Hedgehog^.Team^.voicepack) + else + PlaySoundV(sndUhOh, gi^.Hedgehog^.Team^.voicepack); + end; + end; + end; + + gi := gi^.NextGear + end; +end; + +procedure HideHog(HH: PHedgehog); +begin + ScriptCall('onHogHide', HH^.Gear^.Uid); + DeleteCI(HH^.Gear); + if FollowGear = HH^.Gear then + FollowGear:= nil; + + if lastGearByUID = HH^.Gear then + lastGearByUID := nil; + + HH^.Gear^.Message:= HH^.Gear^.Message or gmRemoveFromList; + with HH^.Gear^ do + begin + Z := cHHZ; + HH^.Gear^.Active:= false; + State:= State and (not (gstHHDriven or gstAttacking or gstAttacked)); + Message := Message and (not gmAttack); + end; + HH^.GearHidden:= HH^.Gear; + HH^.Gear:= nil +end; + + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepDrowningGear(Gear: PGear); + begin + AllInactive := false; + Gear^.Y := Gear^.Y + cDrownSpeed; + Gear^.X := Gear^.X + Gear^.dX * cDrownSpeed; + // Create some bubbles (0.5% might be better but causes too few bubbles sometimes) + if ((not SuddenDeathDmg and (WaterOpacity < $FF)) + or (SuddenDeathDmg and (SDWaterOpacity < $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); + if (not SuddenDeathDmg and (WaterOpacity > $FE)) + or (SuddenDeathDmg and (SDWaterOpacity > $FE)) + or (hwRound(Gear^.Y) > Gear^.Radius + cWaterLine + cVisibleWater) then + DeleteGear(Gear); + end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepFallingGear(Gear: PGear); +var + isFalling: boolean; + //tmp: QWord; + tdX, tdY: hwFloat; + collV, collH: LongInt; + land: word; +begin + // clip velocity at 2 - over 1 per pixel, but really shouldn't cause many actual problems. + if Gear^.dX.Round > 2 then + Gear^.dX.QWordValue:= 8589934592; + if Gear^.dY.Round > 2 then + Gear^.dY.QWordValue:= 8589934592; + + if (Gear^.State and gstSubmersible <> 0) and (hwRound(Gear^.Y) > cWaterLine) then + begin + Gear^.dX:= Gear^.dX * _0_999; + Gear^.dY:= Gear^.dY * _0_999 + end; + + Gear^.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) < min(LAND_WIDTH div -2, -2048)) + or (hwRound(Gear^.X) > max(LAND_WIDTH * 3 div 2, 6144)) then + Gear^.State := Gear^.State or gstCollision; + + if Gear^.dY.isNegative then + begin + isFalling := true; + land:= TestCollisionYwithGear(Gear, -1); + if land <> 0 then + begin + collV := -1; + if land and lfIce <> 0 then + Gear^.dX := Gear^.dX * (_0_9 + Gear^.Friction * _0_1) + else + 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) <> 0) then + collV := 1; + end + else + begin // Gear^.dY.isNegative is false + land:= TestCollisionYwithGear(Gear, 1); + if land <> 0 then + begin + collV := 1; + isFalling := false; + if land and lfIce <> 0 then + Gear^.dX := Gear^.dX * (_0_9 + Gear^.Friction * _0_1) + else + Gear^.dX := Gear^.dX * Gear^.Friction; + + Gear^.dY := - Gear^.dY * Gear^.Elasticity; + Gear^.State := Gear^.State or gstCollision + end + else + begin + isFalling := true; + if (Gear^.AdvBounce=1) and (TestCollisionYwithGear(Gear, -1) <> 0) then + collV := -1 + end + 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) then + if (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 + begin + Gear^.dY := Gear^.dY + cGravity; + if (GameFlags and gfMoreWind) <> 0 then + Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density + end; + + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + if Gear^.Kind <> gtBee then + CheckGearDrowning(Gear); + //if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) < _0_0002) and + if (not isFalling) and ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_02.QWordValue) then + Gear^.State := Gear^.State and (not gstMoving) + else + Gear^.State := Gear^.State or gstMoving; + + if (Gear^.nImpactSounds > 0) and + (Gear^.State and gstCollision <> 0) and + (((Gear^.Kind <> gtMine) and (Gear^.Damage <> 0)) or (Gear^.State and gstMoving <> 0)) and + (((Gear^.Radius < 3) and (Gear^.dY < -_0_1)) or + ((Gear^.Radius >= 3) 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, gdX: hwFloat; + vg: PVisualGear; +begin + AllInactive := false; + + doStepFallingGear(Gear); + + dec(Gear^.Timer); + if Gear^.Timer = 1000 then // might need adjustments + case Gear^.Kind of + gtGrenade: 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, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx); + end; + + if (Gear^.Kind = gtGasBomb) and ((GameTicks mod 200) = 0) then + begin + vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite); + if vg <> nil then + vg^.Tint:= $FFC0C000; + end; + + if Gear^.Timer = 0 then + begin + case Gear^.Kind of + gtGrenade: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + gtBall: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 40, Gear^.Hedgehog, EXPLAutoSound); + gtClusterBomb: + begin + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + gdX:= Gear^.dX; + doMakeExplosion(x, y, 20, Gear^.Hedgehog, EXPLAutoSound); + for i:= 0 to 4 do + begin + dX := rndSign(GetRandomf * _0_1) + gdX / 5; + dY := (GetRandomf - _3) * _0_08; + FollowGear := AddGear(x, y, gtCluster, 0, dX, dY, 25) + end + end; + gtWatermelon: + begin + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + gdX:= Gear^.dX; + doMakeExplosion(x, y, 75, Gear^.Hedgehog, EXPLAutoSound); + for i:= 0 to 5 do + begin + dX := rndSign(GetRandomf * _0_1) + gdX / 5; + dY := (GetRandomf - _1_5) * _0_3; + FollowGear:= AddGear(x, y, gtMelonPiece, 0, dX, dY, 75); + FollowGear^.DirAngle := i * 60 + end + end; + gtHellishBomb: + begin + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + doMakeExplosion(x, y, 90, Gear^.Hedgehog, EXPLAutoSound); + + for i:= 0 to 127 do + begin + dX := AngleCos(i * 16) * _0_5 * (GetRandomf + _1); + dY := AngleSin(i * 16) * _0_5 * (GetRandomf + _1); + if i mod 2 = 0 then + begin + AddGear(x, y, gtFlame, gstTmpFlag, dX, dY, 0); + AddGear(x, y, gtFlame, 0, dX, -dY, 0) + end + else + begin + AddGear(x, y, gtFlame, 0, dX, dY, 0); + AddGear(x, y, gtFlame, gstTmpFlag, dX, -dY, 0) + end; + end + end; + gtGasBomb: + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound); + for i:= 0 to 2 do + begin + x:= GetRandom(60); + y:= GetRandom(40); + FollowGear:= AddGear(hwRound(Gear^.X) - 30 + x, hwRound(Gear^.Y) - 20 + y, gtPoisonCloud, 0, _0, _0, 0); + end + end; + 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 + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace); + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepMolotov(Gear: PGear); +var + s: Longword; + i, gX, gY: LongInt; + dX, dY: hwFloat; + smoke, glass: PVisualGear; +begin + AllInactive := false; + + doStepFallingGear(Gear); + CalcRotationDirAngle(Gear); + + // let's add some smoke depending on speed + s:= max(32,152 - round((abs(hwFloat2FLoat(Gear^.dX))+abs(hwFloat2Float(Gear^.dY)))*120))+random(10); + if (GameTicks mod s) = 0 then + begin + // adjust angle to match the texture + if Gear^.dX.isNegative then + i:= 130 + else i:= 50; + + smoke:= AddVisualGear(hwRound(Gear^.X)-round(cos((Gear^.DirAngle+i) * pi / 180)*20), hwRound(Gear^.Y)-round(sin((Gear^.DirAngle+i) * pi / 180)*20), vgtSmoke); + if smoke <> nil then + smoke^.Scale:= 0.75; + end; + + if (Gear^.State and gstCollision) <> 0 then + begin + PlaySound(sndMolotov); + gX := hwRound(Gear^.X); + gY := hwRound(Gear^.Y); + for i:= 0 to 4 do + begin + (*glass:= AddVisualGear(gx+random(7)-3, gy+random(5)-2, vgtEgg); + if glass <> nil then + begin + glass^.Frame:= 2; + glass^.Tint:= $41B83ED0 - i * $10081000; + glass^.dX:= 1/(10*(random(11)-5)); + glass^.dY:= -1/(random(4)+5); + end;*) + glass:= AddVisualGear(gx+random(7)-3, gy+random(7)-3, vgtStraightShot); + if glass <> nil then + with glass^ do + begin + Frame:= 2; + Tint:= $41B83ED0 - i * $10081000; + Angle:= random(360); + dx:= 0.0000001; + dy:= 0; + if random(2) = 0 then + dx := -dx; + FrameTicks:= 750; + State:= ord(sprEgg) + end; + end; + for i:= 0 to 24 do + begin + dX := AngleCos(i * 2) * ((_0_15*(i div 5))) * (GetRandomf + _1); + dY := AngleSin(i * 8) * _0_5 * (GetRandomf + _1); + AddGear(gX, gY, gtFlame, gstTmpFlag, dX, dY, 0); + AddGear(gX, gY, gtFlame, gstTmpFlag, dX,-dY, 0); + AddGear(gX, gY, gtFlame, gstTmpFlag,-dX, dY, 0); + AddGear(gX, gY, gtFlame, gstTmpFlag,-dX,-dY, 0); + end; + DeleteGear(Gear); + exit + end; +end; + +//////////////////////////////////////////////////////////////////////////////// + +procedure doStepCluster(Gear: PGear); +begin + AllInactive := false; + doStepFallingGear(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Timer, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end; + + if (Gear^.Kind = gtMelonPiece) + or (Gear^.Kind = gtBall) then + CalcRotationDirAngle(Gear) + else if (GameTicks and $1F) = 0 then + begin + if hwRound(Gear^.Y) > cWaterLine then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) + else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepShell(Gear: PGear); +begin + AllInactive := false; + if (GameFlags and gfMoreWind) = 0 then + Gear^.dX := Gear^.dX + cWindSpeed; + doStepFallingGear(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end; + if (GameTicks and $3F) = 0 then + begin + if hwRound(Gear^.Y) > cWaterLine then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) + else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSnowball(Gear: PGear); +var kick, i: LongInt; + particle: PVisualGear; + gdX, gdY: hwFloat; +begin + AllInactive := false; + if (GameFlags and gfMoreWind) = 0 then + Gear^.dX := Gear^.dX + cWindSpeed; + gdX := Gear^.dX; + gdY := Gear^.dY; + doStepFallingGear(Gear); + CalcRotationDirAngle(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + kick:= hwRound((hwAbs(gdX)+hwAbs(gdY)) * _20); + Gear^.dX:= gdX; + Gear^.dY:= gdY; + AmmoShove(Gear, 0, kick); + for i:= 15 + kick div 10 downto 0 do + begin + particle := AddVisualGear(hwRound(Gear^.X) + Random(25), hwRound(Gear^.Y) + Random(25), vgtDust); + if particle <> nil then + particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480) + end; + DeleteGear(Gear); + exit + end; + if ((GameTicks and $1F) = 0) and (Random(3) = 0) then + begin + particle:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust); + if particle <> nil then + particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSnowflake(Gear: PGear); +var xx, yy, px, py, rx, ry, lx, ly: LongInt; + move, draw, allpx, gun: Boolean; + s: PSDL_Surface; + p: PLongwordArray; + lf: LongWord; +begin +inc(Gear^.Pos); +gun:= (Gear^.State and gstTmpFlag) <> 0; +move:= false; +draw:= false; +if gun then + begin + Gear^.State:= Gear^.State and (not gstInvisible); + doStepFallingGear(Gear); + CheckCollision(Gear); + if ((Gear^.State and gstCollision) <> 0) or ((Gear^.State and gstMoving) = 0) then + draw:= true; + xx:= hwRound(Gear^.X); + yy:= hwRound(Gear^.Y); + end +else if GameTicks and $7 = 0 then + begin + with Gear^ do + begin + State:= State and (not gstInvisible); + X:= X + cWindSpeed * 3200 + dX; + Y:= Y + dY + cGravity * vobFallSpeed * 8; // using same value as flakes to try and get similar results + xx:= hwRound(X); + yy:= hwRound(Y); + if vobVelocity <> 0 then + begin + DirAngle := DirAngle + (Damage / 1000); + if DirAngle < 0 then + DirAngle := DirAngle + 360 + else if 360 < DirAngle then + DirAngle := DirAngle - 360; + end; +(* +We aren't using frametick right now, so just a waste of cycles. + inc(Health, 8); + if longword(Health) > vobFrameTicks then + begin + dec(Health, vobFrameTicks); + inc(Timer); + if Timer = vobFramesCount then + Timer:= 0 + end; +*) + // move back to cloud layer + if yy > cWaterLine then + move:= true + else if (xx > snowRight) or (xx < snowLeft) then + move:=true + // Solid pixel encountered + else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then + begin + lf:= Land[yy, xx] and (lfObject or lfBasic or lfIndestructible); + if lf = 0 then lf:= lfObject; + // If there's room below keep falling + if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (Land[yy-1, xx] = 0) then + begin + X:= X - cWindSpeed * 1600 - dX; + end + // If there's room below, on the sides, fill the gaps + else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx-(1*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx-(1*hwSign(cWindSpeed)))] = 0) then + begin + X:= X - _0_8 * hwSign(cWindSpeed); + Y:= Y - dY - cGravity * vobFallSpeed * 8; + end + else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx-(2*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx-(2*hwSign(cWindSpeed)))] = 0) then + begin + X:= X - _0_8 * 2 * hwSign(cWindSpeed); + Y:= Y - dY - cGravity * vobFallSpeed * 8; + end + else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx+(1*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx+(1*hwSign(cWindSpeed)))] = 0) then + begin + X:= X + _0_8 * hwSign(cWindSpeed); + Y:= Y - dY - cGravity * vobFallSpeed * 8; + end + else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx+(2*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx+(2*hwSign(cWindSpeed)))] = 0) then + begin + X:= X + _0_8 * 2 * hwSign(cWindSpeed); + Y:= Y - dY - cGravity * vobFallSpeed * 8; + end + // if there's an hog/object below do nothing + else if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((Land[yy+1, xx] and $FF) <> 0)) + then move:=true + else draw:= true + end + end + end; +if draw then + with Gear^ do + begin + // we've collided with land. draw some stuff and get back into the clouds + move:= true; + if (Pos > 20) and ((CurAmmoGear = nil) + or (CurAmmoGear^.Kind <> gtRope)) then + begin +////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS //////////////////////////////////// + if not gun then + begin + dec(yy,3); + dec(xx,1) + end; + s:= SpritesData[sprSnow].Surface; + p:= s^.pixels; + allpx:= true; + for py:= 0 to Pred(s^.h) do + begin + for px:= 0 to Pred(s^.w) do + begin + lx:=xx + px; ly:=yy + py; + if (ly and LAND_HEIGHT_MASK = 0) and (lx and LAND_WIDTH_MASK = 0) and (Land[ly, lx] and $FF = 0) then + begin + rx:= lx; + ry:= ly; + if cReducedQuality and rqBlurryLand <> 0 then + begin + rx:= rx div 2;ry:= ry div 2; + end; + if Land[yy + py, xx + px] <= lfAllObjMask then + if gun then + begin + LandDirty[yy div 32, xx div 32]:= 1; + if LandPixels[ry, rx] = 0 then + Land[ly, lx]:= lfDamaged or lfObject + else Land[ly, lx]:= lfDamaged or lfBasic + end + else Land[ly, lx]:= lf; + if gun then + LandPixels[ry, rx]:= (ExplosionBorderColor and (not AMask)) or (p^[px] and AMask) + else LandPixels[ry, rx]:= addBgColor(LandPixels[ry, rx], p^[px]); + end + else allpx:= false + end; + p:= @(p^[s^.pitch shr 2]) + end; + + // Why is this here. For one thing, there's no test on +1 being safe. + //Land[py, px+1]:= lfBasic; + + if allpx then + UpdateLandTexture(xx, Pred(s^.h), yy, Pred(s^.w), true) + else + begin + UpdateLandTexture( + max(0, min(LAND_WIDTH, xx)), + min(LAND_WIDTH - xx, Pred(s^.w)), + max(0, min(LAND_WIDTH, yy)), + min(LAND_HEIGHT - yy, Pred(s^.h)), false // could this be true without unnecessarily creating blanks? + ); + end; +////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS //////////////////////////////////// + end + end; + +if move then + begin + if gun then + begin + DeleteGear(Gear); + exit + end; + Gear^.Pos:= 0; + Gear^.X:= int2hwFloat(LongInt(GetRandom(snowRight - snowLeft)) + snowLeft); + Gear^.Y:= int2hwFloat(LAND_HEIGHT + LongInt(GetRandom(50)) - 1325); + Gear^.State:= Gear^.State or gstInvisible; + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepGrave(Gear: PGear); +begin + if (Gear^.Message and gmDestroy) <> 0 then + begin + DeleteGear(Gear); + exit + end; + + AllInactive := 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 + cGravity +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepBeeWork(Gear: PGear); +var + t: hwFloat; + gX,gY,i: LongInt; + uw, nuw: boolean; + flower: PVisualGear; + +begin + AllInactive := false; + gX := hwRound(Gear^.X); + gY := hwRound(Gear^.Y); + uw := (Gear^.Tag <> 0); // was bee underwater last tick? + nuw := (cWaterLine < gy + Gear^.Radius); // is bee underwater now? + + // if water entered or left + if nuw <> 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); + StopSoundChan(Gear^.SoundChannel); + if nuw then + begin + Gear^.SoundChannel := LoopSound(sndBeeWater); + Gear^.Tag := 1; + end + else + begin + Gear^.SoundChannel := LoopSound(sndBee); + Gear^.Tag := 0; + end; + end; + + + if Gear^.Timer = 0 then + Gear^.RenderTimer:= false + else + begin + if (GameTicks and $F) = 0 then + begin + if (GameTicks and $30) = 0 then + AddVisualGear(gX, gY, vgtBeeTrace); + Gear^.dX := Gear^.Elasticity * (Gear^.dX + _0_000064 * (Gear^.Target.X - gX)); + Gear^.dY := Gear^.Elasticity * (Gear^.dY + _0_000064 * (Gear^.Target.Y - gY)); + // make sure new speed isn't higher than original one (which we stored in Friction variable) + t := Gear^.Friction / Distance(Gear^.dX, Gear^.dY); + Gear^.dX := Gear^.dX * t; + Gear^.dY := Gear^.dY * t; + end; + + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + + end; + + + CheckCollision(Gear); + if ((Gear^.State and gstCollision) <> 0) then + begin + StopSoundChan(Gear^.SoundChannel); + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + for i:= 0 to 31 do + begin + flower:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot); + if flower <> nil then + with flower^ do + begin + Scale:= 0.75; + dx:= 0.001 * (random(200)); + dy:= 0.001 * (random(200)); + if random(2) = 0 then + dx := -dx; + if random(2) = 0 then + dy := -dy; + FrameTicks:= random(250) + 250; + State:= ord(sprTargetBee); + end; + end; + DeleteGear(Gear); + end; + + if (Gear^.Timer > 0) then + dec(Gear^.Timer) + else + begin + if nuw then + begin + StopSoundChan(Gear^.SoundChannel); + CheckGearDrowning(Gear); + end + else + doStepFallingGear(Gear); + end; +end; + +procedure doStepBee(Gear: PGear); +begin + AllInactive := 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, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end; + dec(Gear^.Timer); + if Gear^.Timer = 0 then + begin + Gear^.Hedgehog^.Gear^.Message:= Gear^.Hedgehog^.Gear^.Message and (not gmAttack); + Gear^.Hedgehog^.Gear^.State:= Gear^.Hedgehog^.Gear^.State and (not gstAttacking); + AttackBar:= 0; + + Gear^.SoundChannel := LoopSound(sndBee); + Gear^.Timer := 5000; + // save initial speed in otherwise unused Friction variable + Gear^.Friction := Distance(Gear^.dX, Gear^.dY); + Gear^.doStep := @doStepBeeWork + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepShotIdle(Gear: PGear); +begin + AllInactive := false; + inc(Gear^.Timer); + if Gear^.Timer > 75 then + begin + DeleteGear(Gear); + AfterAttack + end +end; + +procedure doStepShotgunShot(Gear: PGear); +var + i: LongWord; + shell: PVisualGear; +begin + AllInactive := 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.QWordValue / -17179869184; + shell^.dY := gear^.dY.QWordValue / -17179869184; + shell^.Frame := 0 + end; + Gear^.State := Gear^.State or gstAnimation + end; + exit + end else + if(Gear^.Hedgehog^.Gear = nil) or ((Gear^.Hedgehog^.Gear^.State and gstMoving) <> 0) then + begin + DeleteGear(Gear); + AfterAttack; + exit + end + else + inc(Gear^.Timer); + + i := 200; + repeat + Gear^.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 := @doStepShotIdle +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure spawnBulletTrail(Bullet: PGear); +var oX, oY: hwFloat; + VGear: PVisualGear; +begin + if Bullet^.PortalCounter = 0 then + begin + ox:= CurrentHedgehog^.Gear^.X + Int2hwFloat(GetLaunchX(CurrentHedgehog^.CurAmmoType, hwSign(CurrentHedgehog^.Gear^.dX), CurrentHedgehog^.Gear^.Angle)); + oy:= CurrentHedgehog^.Gear^.Y + Int2hwFloat(GetLaunchY(CurrentHedgehog^.CurAmmoType, CurrentHedgehog^.Gear^.Angle)); + end + else + begin + ox:= Bullet^.Elasticity; + oy:= Bullet^.Friction; + end; + + // Bullet trail + VGear := AddVisualGear(hwRound(ox), hwRound(oy), vgtLineTrail); + + if VGear <> nil then + begin + VGear^.X:= hwFloat2Float(ox); + VGear^.Y:= hwFloat2Float(oy); + VGear^.dX:= hwFloat2Float(Bullet^.X); + VGear^.dY:= hwFloat2Float(Bullet^.Y); + + // reached edge of land. assume infinite beam. Extend it way out past camera + if (hwRound(Bullet^.X) and LAND_WIDTH_MASK <> 0) + or (hwRound(Bullet^.Y) and LAND_HEIGHT_MASK <> 0) then + // only extend if not under water + if hwRound(Bullet^.Y) < cWaterLine then + begin + VGear^.dX := VGear^.dX + max(LAND_WIDTH,4096) * (VGear^.dX - VGear^.X); + VGear^.dY := VGear^.dY + max(LAND_WIDTH,4096) * (VGear^.dY - VGear^.Y); + end; + + VGear^.Timer := 200; + end; +end; + +procedure doStepBulletWork(Gear: PGear); +var + i, x, y: LongWord; + oX, oY: hwFloat; + VGear: PVisualGear; +begin + AllInactive := 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); + // let's interrupt before a collision to give portals a chance to catch the bullet + if (Gear^.Damage = 1) and (Gear^.Tag = 0) and not(CheckLandValue(x, y, lfLandMask)) then + begin + Gear^.Tag := 1; + Gear^.Damage := 0; + Gear^.X := Gear^.X - Gear^.dX; + Gear^.Y := Gear^.Y - Gear^.dY; + CheckGearDrowning(Gear); + break; + end + else + Gear^.Tag := 0; + + if Gear^.Damage > 5 then + if Gear^.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 ((not SuddenDeathDmg and (WaterOpacity < $FF)) or (SuddenDeathDmg and (SDWaterOpacity < $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 (Ammoz[Gear^.AmmoType].Ammo.NumPerTurn <= CurrentHedgehog^.MultiShootAttacks) and ((GameFlags and gfArtillery) = 0) then + cArtillery := false; + + // Bullet Hit + if (hwRound(Gear^.X) and LAND_WIDTH_MASK = 0) and (hwRound(Gear^.Y) and LAND_HEIGHT_MASK = 0) then + begin + VGear := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBulletHit); + if VGear <> nil then + begin + VGear^.Angle := DxDy2Angle(-Gear^.dX, Gear^.dY); + end; + end; + + spawnBulletTrail(Gear); + Gear^.doStep := @doStepShotIdle + end; +end; + +procedure doStepDEagleShot(Gear: PGear); +begin + PlaySound(sndGun); + // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just plain old weird angles + Gear^.X := Gear^.X + Gear^.dX * 3; + Gear^.Y := Gear^.Y + Gear^.dY * 3; + Gear^.doStep := @doStepBulletWork +end; + +procedure doStepSniperRifleShot(Gear: PGear); +var + HHGear: PGear; + shell: PVisualGear; +begin + cArtillery := true; + HHGear := 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) then + dec(HHGear^.Angle,32) + end; + + if (HHGear^.Message and gmAttack) <> 0 then + begin + shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell); + if shell <> nil then + begin + shell^.dX := gear^.dX.QWordValue / -8589934592; + shell^.dY := gear^.dY.QWordValue / -8589934592; + 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); + // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just weird angles + Gear^.X := Gear^.X + Gear^.dX * 3; + Gear^.Y := Gear^.Y + Gear^.dY * 3; + Gear^.doStep := @doStepBulletWork; + end + else + 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) then + dec(HHGear^.Angle); + + if (TurnTimeLeft > 0) then + dec(TurnTimeLeft) + else + begin + DeleteGear(Gear); + AfterAttack + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepActionTimer(Gear: PGear); +begin +dec(Gear^.Timer); +case Gear^.Kind of + gtATStartGame: + begin + AllInactive := false; + if Gear^.Timer = 0 then + begin + AddCaption(trmsg[sidStartFight], cWhiteColor, capgrpGameState); + 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(_S'N'); + SendIPC(_S'q'); + GameState := gsExit + end + end; + end; +if Gear^.Timer = 0 then + DeleteGear(Gear) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepPickHammerWork(Gear: PGear); +var + i, ei, x, y: LongInt; + HHGear: PGear; +begin + AllInactive := false; + HHGear := Gear^.Hedgehog^.Gear; + dec(Gear^.Timer); + if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then + dec(TurnTimeLeft); + if (TurnTimeLeft = 0) or (Gear^.Timer = 0) + or((Gear^.Message and gmDestroy) <> 0) + or((HHGear^.State and gstHHDriven) =0) then + begin + StopSoundChan(Gear^.SoundChannel); + DeleteGear(Gear); + AfterAttack; + doStepHedgehogMoving(HHGear); // for gfInfAttack + exit + end; + + x:= hwRound(Gear^.X); + y:= hwRound(Gear^.Y); + if (Gear^.Timer mod 33) = 0 then + begin + HHGear^.State := HHGear^.State or gstNoDamage; + doMakeExplosion(x, y + 7, 6, Gear^.Hedgehog, EXPLDontDraw); + HHGear^.State := HHGear^.State and (not gstNoDamage) + end; + + if (Gear^.Timer mod 47) = 0 then + begin + // ok. this was an attempt to turn off dust if not actually drilling land. I have no idea why it isn't working as expected + if (( (y + 12) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y + 12, x] > 255) then + for i:= 0 to 1 do + AddVisualGear(x - 5 + Random(10), y + 12, vgtDust); + + i := x - Gear^.Radius - LongInt(GetRandom(2)); + ei := x + Gear^.Radius + LongInt(GetRandom(2)); + while i <= ei do + begin + DrawExplosion(i, y + 3, 3); + inc(i, 1) + end; + + if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9), lfIndestructible) then + begin + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + _1_9; + end; + SetAllHHToActive; + end; + if TestCollisionYwithGear(Gear, 1) <> 0 then + begin + Gear^.dY := _0; + SetLittle(HHGear^.dX); + HHGear^.dY := _0; + end + else + begin + if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y + Gear^.dY + cGravity), lfLandMask) then + begin + Gear^.dY := Gear^.dY + cGravity; + Gear^.Y := Gear^.Y + Gear^.dY + end; + if hwRound(Gear^.Y) > cWaterLine then + Gear^.Timer := 1 + end; + + Gear^.X := Gear^.X + HHGear^.dX; + if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y)-cHHRadius, lfLandMask) then + begin + HHGear^.X := Gear^.X; + HHGear^.Y := Gear^.Y - int2hwFloat(cHHRadius) + end; + + if (Gear^.Message and gmAttack) <> 0 then + if (Gear^.State and gsttmpFlag) <> 0 then + Gear^.Timer := 1 + else //there would be a mistake. + else + if (Gear^.State and gsttmpFlag) = 0 then + Gear^.State := Gear^.State or gsttmpFlag; + if ((Gear^.Message and gmLeft) <> 0) then + Gear^.dX := - _0_3 + else + if ((Gear^.Message and gmRight) <> 0) then + Gear^.dX := _0_3 + else Gear^.dX := _0; +end; + +procedure doStepPickHammer(Gear: PGear); +var + i, y: LongInt; + ar: TRangeArray; + HHGear: PGear; +begin + i := 0; + HHGear := 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 := @doStepPickHammerWork +end; + +//////////////////////////////////////////////////////////////////////////////// +var + BTPrevAngle, BTSteps: LongInt; + +procedure doStepBlowTorchWork(Gear: PGear); +var + HHGear: PGear; + b: boolean; + prevX: LongInt; +begin + AllInactive := false; + dec(Gear^.Timer); + if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then + dec(TurnTimeLeft); + + HHGear := Gear^.Hedgehog^.Gear; + + HedgehogChAngle(HHGear); + + b := false; + + if abs(LongInt(HHGear^.Angle) - BTPrevAngle) > 7 then + begin + Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.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 (gmAttack or gmUp or gmDown)) or gmLeft + else + HHGear^.Message := (HHGear^.Message and (gmAttack or gmUp or gmDown)) or gmRight; + + 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), + lfIndestructible) then HedgehogStep(HHGear); + + if (prevX = hwRound(HHGear^.X)) and + CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y), + lfIndestructible) 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)),lfIndestructible) 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 + begin + DrawTunnel(HHGear^.X + Gear^.dX * cHHRadius, + HHGear^.Y + Gear^.dY * cHHRadius - _1 - + ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7), + Gear^.dX, Gear^.dY, + cHHStepTicks, cHHRadius * 2 + 7); + end; + + if (TurnTimeLeft = 0) or (Gear^.Timer = 0) + or ((HHGear^.Message and gmAttack) <> 0) then + begin + HHGear^.Message := 0; + HHGear^.State := HHGear^.State and (not gstNotKickable); + DeleteGear(Gear); + AfterAttack + end +end; + +procedure doStepBlowTorch(Gear: PGear); +var + HHGear: PGear; +begin + BTPrevAngle := High(LongInt); + BTSteps := 0; + HHGear := Gear^.Hedgehog^.Gear; + HedgehogChAngle(HHGear); + Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.dX); + Gear^.dY := AngleCos(HHGear^.Angle) * ( - _0_5); + DrawTunnel(HHGear^.X, + HHGear^.Y + Gear^.dY * cHHRadius - _1 - + ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7), + Gear^.dX, Gear^.dY, + cHHStepTicks, cHHRadius * 2 + 7); + HHGear^.Message := 0; + HHGear^.State := HHGear^.State or gstNotKickable; + Gear^.doStep := @doStepBlowTorchWork +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepMine(Gear: PGear); +var vg: PVisualGear; + dxdy: hwFloat; +begin + if Gear^.Health = 0 then dxdy:= hwAbs(Gear^.dX)+hwAbs(Gear^.dY); + if (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^.Health = 0) then + begin + if (dxdy > _0_4) and (Gear^.State and gstCollision <> 0) then + inc(Gear^.Damage, hwRound(dxdy * _50)); + + if ((GameTicks and $FF) = 0) and (Gear^.Damage > random(30)) then + begin + vg:= AddVisualGear(hwRound(Gear^.X) - 4 + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke); + if vg <> nil then + vg^.Scale:= 0.5 + end; + + if (Gear^.Damage > 35) then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end + end; + + 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, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear) + end + else + begin + vg:= AddVisualGear(hwRound(Gear^.X) - 4 + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke); + if vg <> nil then + vg^.Scale:= 0.5; + PlaySound(sndVaporize); + Gear^.Health := 0; + Gear^.Damage := 0; + Gear^.State := Gear^.State and (not gstAttacking) + end; + exit + end; + dec(Gear^.Timer); + end + else // gsttmpFlag = 0 + if (TurnTimeLeft = 0) + or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime)) + or (Gear^.Hedgehog^.Gear = nil) then + Gear^.State := Gear^.State or gsttmpFlag; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSMine(Gear: PGear); +begin + // TODO: do real calculation? + if TestCollisionXwithGear(Gear, 2) + or (TestCollisionYwithGear(Gear, -2) <> 0) + or TestCollisionXwithGear(Gear, -2) + or (TestCollisionYwithGear(Gear, 2) <> 0) then + begin + if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then + begin + PlaySound(sndRopeAttach); + Gear^.dX:= _0; + Gear^.dY:= _0; + AddGearCI(Gear); + end; + end + else + begin + DeleteCI(Gear); + doStepFallingGear(Gear); + AllInactive := false; + CalcRotationDirAngle(Gear); + end; + + if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then + begin + 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 = 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end else + if (Gear^.Timer and $FF) = 0 then + PlaySound(sndMineTick); + + dec(Gear^.Timer); + end + end + else // gsttmpFlag = 0 + if (TurnTimeLeft = 0) + or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime)) + or (Gear^.Hedgehog^.Gear = nil) then + Gear^.State := Gear^.State or gsttmpFlag; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepDynamite(Gear: PGear); +begin + doStepFallingGear(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, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end; + dec(Gear^.Timer); +end; + +/////////////////////////////////////////////////////////////////////////////// + +procedure doStepRollingBarrel(Gear: PGear); +var + i: LongInt; + particle: PVisualGear; + dxdy: hwFloat; +begin + if (Gear^.dY.QWordValue = 0) and (Gear^.dY.QWordValue = 0) and (TestCollisionYwithGear(Gear, 1) = 0) then + SetLittle(Gear^.dY); + Gear^.State := Gear^.State or gstAnimation; + if Gear^.Health < cBarrelHealth then Gear^.State:= Gear^.State and not gstFrozen; + + if ((Gear^.dX.QWordValue <> 0) + or (Gear^.dY.QWordValue <> 0)) then + begin + DeleteCI(Gear); + AllInactive := false; + dxdy:= hwAbs(Gear^.dX)+hwAbs(Gear^.dY); + doStepFallingGear(Gear); + if (Gear^.State and gstCollision <> 0) and(dxdy > _0_4) then + begin + if (TestCollisionYwithGear(Gear, 1) <> 0) then + begin + Gear^.State := Gear^.State or gsttmpFlag; + for i:= min(12, hwRound(dxdy*_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.QWordValue / 21474836480) + end + end; + inc(Gear^.Damage, hwRound(dxdy * _50)) + end; + CalcRotationDirAngle(Gear); + //CheckGearDrowning(Gear) + end + else + 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) <> 0) then + Gear^.dY := _0; + if hwAbs(Gear^.dX) < _0_001 then + Gear^.dX := _0; + + if (Gear^.Health > 0) and ((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 + doStepCase(Gear); +end; + +procedure doStepCase(Gear: PGear); +var + i, x, y: LongInt; + k: TGearType; + dX, dY: HWFloat; + hog: PHedgehog; + sparkles: PVisualGear; + gi: PGear; +begin + k := Gear^.Kind; + + if (Gear^.Message and gmDestroy) > 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 (gmLJump or gmHJump)); + exit + end; + if (k = gtExplosives) and (Gear^.Health < cBarrelHealth) then Gear^.State:= Gear^.State and not gstFrozen; + + if ((k <> gtExplosives) and (Gear^.Damage > 0)) or ((k = gtExplosives) and (Gear^.Health<=0)) then + begin + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + hog:= Gear^.Hedgehog; + + DeleteGear(Gear); + // <-- delete gear! + + if k = gtCase then + begin + doMakeExplosion(x, y, 25, hog, 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, hog, EXPLAutoSound); + for i:= 0 to 31 do + begin + dX := AngleCos(i * 64) * _0_5 * (getrandomf + _1); + dY := AngleSin(i * 64) * _0_5 * (getrandomf + _1); + AddGear(x, y, gtFlame, 0, dX, dY, 0); + AddGear(x, y, gtFlame, gstTmpFlag, -dX, -dY, 0); + end + end; + 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 + begin + Gear^.doStep := @doStepRollingBarrel; + exit; + end + else 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; + end + else + begin + if (Gear^.Pos <> posCaseHealth) and (GameTicks and $1FFF = 0) then // stir 'em up periodically + begin + gi := GearsList; + while gi <> nil do + begin + if gi^.Kind = gtGenericFaller then + begin + gi^.Active:= true; + gi^.X:= int2hwFloat(GetRandom(rightX-leftX)+leftX); + gi^.Y:= int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY); + gi^.dX:= _90-(GetRandomf*_360); + gi^.dY:= _90-(GetRandomf*_360) + end; + gi := gi^.NextGear + end + end; + + if Gear^.Timer = 500 then + begin +(* Can't make sparkles team coloured without working out what the next team is going to be. This should be solved, really, since it also screws up + voices. Reinforcements voices is heard for active team, not team-to-be. Either that or change crate spawn from end of turn to start, although that + has its own complexities. *) + // Abuse a couple of gear values to track origin + Gear^.Angle:= hwRound(Gear^.Y); + Gear^.Tag:= random(2); + inc(Gear^.Timer) + end; + if Gear^.Timer < 1833 then inc(Gear^.Timer); + if Gear^.Timer = 1000 then + begin + sparkles:= AddVisualGear(hwRound(Gear^.X), Gear^.Angle, vgtDust, 1); + if sparkles <> nil then + begin + sparkles^.dX:= 0; + sparkles^.dY:= 0; + sparkles^.Angle:= 270; + if Gear^.Tag = 1 then + sparkles^.Tint:= $3744D7FF + else sparkles^.Tint:= $FAB22CFF + end; + end; + if Gear^.Timer < 1000 then + begin + AllInactive:= false; + exit + end + end; + + + if (Gear^.dY.QWordValue <> 0) + or (TestCollisionYwithGear(Gear, 1) = 0) then + begin + AllInactive := false; + + Gear^.dY := Gear^.dY + cGravity; + + if (Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, -1) <> 0) then + Gear^.dY := _0; + + Gear^.Y := Gear^.Y + Gear^.dY; + + if (not Gear^.dY.isNegative) and (Gear^.dY > _0_001) then + SetAllHHToActive(false); + + if (not Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, 1) <> 0) then + begin + if (Gear^.dY > _0_2) and (k = gtExplosives) then + inc(Gear^.Damage, hwRound(Gear^.dY * _70)); + + 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); +begin + if (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 + Gear^.Tag := 2 + else if Gear^.Tag = 2 then + if Gear^.Timer > 0 then + dec(Gear^.Timer) + else + begin + DeleteGear(Gear); + exit; + end; + + doStepCase(Gear) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepIdle(Gear: PGear); +begin + AllInactive := false; + dec(Gear^.Timer); + if Gear^.Timer = 0 then + begin + DeleteGear(Gear); + AfterAttack + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepShover(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.State := HHGear^.State or gstNoDamage; + DeleteCI(HHGear); + + AmmoShove(Gear, 30, 115); + + HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving; + Gear^.Timer := 250; + Gear^.doStep := @doStepIdle +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepWhip(Gear: PGear); +var + HHGear: PGear; + i: LongInt; +begin + HHGear := 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)) or gstMoving; + + Gear^.Timer := 250; + Gear^.doStep := @doStepIdle +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepFlame(Gear: PGear); +var + gX,gY,i: LongInt; + sticky: Boolean; + vgt: PVisualGear; + tdX,tdY: HWFloat; +begin + sticky:= (Gear^.State and gsttmpFlag) <> 0; + if not sticky then AllInactive := false; + + if TestCollisionYwithGear(Gear, 1) = 0 then + begin + AllInactive := false; + + if ((GameTicks mod 100) = 0) then + begin + vgt:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFire, gstTmpFlag); + if vgt <> nil then + begin + vgt^.dx:= 0; + vgt^.dy:= 0; + vgt^.FrameTicks:= 1800 div (Gear^.Tag mod 3 + 2); + end; + end; + + + if Gear^.dX.QWordValue > _0_01.QWordValue then + Gear^.dX := Gear^.dX * _0_995; + + Gear^.dY := Gear^.dY + cGravity; + // if sticky then Gear^.dY := Gear^.dY + cGravity; + + if Gear^.dY.QWordValue > _0_2.QWordValue then + Gear^.dY := Gear^.dY * _0_995; + + //if sticky 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 sticky and (GameTicks and $F = 0) then + begin + Gear^.Radius := 7; + tdX:= Gear^.dX; + tdY:= Gear^.dY; + Gear^.dX.QWordValue:= 120000000; + Gear^.dY.QWordValue:= 429496730; + Gear^.dX.isNegative:= getrandom(2)<>1; + Gear^.dY.isNegative:= true; + AmmoShove(Gear, 2, 125); + Gear^.dX:= tdX; + Gear^.dY:= tdY; + Gear^.Radius := 1 + end; + if Gear^.Timer > 0 then + begin + dec(Gear^.Timer); + inc(Gear^.Damage) + end + else + begin + gX := hwRound(Gear^.X); + gY := hwRound(Gear^.Y); + // Standard fire + if not sticky then + begin + if ((GameTicks and $1) = 0) then + begin + Gear^.Radius := 7; + tdX:= Gear^.dX; + tdY:= Gear^.dY; + Gear^.dX.QWordValue:= 214748365; + Gear^.dY.QWordValue:= 429496730; + Gear^.dX.isNegative:= getrandom(2)<>1; + Gear^.dY.isNegative:= true; + AmmoShove(Gear, 6, 100); + Gear^.dX:= tdX; + Gear^.dY:= tdY; + Gear^.Radius := 1; + end + else if ((GameTicks and $3) = 3) then + doMakeExplosion(gX, gY, 8, Gear^.Hedgehog, 0);//, EXPLNoDamage); + //DrawExplosion(gX, gY, 4); + + if ((GameTicks and $7) = 0) and (Random(2) = 0) then + for i:= Random(2) downto 0 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:= Random(3) downto 0 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 not sticky then + begin + if ((GameTicks and $3) = 0) and (Random(1) = 0) then + for i:= Random(2) downto 0 do + AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); + end + else + for i:= Random(3) downto 0 do + AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); + + DeleteGear(Gear) + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepFirePunchWork(Gear: PGear); +var + HHGear: PGear; +begin + AllInactive := false; + if ((Gear^.Message and gmDestroy) <> 0) then + begin + DeleteGear(Gear); + AfterAttack; + exit + end; + + HHGear := 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)), + lfIndestructible) then + HHGear^.Y := HHGear^.Y + HHGear^.dY +end; + +procedure doStepFirePunch(Gear: PGear); +var + HHGear: PGear; +begin + AllInactive := false; + HHGear := Gear^.Hedgehog^.Gear; + DeleteCI(HHGear); + //HHGear^.X := int2hwFloat(hwRound(HHGear^.X)) - _0_5; WTF? + 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); + + PlaySoundV(TSound(ord(sndFirePunch1) + GetRandom(6)), HHGear^.Hedgehog^.Team^.voicepack) +end; + +//////////////////////////////////////////////////////////////////////////////// + +procedure doStepParachuteWork(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + + inc(Gear^.Timer); + + if (TestCollisionYwithGear(HHGear, 1) <> 0) + or ((HHGear^.State and gstHHDriven) = 0) + or CheckGearDrowning(HHGear) + or ((Gear^.Message and gmAttack) <> 0) then + begin + with HHGear^ do + begin + Message := 0; + SetLittle(dX); + dY := _0; + State := State or gstMoving; + end; + DeleteGear(Gear); + isCursorVisible := false; + ApplyAmmoChanges(HHGear^.Hedgehog^); + exit + end; + + HHGear^.X := HHGear^.X + cWindSpeed * 200; + + if (Gear^.Message and gmLeft) <> 0 then + HHGear^.X := HHGear^.X - cMaxWindSpeed * 80 + + else if (Gear^.Message and gmRight) <> 0 then + HHGear^.X := HHGear^.X + cMaxWindSpeed * 80; + + if (Gear^.Message and gmUp) <> 0 then + HHGear^.Y := HHGear^.Y - cGravity * 40 + + else if (Gear^.Message and gmDown) <> 0 then + HHGear^.Y := HHGear^.Y + cGravity * 40; + + // don't drift into obstacles + if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then + HHGear^.X := HHGear^.X - int2hwFloat(hwSign(HHGear^.dX)); + HHGear^.Y := HHGear^.Y + cGravity * 100; + Gear^.X := HHGear^.X; + Gear^.Y := HHGear^.Y +end; + +procedure doStepParachute(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + + DeleteCI(HHGear); + + AfterAttack; + + HHGear^.State := HHGear^.State and (not (gstAttacking or gstAttacked or gstMoving)); + HHGear^.Message := HHGear^.Message and (not gmAttack); + + Gear^.doStep := @doStepParachuteWork; + + Gear^.Message := HHGear^.Message; + doStepParachuteWork(Gear) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepAirAttackWork(Gear: PGear); +begin + AllInactive := 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: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtNapalmBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0); + 3: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtDrill, gsttmpFlag, cBombsSpeed * Gear^.Tag, _0, Gear^.Timer + 1); + //4: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtWaterMelon, 0, cBombsSpeed * + // Gear^.Tag, _0, 5000); + end; + Gear^.dX := Gear^.dX + int2hwFloat(30 * Gear^.Tag); + StopSoundChan(Gear^.SoundChannel, 4000); + end; + + if (GameTicks and $3F) = 0 then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); + + if (hwRound(Gear^.X) > (max(LAND_WIDTH,4096)+2048)) or (hwRound(Gear^.X) < -2048) then + begin + // avoid to play forever (is this necessary?) + StopSoundChan(Gear^.SoundChannel); + DeleteGear(Gear) + end; +end; + +procedure doStepAirAttack(Gear: PGear); +begin + AllInactive := false; + + if Gear^.X.QWordValue = 0 then + begin + Gear^.Tag := 1; + Gear^.X := -_2048; + end + else + begin + Gear^.Tag := -1; + Gear^.X := int2hwFloat(max(LAND_WIDTH,4096) + 2048); + end; + + Gear^.Y := int2hwFloat(topY-300); + Gear^.dX := int2hwFloat(Gear^.Target.X - 5 * Gear^.Tag * 15); + + // calcs for Napalm Strike, so that it will hit the target (without wind at least :P) + if (Gear^.State = 2) then + Gear^.dX := Gear^.dX - cBombsSpeed * Gear^.Tag * 900 + // calcs for regular falling gears + else if (int2hwFloat(Gear^.Target.Y) - Gear^.Y > _0) then + Gear^.dX := Gear^.dX - cBombsSpeed * hwSqrt((int2hwFloat(Gear^.Target.Y) - Gear^.Y) * 2 / + cGravity) * Gear^.Tag; + + Gear^.Health := 6; + Gear^.doStep := @doStepAirAttackWork; + Gear^.SoundChannel := LoopSound(sndPlane, 4000); + +end; + +//////////////////////////////////////////////////////////////////////////////// + +procedure doStepAirBomb(Gear: PGear); +begin + AllInactive := false; + doStepFallingGear(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + with mobileRecord do + if (performRumble <> nil) and (not fastUntilLag) then + performRumble(kSystemSoundID_Vibrate); + exit + end; + if (GameTicks and $3F) = 0 then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) +end; + +//////////////////////////////////////////////////////////////////////////////// + +procedure doStepGirder(Gear: PGear); +var + HHGear: PGear; + x, y, tx, ty: hwFloat; +begin + AllInactive := false; + + HHGear := Gear^.Hedgehog^.Gear; + tx := int2hwFloat(Gear^.Target.X); + ty := int2hwFloat(Gear^.Target.Y); + x := HHGear^.X; + y := HHGear^.Y; + + if (Distance(tx - x, ty - y) > _256) + or (not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprAmGirder].Width div 2, Gear^.Target.Y - SpritesData[sprAmGirder].Height div 2, sprAmGirder, Gear^.State, true, false)) then + begin + PlaySound(sndDenied); + HHGear^.Message := HHGear^.Message and (not gmAttack); + HHGear^.State := HHGear^.State and (not gstAttacking); + HHGear^.State := HHGear^.State or gstHHChooseTarget; + isCursorVisible := true; + DeleteGear(Gear) + end + else + begin + PlaySound(sndPlaced); + DeleteGear(Gear); + AfterAttack; + end; + + HHGear^.State := HHGear^.State and (not (gstAttacking or gstAttacked)); + HHGear^.Message := HHGear^.Message and (not gmAttack); +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepTeleportAfter(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + doStepHedgehogMoving(HHGear); + // if not infattack mode wait for hedgehog finish falling to collect cases + if ((GameFlags and gfInfAttack) <> 0) + or ((HHGear^.State and gstMoving) = 0) + or (Gear^.Hedgehog^.Gear^.Damage > 0) + or ((HHGear^.State and gstDrowning) = 1) then + begin + DeleteGear(Gear); + AfterAttack + end +end; + +procedure doStepTeleportAnim(Gear: PGear); +begin + if (Gear^.Hedgehog^.Gear^.Damage > 0) then + begin + DeleteGear(Gear); + AfterAttack; + end; + inc(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; +begin + AllInactive := false; + + HHGear := Gear^.Hedgehog^.Gear; + if not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprHHTelepMask].Width div 2, + Gear^.Target.Y - SpritesData[sprHHTelepMask].Height div 2, + sprHHTelepMask, 0, false, false) then + begin + HHGear^.Message := HHGear^.Message and (not gmAttack); + 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(Gear^.Target.X); + HHGear^.Y := int2hwFloat(Gear^.Target.Y); + HHGear^.State := HHGear^.State or gstMoving; + Gear^.Hedgehog^.Unplaced := false; + isCursorVisible := false; + playSound(sndWarp) + end; + Gear^.Target.X:= NoPointX +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSwitcherWork(Gear: PGear); +var + HHGear: PGear; + hedgehog: PHedgehog; + State: Longword; +begin + AllInactive := false; + + if ((Gear^.Message and (not gmSwitch)) <> 0) or (TurnTimeLeft = 0) then + begin + hedgehog := Gear^.Hedgehog; + //Msg := Gear^.Message and (not gmSwitch); + DeleteGear(Gear); + ApplyAmmoChanges(hedgehog^); + + HHGear := CurrentHedgehog^.Gear; + ApplyAmmoChanges(HHGear^.Hedgehog^); + //HHGear^.Message := Msg; + exit + end; + + if (Gear^.Message and gmSwitch) <> 0 then + begin + HHGear := CurrentHedgehog^.Gear; + HHGear^.Message := HHGear^.Message and (not gmSwitch); + Gear^.Message := Gear^.Message and (not gmSwitch); + State := HHGear^.State; + HHGear^.State := 0; + HHGear^.Z := cHHZ; + HHGear^.Active := false; + HHGear^.Message:= HHGear^.Message or gmRemoveFromList or gmAddToList; + + PlaySound(sndSwitchHog); + + repeat + CurrentTeam^.CurrHedgehog := Succ(CurrentTeam^.CurrHedgehog) mod (CurrentTeam^.HedgehogsNumber); + until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil) and + (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear^.Damage = 0) and + (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Effects[heFrozen]=0); + + SwitchCurrentHedgehog(@CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog]); + AmmoMenuInvalidated:= true; + + HHGear := CurrentHedgehog^.Gear; + HHGear^.State := State; + HHGear^.Active := true; + FollowGear := HHGear; + HHGear^.Z := cCurrHHZ; + HHGear^.Message:= HHGear^.Message or gmRemoveFromList or gmAddToList; + Gear^.X := HHGear^.X; + Gear^.Y := HHGear^.Y + end; +end; + +procedure doStepSwitcher(Gear: PGear); +var + HHGear: PGear; +begin + Gear^.doStep := @doStepSwitcherWork; + + HHGear := Gear^.Hedgehog^.Gear; + OnUsedAmmo(HHGear^.Hedgehog^); + with HHGear^ do + begin + State := State and (not gstAttacking); + Message := Message and (not gmAttack) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepMortar(Gear: PGear); +var + dX, dY, gdX, gdY: hwFloat; + i: LongInt; +begin + AllInactive := false; + gdX := Gear^.dX; + gdY := Gear^.dY; + + doStepFallingGear(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound); + gdX.isNegative := not gdX.isNegative; + gdY.isNegative := not gdY.isNegative; + gdX:= gdX*_0_2; + gdY:= gdY*_0_2; + + for i:= 0 to 4 do + begin + dX := gdX + rndSign(GetRandomf) * _0_03; + dY := gdY + rndSign(GetRandomf) * _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 + begin + if hwRound(Gear^.Y) > cWaterLine then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) + else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepKamikazeWork(Gear: PGear); +var + i: LongWord; + HHGear: PGear; + sparkles: PVisualGear; + hasWishes: boolean; +begin + AllInactive := false; + hasWishes:= ((Gear^.Message and (gmPrecise or gmSwitch)) = (gmPrecise or gmSwitch)); + if hasWishes then + Gear^.AdvBounce:= 1; + + HHGear := Gear^.Hedgehog^.Gear; + if HHGear = nil then + begin + DeleteGear(Gear); + exit + end; + + HHGear^.State := HHGear^.State or gstNoDamage; + DeleteCI(HHGear); + + Gear^.X := HHGear^.X; + Gear^.Y := HHGear^.Y; + if (GameTicks mod 2 = 0) and hasWishes then + begin + sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust, 1); + if sparkles <> nil then + begin + sparkles^.Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF; + sparkles^.Angle:= random(360); + end + end; + + 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 + begin + if Gear^.AdvBounce <> 0 then + Gear^.Pos := 3 + else + Gear^.Pos := 2; + end; + + 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 + 7); + + upd := 0 + end; + + if Gear^.Health < Gear^.Damage then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); + if hasWishes then + for i:= 0 to 31 do + begin + sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot); + if sparkles <> nil then + with sparkles^ do + begin + Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF; + Angle:= random(360); + dx:= 0.001 * (random(200)); + dy:= 0.001 * (random(200)); + if random(2) = 0 then + dx := -dx; + if random(2) = 0 then + dy := -dy; + FrameTicks:= random(400) + 250 + end + end; + AfterAttack; + HHGear^.Message:= HHGear^.Message or gmDestroy; + DeleteGear(Gear); + end + else + begin + dec(Gear^.Health, Gear^.Damage); + Gear^.Damage := 0 + end +end; + +procedure doStepKamikazeIdle(Gear: PGear); +begin + AllInactive := false; + dec(Gear^.Timer); + if Gear^.Timer = 0 then + begin + Gear^.Pos := 1; + PlaySoundV(sndKamikaze, Gear^.Hedgehog^.Team^.voicepack); + Gear^.doStep := @doStepKamikazeWork + end +end; + +procedure doStepKamikaze(Gear: PGear); +var + HHGear: PGear; +begin + AllInactive := false; + + HHGear := 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 := @doStepKamikazeIdle +end; + +//////////////////////////////////////////////////////////////////////////////// + +const cakeh = 27; +var + CakePoints: array[0..Pred(cakeh)] of record + x, y: hwFloat; + end; + CakeI: Longword; + +procedure doStepCakeExpl(Gear: PGear); +begin + AllInactive := false; + + inc(Gear^.Tag); + if Gear^.Tag < 2250 then + exit; + + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg, Gear^.Hedgehog, EXPLAutoSound); + AfterAttack; + DeleteGear(Gear) +end; + +procedure doStepCakeDown(Gear: PGear); +var + gi: PGear; + dmg, dmgBase: LongInt; + fX, fY, tdX, tdY: hwFloat; +begin + AllInactive := false; + + inc(Gear^.Tag); + if Gear^.Tag < 100 then + exit; + Gear^.Tag := 0; + + if Gear^.Pos = 0 then + begin +///////////// adapted from doMakeExplosion /////////////////////////// + //fX:= Gear^.X; + //fY:= Gear^.Y; + //fX.QWordValue:= fX.QWordValue and $FFFFFFFF00000000; + //fY.QWordValue:= fY.QWordValue and $FFFFFFFF00000000; + fX:= int2hwFloat(hwRound(Gear^.X)); + fY:= int2hwFloat(hwRound(Gear^.Y)); + dmgBase:= cakeDmg shl 1 + cHHRadius div 2; + gi := GearsList; + while gi <> nil do + begin + if gi^.Kind = gtHedgehog then + begin + dmg:= 0; + tdX:= gi^.X-fX; + tdY:= gi^.Y-fY; + if hwRound(hwAbs(tdX)+hwAbs(tdY)) < dmgBase then + dmg:= dmgBase - max(hwRound(Distance(tdX, tdY)),gi^.Radius); + if (dmg > 1) then dmg:= ModifyDamage(min(dmg div 2, cakeDmg), gi); + if (dmg > 1) then + if (CurrentHedgehog^.Gear = gi) and (not gi^.Invulnerable) then + gi^.State := gi^.State or gstLoser + else + gi^.State := gi^.State or gstWinner; + end; + gi := gi^.NextGear + end; +////////////////////////////////////////////////////////////////////// + Gear^.doStep := @doStepCakeExpl; + PlaySound(sndCake) + end + else dec(Gear^.Pos) +end; + + +procedure doStepCakeWork(Gear: PGear); +var + tdx, tdy: hwFloat; +begin + AllInactive := false; + + inc(Gear^.Tag); + if Gear^.Tag < 7 then + exit; + + dec(Gear^.Health); + Gear^.Timer := Gear^.Health*10; + if Gear^.Health mod 100 = 0 then + Gear^.PortalCounter:= 0; + // This is not seconds, but at least it is *some* feedback + if (Gear^.Health = 0) or ((Gear^.Message and gmAttack) <> 0) then + begin + FollowGear := Gear; + Gear^.RenderTimer := false; + Gear^.doStep := @doStepCakeDown; + exit + end; + + cakeStep(Gear); + + 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; +end; + +procedure doStepCakeUp(Gear: PGear); +var + i: Longword; +begin + AllInactive := 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); +begin + AllInactive := false; + + Gear^.dY := Gear^.dY + cGravity; + if TestCollisionYwithGear(Gear, 1) <> 0 then + Gear^.doStep := @doStepCakeUp + else + begin + Gear^.Y := Gear^.Y + Gear^.dY; + if CheckGearDrowning(Gear) then + AfterAttack + end +end; + +procedure doStepCake(Gear: PGear); +var + HHGear: PGear; +begin + AllInactive := false; + + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.Message := HHGear^.Message and (not gmAttack); + Gear^.CollisionMask:= lfNotCurrentMask; + + FollowGear := Gear; + + Gear^.doStep := @doStepCakeFall +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSeductionWork(Gear: PGear); +var i: LongInt; + hogs: PGearArrayS; +begin + AllInactive := false; + hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Radius); + if hogs.size > 0 then + begin + for i:= 0 to hogs.size - 1 do + with hogs.ar^[i]^ do + begin + if hogs.ar^[i] <> CurrentHedgehog^.Gear then + begin + dX:= _50 * cGravity * (Gear^.X - X) / _25; + dY:= -_450 * cGravity; + Active:= true; + end + end; + end ; + AfterAttack; + DeleteGear(Gear); +(* + 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 + else + else + begin + AfterAttack; + DeleteGear(Gear) + end*) +end; + +procedure doStepSeductionWear(Gear: PGear); +var heart: PVisualGear; +begin + AllInactive := false; + inc(Gear^.Timer); + if Gear^.Timer > 250 then + begin + Gear^.Timer := 0; + inc(Gear^.Pos); + if Gear^.Pos = 5 then + PlaySoundV(sndYoohoo, Gear^.Hedgehog^.Team^.voicepack) + end; + + if (Gear^.Pos = 14) and (RealTicks and $3 = 0) then + begin + heart:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot); + if heart <> nil then + with heart^ do + begin + dx:= 0.001 * (random(200)); + dy:= 0.001 * (random(200)); + if random(2) = 0 then + dx := -dx; + if random(2) = 0 then + dy := -dy; + FrameTicks:= random(750) + 1000; + State:= ord(sprSeduction) + end; + end; + + if Gear^.Pos = 15 then + Gear^.doStep := @doStepSeductionWork +end; + +procedure doStepSeduction(Gear: PGear); +begin + AllInactive := false; + //DeleteCI(Gear^.Hedgehog^.Gear); + Gear^.doStep := @doStepSeductionWear +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepWaterUp(Gear: PGear); +var + i: LongWord; +begin + if (Gear^.Tag = 0) + or (cWaterLine = 0) then + begin + DeleteGear(Gear); + exit + end; + + AllInactive := false; + + inc(Gear^.Timer); + if Gear^.Timer = 17 then + Gear^.Timer := 0 + else + exit; + + if cWaterLine > 0 then + begin + dec(cWaterLine); + for i:= 0 to LAND_WIDTH - 1 do + Land[cWaterLine, i] := 0; + SetAllToActive + end; + + dec(Gear^.Tag); +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepDrillDrilling(Gear: PGear); +var + t: PGearArray; + tempColl: Word; +begin + AllInactive := false; + if (Gear^.Timer > 0) and (Gear^.Timer mod 10 <> 0) then + begin + dec(Gear^.Timer); + exit; + end; + + DrawTunnel(Gear^.X, Gear^.Y, Gear^.dX, Gear^.dY, 2, 6); + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + if (Gear^.Timer mod 30) = 0 then + AddVisualGear(hwRound(Gear^.X + _20 * Gear^.dX), hwRound(Gear^.Y + _20 * Gear^.dY), vgtDust); + if (CheckGearDrowning(Gear)) then + begin + StopSoundChan(Gear^.SoundChannel); + exit + end; + + tempColl:= Gear^.CollisionMask; + Gear^.CollisionMask:= $007F; + if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) <> 0) or TestCollisionXWithGear(Gear, hwSign(Gear^.dX)) or (GameTicks > Gear^.FlightTime) then + t := CheckGearsCollision(Gear) + else t := nil; + Gear^.CollisionMask:= tempColl; + //fixes drill not exploding when touching HH bug + + if (Gear^.Timer = 0) or ((t <> nil) and (t^.Count <> 0)) + or ( ((Gear^.State and gsttmpFlag) = 0) and (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and (not TestCollisionXWithGear(Gear, hwSign(Gear^.dX)))) +// CheckLandValue returns true if the type isn't matched + or (not CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y), lfIndestructible)) then + begin + //out of time or exited ground + StopSoundChan(Gear^.SoundChannel); + if (Gear^.State and gsttmpFlag) <> 0 then + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound) + else + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end + + else if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and (not TestCollisionXWithGear(Gear, hwSign(Gear^.dX))) then + begin + StopSoundChan(Gear^.SoundChannel); + Gear^.Tag := 1; + Gear^.doStep := @doStepDrill + end; + + dec(Gear^.Timer); +end; + +procedure doStepDrill(Gear: PGear); +var + t: PGearArray; + oldDx, oldDy: hwFloat; + t2: hwFloat; +begin + AllInactive := false; + + if (Gear^.State and gsttmpFlag) = 0 then + Gear^.dX := Gear^.dX + cWindSpeed; + + oldDx := Gear^.dX; + oldDy := Gear^.dY; + + doStepFallingGear(Gear); + + if (GameTicks and $3F) = 0 then + begin + if hwRound(Gear^.Y) > cWaterLine then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) + else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) + end; + + if ((Gear^.State and gstCollision) <> 0) then + begin + //hit + Gear^.dX := oldDx; + Gear^.dY := oldDy; + + if GameTicks > Gear^.FlightTime then + t := CheckGearsCollision(Gear) + else + t := nil; + if (t = nil) or (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 if (t <> nil) then + begin + //explode right on contact with HH + if (Gear^.State and gsttmpFlag) <> 0 then + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound) + else + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit; + end; + + Gear^.SoundChannel := LoopSound(sndDrillRocket); + Gear^.doStep := @doStepDrillDrilling; + + if (Gear^.State and gsttmpFlag) <> 0 then + gear^.RenderTimer:= true; + if Gear^.Timer > 0 then dec(Gear^.Timer) + end + else if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Tag <> 0) then + begin + if Gear^.Timer > 0 then + dec(Gear^.Timer) + else + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + end + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepBallgunWork(Gear: PGear); +var + HHGear, ball: PGear; + rx, ry: hwFloat; + gX, gY: LongInt; +begin + AllInactive := false; + dec(Gear^.Timer); + HHGear := Gear^.Hedgehog^.Gear; + HedgehogChAngle(HHGear); + gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); + gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); + if (Gear^.Timer mod 100) = 0 then + begin + rx := rndSign(getRandomf * _0_1); + ry := rndSign(getRandomf * _0_1); + + ball:= AddGear(gx, gy, gtBall, 0, SignAs(AngleSin(HHGear^.Angle) * _0_8, HHGear^.dX) + rx, AngleCos(HHGear^.Angle) * ( - _0_8) + ry, 0); + ball^.CollisionMask:= lfNotCurrentMask; + + PlaySound(sndGun); + end; + + if (Gear^.Timer = 0) or ((HHGear^.State and gstHHDriven) = 0) then + begin + DeleteGear(Gear); + AfterAttack + end +end; + +procedure doStepBallgun(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown)); + HHGear^.State := HHGear^.State or gstNotKickable; + Gear^.doStep := @doStepBallgunWork +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepRCPlaneWork(Gear: PGear); + +const cAngleSpeed = 3; +var + HHGear: PGear; + i: LongInt; + dX, dY: hwFloat; + fChanged: boolean; + trueAngle: Longword; + t: PGear; +begin + AllInactive := false; + + HHGear := Gear^.Hedgehog^.Gear; + FollowGear := Gear; + + if Gear^.Timer > 0 then + dec(Gear^.Timer); + + 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 + end + else + begin + if ((Gear^.Message and gmLeft) <> 0) then + begin + fChanged := true; + Gear^.Angle := (Gear^.Angle + (4096 - cAngleSpeed)) mod 4096 + end; + + if ((Gear^.Message and gmRight) <> 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 (GameTicks and $FF) = 0 then + if Gear^.Timer < 3500 then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace) + else + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); + + if ((HHGear^.Message and gmAttack) <> 0) and (Gear^.Health <> 0) then + begin + HHGear^.Message := HHGear^.Message and (not gmAttack); + 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 gmLJump) <> 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); + + CheckCollision(Gear); + + if ((Gear^.State and gstCollision) <> 0) or CheckGearDrowning(Gear) then + begin + StopSoundChan(Gear^.SoundChannel); + StopSound(sndRideOfTheValkyries); + ResumeMusic; + + if ((Gear^.State and gstCollision) <> 0) then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, Gear^.Hedgehog, EXPLAutoSound); + for i:= 0 to 15 do + begin + dX := AngleCos(i * 64) * _0_5 * (GetRandomf + _1); + dY := AngleSin(i * 64) * _0_5 * (GetRandomf + _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; + if (GameFlags and gfInfAttack) = 0 then + begin + if TagTurnTimeLeft = 0 then + TagTurnTimeLeft:= TurnTimeLeft; + + TurnTimeLeft:= 14 * 125; + end; + + HHGear^.Message := 0; + ParseCommand('/taunt ' + #1, true) + end +end; + +procedure doStepRCPlane(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := 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 := @doStepRCPlaneWork +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepJetpackWork(Gear: PGear); +var + HHGear: PGear; + fuel, i: LongInt; + move: hwFloat; + isUnderwater: Boolean; + bubble: PVisualGear; +begin + isUnderwater:= cWaterLine < hwRound(Gear^.Y) + Gear^.Radius; + if Gear^.Pos > 0 then + dec(Gear^.Pos); + AllInactive := false; + HHGear := Gear^.Hedgehog^.Gear; + //dec(Gear^.Timer); + move := _0_2; + fuel := 50; +(*if (HHGear^.Message and gmPrecise) <> 0 then + begin + move:= _0_02; + fuel:= 5; + end;*) + if HHGear^.Message and gmPrecise <> 0 then + HedgehogChAngle(HHGear) + else if Gear^.Health > 0 then + begin + if HHGear^.Message and gmUp <> 0 then + begin + if (not HHGear^.dY.isNegative) or (HHGear^.Y > -_256) then + begin + if isUnderwater then + begin + HHGear^.dY := HHGear^.dY - (move * _0_7); + for i:= random(10)+10 downto 0 do + begin + bubble := AddVisualGear(hwRound(HHGear^.X) - 8 + random(16), hwRound(HHGear^.Y) + 16 + random(8), vgtBubble); + if bubble <> nil then + bubble^.dY:= random(20)/10+0.1; + end + end + else HHGear^.dY := HHGear^.dY - move; + end; + dec(Gear^.Health, fuel); + Gear^.MsgParam := Gear^.MsgParam or gmUp; + Gear^.Timer := GameTicks + end; + move.isNegative := (HHGear^.Message and gmLeft) <> 0; + if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then + begin + HHGear^.dX := HHGear^.dX + (move * _0_1); + if isUnderwater then + begin + for i:= random(5)+5 downto 0 do + begin + bubble := AddVisualGear(hwRound(HHGear^.X)+random(8), hwRound(HHGear^.Y) - 8 + random(16), vgtBubble); + if bubble <> nil then + begin + bubble^.dX:= (random(10)/10 + 0.02) * -1; + if (move.isNegative) then + begin + bubble^.X := bubble^.X + 28; + bubble^.dX:= bubble^.dX * (-1) + end + else bubble^.X := bubble^.X - 28; + end; + end + end; + dec(Gear^.Health, fuel div 5); + Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight)); + Gear^.Timer := GameTicks + end + 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; + + i:= Gear^.Health div 20; + + if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then + begin + Gear^.Damage:= i; + //AddCaption('Fuel: '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate); + FreeTexture(Gear^.Tex); + Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + '%', cWhiteColor, fntSmall) + end; + + if (HHGear^.Message and (gmAttack or gmUp or gmLeft or gmRight) <> 0) and + (HHGear^.Message and gmPrecise = 0) then + Gear^.State := Gear^.State and (not gsttmpFlag); + + if HHGear^.Message and gmPrecise = 0 then + HHGear^.Message := HHGear^.Message and (not (gmUp or gmLeft or gmRight)); + HHGear^.State := HHGear^.State or gstMoving; + + Gear^.X := HHGear^.X; + Gear^.Y := HHGear^.Y; + + if not isUnderWater and hasBorder and ((HHGear^.X < _0) + or (hwRound(HHGear^.X) > LAND_WIDTH)) then + HHGear^.dY.isNegative:= false; + + if ((Gear^.State and gsttmpFlag) = 0) + or (HHGear^.dY < _0) then + doStepHedgehogMoving(HHGear); + + if // (Gear^.Health = 0) + (HHGear^.Damage <> 0) + //or CheckGearDrowning(HHGear) + or (cWaterLine + cVisibleWater * 4 < hwRound(HHGear^.Y)) + 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) <> 0)) + or ((Gear^.Message and gmAttack) <> 0) then + begin + with HHGear^ do + begin + Message := 0; + Active := true; + State := State or gstMoving + end; + DeleteGear(Gear); + isCursorVisible := false; + ApplyAmmoChanges(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); + end +end; + +procedure doStepJetpack(Gear: PGear); +var + HHGear: PGear; +begin + Gear^.Pos:= 0; + Gear^.doStep := @doStepJetpackWork; + + HHGear := Gear^.Hedgehog^.Gear; + FollowGear := HHGear; + AfterAttack; + with HHGear^ do + begin + State := State and (not gstAttacking); + Message := Message and (not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight)); + + if (dY < _0_1) and (dY > -_0_1) then + begin + Gear^.State := Gear^.State or gsttmpFlag; + dY := dY - _0_2 + end + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepBirdyDisappear(Gear: PGear); +begin + AllInactive := 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; +begin + HHGear := Gear^.Hedgehog^.Gear; + if HHGear = nil then + begin + DeleteGear(Gear); + exit + end; + + move := _0_2; + fuel := 50; + + if Gear^.Pos > 0 then + dec(Gear^.Pos, 1) + else if (HHGear^.Message and (gmLeft or gmRight or gmUp)) <> 0 then + Gear^.Pos := 500; + + if HHGear^.dX.isNegative then + Gear^.Tag := -1 + else + Gear^.Tag := 1; + + if (HHGear^.Message and gmUp) <> 0 then + begin + if (not HHGear^.dY.isNegative) + or (HHGear^.Y > -_256) then + HHGear^.dY := HHGear^.dY - move; + + dec(Gear^.Health, fuel); + Gear^.MsgParam := Gear^.MsgParam or gmUp; + end; + + if (HHGear^.Message and gmLeft) <> 0 then move.isNegative := true; + if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then + begin + HHGear^.dX := HHGear^.dX + (move * _0_1); + dec(Gear^.Health, fuel div 5); + Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight)); + 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 gmAttack <> 0) then + begin + HHGear^.Message := HHGear^.Message and (not gmAttack); + 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 (gmUp or gmPrecise or gmLeft or gmRight) <> 0 then + Gear^.State := Gear^.State and (not gsttmpFlag); + + HHGear^.Message := HHGear^.Message and (not (gmUp or gmPrecise or gmLeft or gmRight)); + 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. + // this is probably not needed anymore + if not CurrentTeam^.ExtDriven 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) <> 0)) + or ((Gear^.Message and gmAttack) <> 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; + end +end; + +procedure doStepBirdyDescend(Gear: PGear); +var + HHGear: PGear; +begin + if Gear^.Timer > 0 then + dec(Gear^.Timer, 1) + else if Gear^.Hedgehog^.Gear = nil then + begin + DeleteGear(Gear); + AfterAttack; + exit + end; + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.Message := HHGear^.Message and (not (gmUp or gmPrecise or gmLeft or gmRight)); + if abs(hwRound(HHGear^.Y - Gear^.Y)) > 32 then + begin + if Gear^.Timer = 0 then + Gear^.Y := Gear^.Y + _0_1 + end + else if Gear^.Timer = 0 then + begin + Gear^.doStep := @doStepBirdyFly; + HHGear^.dY := -_0_2 + end +end; + +procedure doStepBirdyAppear(Gear: PGear); +begin + Gear^.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; + end +end; + +procedure doStepBirdy(Gear: PGear); +var + HHGear: PGear; +begin + gear^.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 := -1 + else + Gear^.Tag := 1; + Gear^.Pos := 0; + AllInactive := false; + FollowGear := HHGear; + with HHGear^ do + begin + State := State and (not gstAttacking); + Message := Message and (not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight)) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +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, Gear^.Hedgehog, EXPLPoisoned, $C0E0FFE0); + 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.QWordValue / 21474836480); + end; + + DeleteGear(Gear); + exit + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doPortalColorSwitch(); +var CurWeapon: PAmmo; +begin + if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and ((CurrentHedgehog^.Gear^.Message and gmSwitch) <> 0) then + with CurrentHedgehog^ do + if (CurAmmoType = amPortalGun) then + begin + CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and (not gmSwitch); + + CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^); + if CurWeapon^.Pos <> 0 then + CurWeapon^.Pos := 0 + + else + CurWeapon^.Pos := 1; + end; +end; + +procedure doStepPortal(Gear: PGear); +var + iterator, conPortal: PGear; + s, r, nx, ny, ox, oy, poffs, noffs, pspeed, nspeed, + resetx, resety, resetdx, resetdy: hwFloat; + sx, sy, rh, resetr: LongInt; + hasdxy, isbullet, iscake, isCollision: Boolean; +begin + doPortalColorSwitch(); + + // destroy portal if ground it was attached too is gone + if (Land[hwRound(Gear^.Y), hwRound(Gear^.X)] <= lfAllObjMask) + or (Gear^.Timer < 1) + or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team) + or (hwRound(Gear^.Y) > cWaterLine) then + begin + deleteGear(Gear); + EXIT; + end; + + if (TurnTimeLeft < 1) + or (Gear^.Health < 1) then + dec(Gear^.Timer); + + if Gear^.Timer < 10000 then + gear^.RenderTimer := true; + + // abort if there is no other portal connected to this one + if (Gear^.LinkedGear = nil) then + exit; + if ((Gear^.LinkedGear^.Tag and 1) = 0) then // or if it's still moving; + exit; + + conPortal := Gear^.LinkedGear; + + // check all gears for stuff to port through + iterator := nil; + while true do + begin + + // iterate through GearsList + if iterator = nil then + iterator := GearsList + else + iterator := iterator^.NextGear; + + // end of list? + if iterator = nil then + break; + + // don't port portals or other gear that wouldn't make sense + if (iterator^.Kind in [gtPortal, gtRope, gtAirAttack, gtIceGun]) + or (iterator^.PortalCounter > 32) then + continue; + + // don't port hogs on rope + // TODO: this will also prevent hogs while falling after rope use from + // falling through portals... fix that! + + // check if gear fits through portal + if (iterator^.Radius > Gear^.Radius) then + continue; + + // this is the max range we accept incoming gears in + r := Int2hwFloat(iterator^.Radius+Gear^.Radius); + + // too far away? + if (iterator^.X < Gear^.X - r) + or (iterator^.X > Gear^.X + r) + or (iterator^.Y < Gear^.Y - r) + or (iterator^.Y > Gear^.Y + r) then + continue; + + hasdxy := (((iterator^.dX.QWordValue <> 0) or (iterator^.dY.QWordValue <> 0)) or ((iterator^.State or gstMoving) = 0)); + + // in case the object is not moving, let's asume it's falling towards the portal + if not hasdxy then + begin + if Gear^.Y < iterator^.Y then + continue; + ox:= Gear^.X - iterator^.X; + oy:= Gear^.Y - iterator^.Y; + end + else + begin + ox:= iterator^.dX; + oy:= iterator^.dY; + end; + + // cake will need extra treatment... it's so delicious and moist! + iscake:= (iterator^.Kind = gtCake); + + // won't port stuff that does not move towards the front/portal entrance + if iscake then + begin + if not (((iterator^.X - Gear^.X)*ox + (iterator^.Y - Gear^.Y)*oy).isNegative) then + continue; + end + else + if not ((Gear^.dX*ox + Gear^.dY*oy).isNegative) then + continue; + + isbullet:= (iterator^.Kind in [gtShotgunShot, gtDEagleShot, gtSniperRifleShot, gtSineGunShot]); + + r:= int2hwFloat(iterator^.Radius); + + if not (isbullet or iscake) then + begin + // wow! good candidate there, let's see if the distance and direction is okay! + if hasdxy then + begin + s := Distance(iterator^.dX, iterator^.dY); + // if the resulting distance is 0 skip this gear + if s.QWordValue = 0 then + continue; + s := r / s; + ox:= iterator^.X + s * iterator^.dX; + oy:= iterator^.Y + s * iterator^.dY; + end + else + begin + ox:= iterator^.X; + oy:= iterator^.Y + r; + end; + + if (hwRound(Distance(Gear^.X-ox,Gear^.Y-oy)) > Gear^.Radius + 1 ) then + continue; + end; + + // draw bullet trail + if isbullet then + spawnBulletTrail(iterator); + + // calc gear offset in portal vector direction + ox := (iterator^.X - Gear^.X); + oy := (iterator^.Y - Gear^.Y); + poffs:= (Gear^.dX * ox + Gear^.dY * oy); + + if not isBullet and poffs.isNegative then + continue; + + // only port bullets close to the portal + if isBullet and (not (hwAbs(poffs) < _3)) then + continue; + + // + // gears that make it till here will definately be ported + // + // (but old position/movement vector might be restored in case there's + // not enough space on the other side) + // + + resetr := iterator^.Radius; + resetx := iterator^.X; + resety := iterator^.Y; + resetdx := iterator^.dX; + resetdy := iterator^.dY; + + // create a normal of the portal vector, but ... + nx := Gear^.dY; + ny := Gear^.dX; + // ... decide where the top is based on the hog's direction when firing the portal + if Gear^.Elasticity.isNegative then + nx.isNegative := (not nx.isNegative) + else + ny.isNegative := not ny.isNegative; + + // calc gear offset in portal normal vector direction + noffs:= (nx * ox + ny * oy); + + if isBullet and (noffs.Round >= Longword(Gear^.Radius)) then + continue; + + // avoid gravity related loops of not really moving gear + if not (iscake or isbullet) + and (Gear^.dY.isNegative) + and (conPortal^.dY.isNegative) + and ((iterator^.dX.QWordValue + iterator^.dY.QWordValue) < _0_08.QWordValue) + and (iterator^.PortalCounter > 0) then + continue; + + // calc gear speed along to the vector and the normal vector of the portal + if hasdxy then + begin + pspeed:= (Gear^.dX * iterator^.dX + Gear^.dY * iterator^.dY); + nspeed:= (nx * iterator^.dX + ny * iterator^.dY); + end + else + begin + pspeed:= hwAbs(cGravity * oy); + nspeed:= _0; + end; + + // creating normal vector of connected (exit) portal + nx := conPortal^.dY; + ny := conPortal^.dX; + if conPortal^.Elasticity.isNegative then + nx.isNegative := (not nx.isNegative) + else + ny.isNegative := not ny.isNegative; + + // inverse cake's normal movement direction, + // as if it just walked through a hole + //if iscake then nspeed.isNegative:= not nspeed.isNegative; + +//AddFileLog('poffs:'+cstr(poffs)+' noffs:'+cstr(noffs)+' pspeed:'+cstr(pspeed)+' nspeed:'+cstr(nspeed)); + iterator^.dX := -pspeed * conPortal^.dX + nspeed * nx; + iterator^.dY := -pspeed * conPortal^.dY + nspeed * ny; + + // make the gear's exit position close to the portal while + // still respecting the movement direction + + // determine the distance (in exit vector direction) + // that we want the gear at + if iscake then + ox:= (r - _0_7) + else + ox:= (r * _1_5); + s:= ox / poffs; + poffs:= ox; + if (nspeed.QWordValue <> 0) + and (pspeed > _0) then + noffs:= noffs * s * (nspeed / pspeed); + + // move stuff with high normal offset closer to the portal's center + if not isbullet then + begin + s := hwAbs(noffs) + r - int2hwFloat(Gear^.Radius); + if s > _0 then + noffs:= noffs - SignAs(s,noffs) + end; + + iterator^.X := conPortal^.X + poffs * conPortal^.dX + noffs * nx; + iterator^.Y := conPortal^.Y + poffs * conPortal^.dY + noffs * ny; + + if not hasdxy and (not (conPortal^.dY.isNegative)) then + begin + iterator^.dY:= iterator^.dY + hwAbs(cGravity * (iterator^.Y - conPortal^.Y)) + end; + + // see if the space on the exit side actually is enough + + if not (isBullet or isCake) then + begin + // TestCollisionXwithXYShift requires a hwFloat for xShift + ox.QWordValue := _1.QWordValue; + ox.isNegative := not iterator^.dX.isNegative; + + sx := hwSign(iterator^.dX); + sy := hwSign(iterator^.dY); + + if iterator^.Radius > 1 then + iterator^.Radius := iterator^.Radius - 1; + + // check front + isCollision := TestCollisionY(iterator, sy) + or TestCollisionX(iterator, sx); + + if not isCollision then + begin + // check center area (with half the radius so that the + // the square check won't check more pixels than we want to) + iterator^.Radius := 1 + resetr div 2; + rh := resetr div 4; + isCollision := TestCollisionYwithXYShift(iterator, 0, -sy * rh, sy, false) + or TestCollisionXwithXYShift(iterator, ox * rh, 0, sx, false); + end; + + iterator^.Radius := resetr; + + if isCollision then + begin + // collision! oh crap! go back! + iterator^.X := resetx; + iterator^.Y := resety; + iterator^.dX := resetdx; + iterator^.dY := resetdy; + continue; + end; + end; + + // + // You're now officially portaled! + // + + // Until loops are reliably broken + if iscake then + iterator^.PortalCounter:= 33 + else + begin + inc(iterator^.PortalCounter); + iterator^.Active:= true; + iterator^.State:= iterator^.State and (not gstHHHJump) or gstMoving; + end; + + // is it worth adding an arcsin table? Just how often would we end up doing something like this? + // SYNCED ANGLE UPDATE + if iterator^.Kind = gtRCPlane then + begin + // recycling as temp vars + resety.isNegative:= false; + resety.QWordValue:= 4294967296 * 112; + resetx.isNegative:= false; + resetx.QWordValue:= 4294967296 * 35; + resetdx.isNegative:= false; + resetdx.QWordValue:= 4294967296 * 1152; + + resetdy:=hwAbs(iterator^.dX*4); + resetdy:= resetdy + hwPow(resetdy,3)/_6 + _3 * hwPow(resetdy,5) / _40 + _5 * hwPow(resetdy,7) / resety + resetx * hwPow(resetdy,9) / resetdx; + iterator^.Angle:= hwRound(resetdy*_2048 / _PI); + if not iterator^.dY.isNegative then iterator^.Angle:= 2048-iterator^.Angle; + if iterator^.dX.isNegative then iterator^.Angle:= 4096-iterator^.Angle; + end + // VISUAL USE OF ANGLE ONLY + else if (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtKamikaze) and (CurAmmoGear^.Hedgehog = iterator^.Hedgehog) then + begin + iterator^.Angle:= DxDy2AttackAngle(iterator^.dX, iterator^.dY); + iterator^.Angle:= 2048-iterator^.Angle; + if iterator^.dX.isNegative then iterator^.Angle:= 4096-iterator^.Angle; + end; + + if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) + and (iterator = CurrentHedgehog^.Gear) + and (CurAmmoGear <> nil) + and (CurAmmoGear^.Kind =gtRope) then + CurAmmoGear^.PortalCounter:= 1; + + if not isbullet and (iterator^.State and gstInvisible = 0) + and (iterator^.Kind <> gtFlake) then + FollowGear := iterator; + + // store X/Y values of exit for net bullet trail + if isbullet then + begin + iterator^.Elasticity:= iterator^.X; + iterator^.Friction := iterator^.Y; + end; + + if Gear^.Health > 1 then + dec(Gear^.Health); + end; +end; + + + +procedure loadNewPortalBall(oldPortal: PGear; destroyGear: Boolean); +var + CurWeapon: PAmmo; +begin + if CurrentHedgehog <> nil then + with CurrentHedgehog^ do + begin + CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^); + if (CurAmmoType = amPortalGun) then + begin + if not destroyGear then + begin + // switch color of ball to opposite of oldPortal + if (oldPortal^.Tag and 2) = 0 then + CurWeapon^.Pos:= 1 + else + CurWeapon^.Pos:= 0; + end; + + // make the ball visible + CurWeapon^.Timer := 0; + end + end; + if destroyGear then + oldPortal^.Timer:= 0; +end; + +procedure doStepMovingPortal_real(Gear: PGear); +var + x, y, tx, ty: LongInt; + s: hwFloat; +begin + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + tx := 0; + ty := 0; + // avoid compiler hints + + if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] > 255) then + begin + Gear^.State := Gear^.State or gstCollision; + Gear^.State := Gear^.State and (not gstMoving); + + if (Land[y, x] and lfBouncy <> 0) + or (not CalcSlopeTangent(Gear, x, y, tx, ty, 255)) + or (DistanceI(tx,ty) < _12) then // reject shots at too irregular terrain + begin + loadNewPortalBall(Gear, true); + EXIT; + end; + + // making a normalized normal vector + s := _1/DistanceI(tx,ty); + Gear^.dX := s * ty; + Gear^.dY := -s * tx; + + Gear^.DirAngle := DxDy2Angle(-Gear^.dY,Gear^.dX); + if not Gear^.dX.isNegative then + Gear^.DirAngle := 180-Gear^.DirAngle; + + if ((Gear^.LinkedGear = nil) + or (hwRound(Distance(Gear^.X - Gear^.LinkedGear^.X,Gear^.Y-Gear^.LinkedGear^.Y)) >=Gear^.Radius*2)) then + begin + loadNewPortalBall(Gear, false); + inc(Gear^.Tag); + Gear^.doStep := @doStepPortal; + end + else + loadNewPortalBall(Gear, true); + end + + else if (y > cWaterLine) + or (y < -max(LAND_WIDTH,4096)) + or (x > 2*max(LAND_WIDTH,4096)) + or (x < -max(LAND_WIDTH,4096)) then + loadNewPortalBall(Gear, true); +end; + +procedure doStepMovingPortal(Gear: PGear); +begin + doPortalColorSwitch(); + doStepPerPixel(Gear, @doStepMovingPortal_real, true); + if (Gear^.Timer < 1) + or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team) then + deleteGear(Gear); +end; + +procedure doStepPortalShot(newPortal: PGear); +var + iterator: PGear; + s: hwFloat; + CurWeapon: PAmmo; +begin + s:= Distance (newPortal^.dX, newPortal^.dY); + + // Adds the hog speed (only that part in/directly against shot direction) + // to the shot speed (which we triple previously btw) + // (This is done my projecting the hog movement vector onto the shot movement vector and then adding the resulting length + // to the scaler) + s := (_2 * s + (newPortal^.dX * CurrentHedgehog^.Gear^.dX + newPortal^.dY * CurrentHedgehog^.Gear^.dY ) / s) / s; + newPortal^.dX := newPortal^.dX * s; + newPortal^.dY := newPortal^.dY * s; + + newPortal^.LinkedGear := nil; + + if CurrentHedgehog <> nil then + with CurrentHedgehog^ do + begin + CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^); + // let's save the HH's dX's direction so we can decide where the "top" of the portal hole + newPortal^.Elasticity.isNegative := CurrentHedgehog^.Gear^.dX.isNegative; + // when doing a backjump the dx is the opposite of the facing direction + if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) then + newPortal^.Elasticity.isNegative := not newPortal^.Elasticity.isNegative; + + // make portal gun look unloaded + if (CurWeapon <> nil) and (CurAmmoType = amPortalGun) then + CurWeapon^.Timer := CurWeapon^.Timer or 2; + + iterator := GearsList; + while iterator <> nil do + begin + if (iterator^.Kind = gtPortal) then + if (iterator <> newPortal) and (iterator^.Timer > 0) and (iterator^.Hedgehog = CurrentHedgehog) then + begin + if ((iterator^.Tag and 2) = (newPortal^.Tag and 2)) then + begin + iterator^.Timer:= 0; + end + else + begin + // link portals with each other + newPortal^.LinkedGear := iterator; + iterator^.LinkedGear := newPortal; + iterator^.Health := newPortal^.Health; + end; + end; + iterator^.PortalCounter:= 0; + iterator := iterator^.NextGear + end; + + if newPortal^.LinkedGear <> nil then + begin + // This jiggles gears, to ensure a portal connection just placed under a gear takes effect. + iterator:= GearsList; + while iterator <> nil do + begin + if not (iterator^.Kind in [gtPortal, gtAirAttack, gtKnife]) and ((iterator^.Hedgehog <> CurrentHedgehog) + or ((iterator^.Message and gmAllStoppable) = 0)) then + begin + iterator^.Active:= true; + if iterator^.dY.QWordValue = 0 then + iterator^.dY.isNegative:= false; + iterator^.State:= iterator^.State or gstMoving; + DeleteCI(iterator); + //inc(iterator^.dY.QWordValue,10); + end; + iterator:= iterator^.NextGear + end + end + end; + newPortal^.State := newPortal^.State and (not gstCollision); + newPortal^.State := newPortal^.State or gstMoving; + newPortal^.doStep := @doStepMovingPortal; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepPiano(Gear: PGear); +var + r0, r1: LongInt; + odY: hwFloat; +begin + AllInactive := false; + if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and + ((CurrentHedgehog^.Gear^.Message and gmSlot) <> 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; + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote); + CurrentHedgehog^.Gear^.MsgParam := 0; + CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and (not gmSlot); + end; + + if (*((Gear^.Pos = 3) and ((GameFlags and gfSolidLand) <> 0)) or*) (Gear^.Pos = 5) then + begin + Gear^.dY := Gear^.dY + cGravity * 2; + Gear^.Y := Gear^.Y + Gear^.dY; + if CheckGearDrowning(Gear) then + begin + Gear^.Y:= Gear^.Y + _50; + OnUsedAmmo(CurrentHedgehog^); + if CurrentHedgehog^.Gear <> nil then + begin + // Drown the hedgehog. Could also just delete it, but hey, this gets a caption + CurrentHedgehog^.Gear^.Active := true; + CurrentHedgehog^.Gear^.X := Gear^.X; + CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128; + CurrentHedgehog^.Unplaced := false; + if TagTurnTimeLeft = 0 then + TagTurnTimeLeft:= TurnTimeLeft; + TurnTimeLeft:= 0 + end; + ResumeMusic + end; + exit + end; + + odY:= Gear^.dY; + doStepFallingGear(Gear); + + if (Gear^.State and gstDrowning) <> 0 then + begin + Gear^.Y:= Gear^.Y + _50; + OnUsedAmmo(CurrentHedgehog^); + if CurrentHedgehog^.Gear <> nil then + begin + // Drown the hedgehog. Could also just delete it, but hey, this gets a caption + CurrentHedgehog^.Gear^.Active := true; + CurrentHedgehog^.Gear^.X := Gear^.X; + CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128; + CurrentHedgehog^.Unplaced := false; + if TagTurnTimeLeft = 0 then + TagTurnTimeLeft:= TurnTimeLeft; + TurnTimeLeft:= 0 + end; + ResumeMusic + end + else if (Gear^.State and gstCollision) <> 0 then + begin + r0 := GetRandom(21); + r1 := GetRandom(21); + doMakeExplosion(hwRound(Gear^.X) - 30 - r0, hwRound(Gear^.Y) + 40, 40 + r1, Gear^.Hedgehog, 0); + doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, 40 + r0, Gear^.Hedgehog, 0); + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 80 + r0, Gear^.Hedgehog, EXPLAutoSound); + for r0:= 0 to 4 do + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote); + Gear^.dY := cGravity * 2 - odY; + Gear^.Pos := Gear^.Pos + 1; + end + else + Gear^.dY := Gear^.dY + cGravity * 2; + // let it fall faster so itdoesn't take too long for the whole attack +end; + + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSineGunShotWork(Gear: PGear); +var + x, y, rX, rY, t, tmp, initHealth: LongInt; + oX, oY, ldX, ldY, sdX, sdY, sine, lx, ly, amp: hwFloat; + justCollided: boolean; +begin + AllInactive := 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 GameTicks + t := getRandom(4096); + + + // used for a work-around detection of area that is within land array, but outside borders + justCollided := 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 + begin + tmp:= GetRandom(2 * Gear^.Radius); + AddGear(x - Gear^.Radius + tmp, y - GetRandom(Gear^.Radius + 1), gtFlame, gsttmpFlag, _0, _0, 0) + end + end; + + if random(100) = 0 then + AddVisualGear(x, y, vgtSmokeTrace); + end + else dec(Gear^.Health, 5); // if underwater get additional damage + 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; +begin + PlaySound(sndSineGun); + + // push the shooting Hedgehog back + HHGear := 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 := @doStepSineGunShotWork; + with mobileRecord do + if (performRumble <> nil) and (not fastUntilLag) then + performRumble(kSystemSoundID_Vibrate); +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepFlamethrowerWork(Gear: PGear); +var + HHGear, flame: PGear; + rx, ry, speed: hwFloat; + i, gX, gY: LongInt; +begin + AllInactive := false; + HHGear := Gear^.Hedgehog^.Gear; + HedgehogChAngle(HHGear); + gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); + gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); + + if (GameTicks and $FF) = 0 then + begin + if (HHGear^.Message and gmRight) <> 0 then + begin + if HHGear^.dX.isNegative and (Gear^.Tag < 20) then + inc(Gear^.Tag) + else if Gear^.Tag > 5 then + dec(Gear^.Tag); + end + else if (HHGear^.Message and gmLeft) <> 0 then + begin + if HHGear^.dX.isNegative and (Gear^.Tag > 5) then + dec(Gear^.Tag) + else if Gear^.Tag < 20 then + inc(Gear^.Tag); + end + end; + + dec(Gear^.Timer); + if Gear^.Timer = 0 then + begin + dec(Gear^.Health); + if (Gear^.Health mod 5) = 0 then + begin + rx := rndSign(getRandomf * _0_1); + ry := rndSign(getRandomf * _0_1); + speed := _0_5 * (_10 / Gear^.Tag); + + flame:= AddGear(gx, gy, gtFlame, gstTmpFlag, + SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx, + AngleCos(HHGear^.Angle) * ( - speed) + ry, 0); + flame^.CollisionMask:= lfNotCurrentMask; + + if (Gear^.Health mod 30) = 0 then + begin + flame:= AddGear(gx, gy, gtFlame, 0, + SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx, + AngleCos(HHGear^.Angle) * ( - speed) + ry, 0); + flame^.CollisionMask:= lfNotCurrentMask; + end + end; + Gear^.Timer:= Gear^.Tag + end; + + if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) then + begin + DeleteGear(Gear); + AfterAttack + end + else + begin + i:= Gear^.Health div 5; + if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then + begin + Gear^.Damage:= i; + FreeTexture(Gear^.Tex); + Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + + '%', cWhiteColor, fntSmall) + end + end +end; + +procedure doStepFlamethrower(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown or gmLeft or gmRight)); + HHGear^.State := HHGear^.State or gstNotKickable; + Gear^.doStep := @doStepFlamethrowerWork +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepLandGunWork(Gear: PGear); +var + HHGear, land: PGear; + rx, ry, speed: hwFloat; + i, gX, gY: LongInt; +begin + AllInactive := false; + HHGear := Gear^.Hedgehog^.Gear; + HedgehogChAngle(HHGear); + gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); + gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); + + if (GameTicks and $FF) = 0 then + begin + if (HHGear^.Message and gmRight) <> 0 then + begin + if HHGear^.dX.isNegative and (Gear^.Tag < 20) then + inc(Gear^.Tag) + else if Gear^.Tag > 5 then + dec(Gear^.Tag); + end + else if (HHGear^.Message and gmLeft) <> 0 then + begin + if HHGear^.dX.isNegative and (Gear^.Tag > 5) then + dec(Gear^.Tag) + else if Gear^.Tag < 20 then + inc(Gear^.Tag); + end + end; + + dec(Gear^.Timer); + if Gear^.Timer = 0 then + begin + dec(Gear^.Health); + + rx := rndSign(getRandomf * _0_1); + ry := rndSign(getRandomf * _0_1); + speed := (_3 / Gear^.Tag); + + land:= AddGear(gx, gy, gtFlake, gstTmpFlag, + SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx, + AngleCos(HHGear^.Angle) * ( - speed) + ry, 0); + land^.CollisionMask:= lfNotCurrentMask; + + Gear^.Timer:= Gear^.Tag + end; + + if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) or ((HHGear^.Message and gmAttack) <> 0) then + begin + HHGear^.Message:= HHGear^.Message and (not gmAttack); + DeleteGear(Gear); + AfterAttack + end + else + begin + i:= Gear^.Health div 10; + if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then + begin + Gear^.Damage:= i; + FreeTexture(Gear^.Tex); + Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + + '%', cWhiteColor, fntSmall) + end + end +end; + +procedure doStepLandGun(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown or gmLeft or gmRight or gmAttack)); + HHGear^.State := HHGear^.State or gstNotKickable; + Gear^.doStep := @doStepLandGunWork +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepPoisonCloud(Gear: PGear); +begin + if Gear^.Timer = 0 then + begin + DeleteGear(Gear); + exit + end; + dec(Gear^.Timer); + Gear^.X:= Gear^.X + Gear^.dX; + Gear^.Y:= Gear^.Y + Gear^.dY; + Gear^.dX := Gear^.dX + cWindSpeed / 4; + Gear^.dY := Gear^.dY + cGravity / 100; + if (GameTicks and $FF) = 0 then + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx or EXPLNoDamage or EXPLDoNotTouchAny or EXPLPoisoned); + AllInactive:= false; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepHammer(Gear: PGear); +var HHGear, tmp, tmp2: PGear; + t: PGearArray; + i: LongInt; +begin +HHGear:= Gear^.Hedgehog^.Gear; +HHGear^.State:= HHGear^.State or gstNoDamage; +DeleteCI(HHGear); + +t:= CheckGearsCollision(Gear); + +for i:= 5 downto 0 do + AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); + +i:= t^.Count; +while i > 0 do + begin + dec(i); + tmp:= t^.ar[i]; + if (tmp^.State and gstNoDamage) = 0 then + if (tmp^.Kind = gtHedgehog) or (tmp^.Kind = gtMine) or (tmp^.Kind = gtExplosives) then + begin + //tmp^.State:= tmp^.State or gstFlatened; + if not tmp^.Invulnerable then + ApplyDamage(tmp, CurrentHedgehog, tmp^.Health div 3, dsUnknown); + //DrawTunnel(tmp^.X, tmp^.Y - _1, _0, _0_5, cHHRadius * 6, cHHRadius * 3); + tmp2:= AddGear(hwRound(tmp^.X), hwRound(tmp^.Y), gtHammerHit, 0, _0, _0, 0); + tmp2^.LinkedGear:= tmp; + SetAllToActive + end + else + begin + end + end; + +HHGear^.State:= HHGear^.State and (not gstNoDamage); +Gear^.Timer:= 250; +Gear^.doStep:= @doStepIdle +end; + +procedure doStepHammerHitWork(Gear: PGear); +var + i, j, ei: LongInt; + HitGear: PGear; +begin + AllInactive := false; + HitGear := Gear^.LinkedGear; + dec(Gear^.Timer); + if (HitGear = nil) or (Gear^.Timer = 0) or ((Gear^.Message and gmDestroy) <> 0) then + begin + DeleteGear(Gear); + exit + end; + + if (Gear^.Timer mod 5) = 0 then + begin + AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); + + i := hwRound(Gear^.X) - HitGear^.Radius + 2; + ei := hwRound(Gear^.X) + HitGear^.Radius - 2; + for j := 1 to 4 do DrawExplosion(i - GetRandom(5), hwRound(Gear^.Y) + 6*j, 3); + for j := 1 to 4 do DrawExplosion(ei + LongInt(GetRandom(5)), hwRound(Gear^.Y) + 6*j, 3); + while i <= ei do + begin + for j := 1 to 11 do DrawExplosion(i, hwRound(Gear^.Y) + 3*j, 3); + inc(i, 1) + end; + + if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9) + , lfIndestructible) then + begin + //Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + _1_9 + end; + end; + if TestCollisionYwithGear(Gear, 1) <> 0 then + begin + Gear^.dY := _0; + SetLittle(HitGear^.dX); + HitGear^.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 + HitGear^.dX; + HitGear^.X := Gear^.X; + HitGear^.Y := Gear^.Y; + SetLittle(HitGear^.dY); + HitGear^.Active:= true; +end; + +procedure doStepHammerHit(Gear: PGear); +var + i, y: LongInt; + ar: TRangeArray; + HHGear: PGear; +begin + i := 0; + HHGear := 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); + + doStepHammerHitWork(Gear); + Gear^.doStep := @doStepHammerHitWork +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepResurrectorWork(Gear: PGear); +var + graves: PGearArrayS; + resgear: PGear; + hh: PHedgehog; + i: LongInt; +begin + if (TurnTimeLeft > 0) then + dec(TurnTimeLeft); + + AllInactive := false; + hh := Gear^.Hedgehog; + + // no, you can't do that here + {DrawCentered(hwRound(hh^.Gear^.X) + WorldDx, hwRound(hh^.Gear^.Y) + WorldDy - + cHHRadius - 14 - hh^.HealthTagTex^.h, hh^.HealthTagTex); + } + (*DrawCircle(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, 1.5, 0, 0, $FF, + $FF);*) + + if ((Gear^.Message and gmUp) <> 0) then + begin + if (GameTicks and $F) <> 0 then + exit; + end + else if (GameTicks and $1FF) <> 0 then + exit; + + if Gear^.Power < 45 then + begin + inc(Gear^.Power); + if TestCollisionYwithGear(hh^.Gear, -1) = 0 then + hh^.Gear^.Y := hh^.Gear^.Y - _1; + end; + + graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius); + + if graves.size = 0 then + begin + StopSoundChan(Gear^.SoundChannel); + Gear^.Timer := 250; + Gear^.doStep := @doStepIdle; + exit; + end; + + if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then + begin + if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0; + dec(hh^.Gear^.Health); + if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then + hh^.Gear^.Damage:= 1; + RenderHealth(hh^); + RecountTeamHealth(hh^.Team); + inc(graves.ar^[Gear^.Tag]^.Health); + inc(Gear^.Tag) +{-for i:= 0 to High(graves) do begin + if hh^.Gear^.Health > 0 then begin + dec(hh^.Gear^.Health); + inc(graves[i]^.Health); + end; + end; -} + end + else + begin + // now really resurrect the hogs with the hp saved in the graves + for i:= 0 to graves.size - 1 do + if graves.ar^[i]^.Health > 0 then + begin + resgear := AddGear(hwRound(graves.ar^[i]^.X), hwRound(graves.ar^[i]^.Y), gtHedgehog, gstWait, _0, _0, 0); + resgear^.Hedgehog := graves.ar^[i]^.Hedgehog; + resgear^.Health := graves.ar^[i]^.Health; + PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := resgear; + graves.ar^[i]^.Message:= graves.ar^[i]^.Message or gmDestroy; + graves.ar^[i]^.Active:= true; + RenderHealth(resgear^.Hedgehog^); + RecountTeamHealth(resgear^.Hedgehog^.Team); + resgear^.Hedgehog^.Effects[heResurrected]:= 1; + // only make hat-less hedgehogs look like zombies, preserve existing hats + + if resgear^.Hedgehog^.Hat = 'NoHat' then + LoadHedgehogHat(resgear^.Hedgehog^, 'Reserved/Zombie'); + end; + + hh^.Gear^.dY := _0; + hh^.Gear^.dX := _0; + doStepHedgehogMoving(hh^.Gear); + StopSoundChan(Gear^.SoundChannel); + Gear^.Timer := 250; + Gear^.doStep := @doStepIdle; + end + //if hh^.Gear^.Health = 0 then doStepHedgehogFree(hh^.Gear); +end; + +procedure doStepResurrector(Gear: PGear); +var + graves: PGearArrayS; + hh: PHedgehog; + i: LongInt; +begin + AllInactive := false; + graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius); + + if graves.size > 0 then + begin + hh := Gear^.Hedgehog; + for i:= 0 to graves.size - 1 do + begin + PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := nil; + graves.ar^[i]^.Health := 0; + end; + Gear^.doStep := @doStepResurrectorWork; + if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then + begin + if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0; + dec(hh^.Gear^.Health); + if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then + hh^.Gear^.Damage:= 1; + RenderHealth(hh^); + RecountTeamHealth(hh^.Team); + inc(graves.ar^[Gear^.Tag]^.Health); + inc(Gear^.Tag) + end + end + else + begin + StopSoundChan(Gear^.SoundChannel); + Gear^.Timer := 250; + Gear^.doStep := @doStepIdle; + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepNapalmBomb(Gear: PGear); +var + i, gX, gY: LongInt; + dX, dY: hwFloat; +begin + AllInactive := false; + doStepFallingGear(Gear); + if (Gear^.Timer > 0) and ((Gear^.State and gstCollision) <> 0) then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound); + gX := hwRound(Gear^.X); + gY := hwRound(Gear^.Y); + for i:= 0 to 10 do + begin + dX := AngleCos(i * 2) * ((_0_1*(i div 5))) * (GetRandomf + _1); + dY := AngleSin(i * 8) * _0_5 * (GetRandomf + _1); + AddGear(gX, gY, gtFlame, 0, dX, dY, 0); + AddGear(gX, gY, gtFlame, 0, dX, -dY, 0); + AddGear(gX, gY, gtFlame, 0, -dX, dY, 0); + AddGear(gX, gY, gtFlame, 0, -dX, -dY, 0); + end; + DeleteGear(Gear); + exit + end; + if (Gear^.Timer = 0) then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound); + for i:= -19 to 19 do + FollowGear := AddGear(hwRound(Gear^.X) + i div 3, hwRound(Gear^.Y), gtFlame, 0, _0_001 * i, _0, 0); + DeleteGear(Gear); + exit + end; + if (GameTicks and $3F) = 0 then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); + dec(Gear^.Timer) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepStructure(Gear: PGear); +var + x, y: LongInt; + HH: PHedgehog; + t: PGear; +begin + HH:= Gear^.Hedgehog; + + if (Gear^.State and gstMoving) <> 0 then + begin + AddGearCI(Gear); + Gear^.dX:= _0; + Gear^.dY:= _0; + Gear^.State:= Gear^.State and (not gstMoving); + end; + + dec(Gear^.Health, Gear^.Damage); + Gear^.Damage:= 0; + + if Gear^.Pos = 1 then + begin + AddGearCI(Gear); + AfterAttack; + if Gear = CurAmmoGear then + CurAmmoGear:= nil; + if HH^.Gear <> nil then + HideHog(HH); + Gear^.Pos:= 2 + end; + + if Gear^.Pos = 2 then + begin + if ((GameTicks mod 100) = 0) and (Gear^.Timer < 1000) then + begin + if (Gear^.Timer mod 10) = 0 then + begin + DeleteCI(Gear); + Gear^.Y:= Gear^.Y - _0_5; + AddGearCI(Gear); + end; + inc(Gear^.Timer); + end; + if Gear^.Tag <= TotalRounds then + Gear^.Pos:= 3; + end; + + if Gear^.Pos = 3 then + if Gear^.Timer < 1000 then + begin + if (Gear^.Timer mod 10) = 0 then + begin + DeleteCI(Gear); + Gear^.Y:= Gear^.Y - _0_5; + AddGearCI(Gear); + end; + inc(Gear^.Timer); + end + else + begin + if HH^.GearHidden <> nil then + RestoreHog(HH); + Gear^.Pos:= 4; + end; + + if Gear^.Pos = 4 then + if ((GameTicks mod 1000) = 0) and ((GameFlags and gfInvulnerable) = 0) then + begin + t:= GearsList; + while t <> nil do + begin + if (t^.Kind = gtHedgehog) and (t^.Hedgehog^.Team^.Clan = HH^.Team^.Clan) then + t^.Invulnerable:= true; + t:= t^.NextGear; + end; + end; + + if Gear^.Health <= 0 then + begin + if HH^.GearHidden <> nil then + RestoreHog(HH); + + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + + DeleteCI(Gear); + DeleteGear(Gear); + + doMakeExplosion(x, y, 50, CurrentHedgehog, EXPLAutoSound); + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +(* + TARDIS needs + Warp in. Pos = 1 + Pause. Pos = 2 + Hide gear (TARDIS hedgehog was nil) + Warp out. Pos = 3 + ... idle active for some time period ... Pos = 4 + Warp in. Pos = 1 + Pause. Pos = 2 + Restore gear (TARDIS hedgehog was not nil) + Warp out. Pos = 3 +*) + +procedure doStepTardisWarp(Gear: PGear); +var HH: PHedgehog; + i,j,cnt: LongWord; +begin +HH:= Gear^.Hedgehog; +if Gear^.Pos = 2 then + begin + StopSoundChan(Gear^.SoundChannel); + if (Gear^.Timer = 0) then + begin + if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible = 0) then + begin + AfterAttack; + if Gear = CurAmmoGear then CurAmmoGear := nil; + if (HH^.Gear^.Damage = 0) and (HH^.Gear^.Health > 0) and + ((Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then + HideHog(HH) + end + //else if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible <> 0) then + else if (HH^.GearHidden <> nil) then// and (HH^.Gear^.State and gstInvisible <> 0) then + RestoreHog(HH) + end; + + inc(Gear^.Timer); + if (Gear^.Timer > 2000) and ((GameTicks mod 2000) = 1000) then + begin + Gear^.SoundChannel := LoopSound(sndTardis); + Gear^.Pos:= 3 + end + end; + +if (Gear^.Pos = 1) and (GameTicks and $1F = 0) and (Gear^.Power < 255) then + begin + inc(Gear^.Power); + if (Gear^.Power = 172) and (HH^.Gear <> nil) and + (HH^.Gear^.Damage = 0) and (HH^.Gear^.Health > 0) and + ((HH^.Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then + with HH^.Gear^ do + begin + State:= State or gstAnimation; + Tag:= 2; + Timer:= 0; + Pos:= 0 + end + end; +if (Gear^.Pos = 3) and (GameTicks and $1F = 0) and (Gear^.Power > 0) then + dec(Gear^.Power); +if (Gear^.Pos = 1) and (Gear^.Power = 255) and ((GameTicks mod 2000) = 1000) then + Gear^.Pos:= 2; +if (Gear^.Pos = 3) and (Gear^.Power = 0) then + begin + StopSoundChan(Gear^.SoundChannel); + if HH^.GearHidden = nil then + begin + DeleteGear(Gear); + exit + end; + Gear^.Pos:= 4; + // This condition might need tweaking + Gear^.Timer:= GetRandom(cHedgehogTurnTime*TeamsCount)+cHedgehogTurnTime + end; + +if (Gear^.Pos = 4) then + begin + cnt:= 0; + for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do + for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do + if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil) + and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0) + and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then + inc(cnt); + if (cnt = 0) or SuddenDeathDmg or (Gear^.Timer = 0) then + begin + if HH^.GearHidden <> nil then + FindPlace(HH^.GearHidden, false, 0, LAND_WIDTH,true); + + if HH^.GearHidden <> nil then + begin + Gear^.X:= HH^.GearHidden^.X; + Gear^.Y:= HH^.GearHidden^.Y; + end; + Gear^.Timer:= 0; + + if (HH^.GearHidden <> nil) and (cnt = 0) then // do an emergency jump back in this case. the team needs you! + begin + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtExplosion); + Gear^.Pos:= 2; + Gear^.Power:= 255; + end + else begin + Gear^.SoundChannel := LoopSound(sndTardis); + Gear^.Pos:= 1; + Gear^.Power:= 0; + end + end + else if (CurrentHedgehog^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan) then dec(Gear^.Timer) + end; + +end; + +procedure doStepTardis(Gear: PGear); +var i,j,cnt: LongWord; + HH: PHedgehog; +begin +(* + Conditions for not activating. + 1. Hog is last of his clan + 2. Sudden Death is in play + 3. Hog is a king +*) + HH:= Gear^.Hedgehog; + if HH^.Gear <> nil then + if (HH^.Gear = nil) or (HH^.King) or (SuddenDeathDmg) then + begin + if HH^.Gear <> nil then + begin + HH^.Gear^.Message := HH^.Gear^.Message and (not gmAttack); + HH^.Gear^.State:= HH^.Gear^.State and (not gstAttacking); + end; + PlaySound(sndDenied); + DeleteGear(gear); + exit + end; + cnt:= 0; + for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do + for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do + if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil) + and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0) + and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then + inc(cnt); + if cnt < 2 then + begin + if HH^.Gear <> nil then + begin + HH^.Gear^.Message := HH^.Gear^.Message and (not gmAttack); + HH^.Gear^.State:= HH^.Gear^.State and (not gstAttacking); + end; + PlaySound(sndDenied); + DeleteGear(gear); + exit + end; + Gear^.SoundChannel := LoopSound(sndTardis); + Gear^.doStep:= @doStepTardisWarp +end; + +//////////////////////////////////////////////////////////////////////////////// + +(* +WIP. The ice gun will have the following effects. It has been proposed by sheepluva that it take the appearance of a large freezer +spewing ice cubes. The cubes will be visual gears only. The scatter from them and the impact snow dust should help hide imprecisions in things like the GearsNear effect. +For now we assume a "ray" like a deagle projected out from the gun. +All these effects assume the ray's angle is not changed and that the target type was unchanged over a number of ticks. This is a simplifying assumption for "gun was applying freezing effect to the same target". + * When fired at water a layer of ice textured land is added above the water. + * When fired at non-ice land (land and lfLandMask and not lfIce) the land is overlaid with a thin layer of ice textured land around that point (say, 1 or 2px into land, 1px above). For attractiveness, a slope would probably be needed. + * When fired at a hog (land and $00FF <> 0), while the hog is targetted, the hog's state is set to frozen. As long as the gun is on the hog, a frozen hog sprite creeps up from the feet to the head. If the effect is interrupted before reaching the top, the freezing state is cleared. +A frozen hog will animate differently. To be decided, but possibly in a similar fashion to a grave when it comes to explosions. The hog might (possibly) not be damaged by explosions. This might make freezing potentially useful for friendlies in a bad position. It might be better to allow damage though. +A frozen hog stays frozen for a certain number of turns. Each turn the frozen overlay becomes fainter, until it fades and the hog animates normally again. +*) + + +procedure updateFuel(Gear: PGear); +var + t:LongInt; +begin + t:= Gear^.Health div 10; + if (t <> Gear^.Damage) and ((GameTicks and $3F) = 0) then + begin + Gear^.Damage:= t; + FreeTexture(Gear^.Tex); + Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(t) + + '%', cWhiteColor, fntSmall) + end; + if Gear^.Message and (gmUp or gmDown) <> 0 then + begin + StopSoundChan(Gear^.SoundChannel); + Gear^.SoundChannel:= -1; + if GameTicks mod 40 = 0 then dec(Gear^.Health) + end + else + begin + if Gear^.SoundChannel = -1 then + Gear^.SoundChannel := LoopSound(sndIceBeam); + if GameTicks mod 10 = 0 then dec(Gear^.Health) + end +end; + + +procedure updateTarget(Gear:PGear; newX, newY:HWFloat); +// var +// iter:PGear; +begin + with Gear^ do + begin + dX:= newX; + dY:= newY; + Pos:= 0; + Target.X:= NoPointX; + LastDamage:= nil; + X:= Hedgehog^.Gear^.X; + Y:= Hedgehog^.Gear^.Y; + end; +end; + +procedure doStepIceGun(Gear: PGear); +const iceWaitCollision = 0; +const iceCollideWithGround = 1; +//const iceWaitNextTarget:Longint = 2; +//const iceCollideWithHog:Longint = 4; +const iceCollideWithWater = 5; +//const waterFreezingTime:Longint = 500; +const groundFreezingTime = 1000; +const iceRadius = 32; +const iceHeight = 40; +var + HHGear, iter: PGear; + landRect: TSDL_Rect; + ndX, ndY: hwFloat; + i, t, gX, gY: LongInt; + hogs: PGearArrayS; + vg: PVisualGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + if (Gear^.Message and gmAttack <> 0) or (Gear^.Health = 0) or (HHGear = nil) or (HHGear^.Damage <> 0) or (HHGear^.dX.QWordValue > 4294967) then + begin + StopSoundChan(Gear^.SoundChannel); + DeleteGear(Gear); + AfterAttack; + exit + end; + updateFuel(Gear); + + with Gear^ do + begin + HedgehogChAngle(HHGear); + ndX:= SignAs(AngleSin(HHGear^.Angle), HHGear^.dX) * _4; + ndY:= -AngleCos(HHGear^.Angle) * _4; + if (ndX <> dX) or (ndY <> dY) or + ((Target.X <> NoPointX) and (Target.X and LAND_WIDTH_MASK = 0) and + (Target.Y and LAND_HEIGHT_MASK = 0) and ((Land[Target.Y, Target.X] = 0))) then + begin + updateTarget(Gear, ndX, ndY); + Timer := iceWaitCollision; + end + else + begin + X:= X + dX; + Y:= Y + dY; + gX:= hwRound(X); + gY:= hwRound(Y); + if Target.X = NoPointX then t:= hwRound(hwSqr(X-HHGear^.X)+hwSqr(Y-HHGear^.Y)); + + if Target.X <> NoPointX then + begin + CheckCollision(Gear); + if (State and gstCollision) <> 0 then + begin + if Timer = iceWaitCollision then + begin + Timer := iceCollideWithGround; + Power := GameTicks; + end + end + else if (target.y >= cWaterLine) then + begin + if Timer = iceWaitCollision then + begin + Timer := iceCollideWithWater; + Power := GameTicks; + end; + end; + + if (abs(gX-Target.X) < 2) and (abs(gY-Target.Y) < 2) then + begin + X:= HHGear^.X; + Y:= HHGear^.Y + end; + + if (Timer = iceCollideWithGround) and ((GameTicks - Power) > groundFreezingTime) then + begin + FillRoundInLand(target.x, target.y, iceRadius, icePixel); + landRect.x := min(max(target.x - iceRadius, 0), LAND_WIDTH - 1); + landRect.y := min(max(target.y - iceRadius, 0), LAND_HEIGHT - 1); + landRect.w := min(2*iceRadius, LAND_WIDTH - landRect.x - 1); + landRect.h := min(2*iceRadius, LAND_HEIGHT - landRect.y - 1); + UpdateLandTexture(landRect.x, landRect.w, landRect.y, landRect.h, true); + + // Freeze nearby mines/explosives/cases too + iter := GearsList; + while iter <> nil do + begin + if (iter^.State and gstFrozen = 0) and + ((iter^.Kind = gtExplosives) or (iter^.Kind = gtCase) or (iter^.Kind = gtMine)) and + (abs(iter^.X.Round-target.x)+abs(iter^.Y.Round-target.y)+2<2*iceRadius) and (Distance(iter^.X-int2hwFloat(target.x),iter^.Y-int2hwFloat(target.y)) nil then + begin + i:= random(100) + 155; + vg^.Tint:= (i shl 24) or (i shl 16) or ($FF shl 8) or (random(200) + 55); + vg^.Angle:= random(360); + vg^.dx:= 0.001 * random(80); + vg^.dy:= 0.001 * random(80) + end + end; + PlaySound(sndHogFreeze); + if iter^.Kind = gtMine then // dud mine block + begin + iter^.State:= iter^.State or gstFrozen; + vg:= AddVisualGear(hwRound(iter^.X) - 4 + Random(8), hwRound(iter^.Y) - 4 - Random(4), vgtSmoke); + if vg <> nil then + vg^.Scale:= 0.5; + PlaySound(sndVaporize); + iter^.Health := 0; + iter^.Damage := 0; + iter^.State := iter^.State and (not gstAttacking) + end + else if iter^.Kind = gtCase then + begin + DeleteCI(iter); + iter^.State:= iter^.State or gstFrozen; + AddGearCI(iter) + end + else // gtExplosives + begin + iter^.State:= iter^.State or gstFrozen; + iter^.Health:= iter^.Health + cBarrelHealth + end + end; + iter:= iter^.NextGear + end; + + // FillRoundInLandWithIce(Target.X, Target.Y, iceRadius); + SetAllHHToActive; + Timer := iceWaitCollision; + end; + + if (Timer = iceCollideWithWater) and ((GameTicks - Power) > groundFreezingTime) then + begin + PlaySound(sndHogFreeze); + DrawIceBreak(Target.X, cWaterLine - iceHeight, iceRadius, iceHeight); + SetAllHHToActive; + Timer := iceWaitCollision; + end; +(* + Any ideas for something that would look good here? + if (Target.X <> NoPointX) and ((Timer = iceCollideWithGround) or (Timer = iceCollideWithWater)) and (GameTicks mod max((groundFreezingTime-((GameTicks - Power)*2)),2) = 0) then //and CheckLandValue(Target.X, Target.Y, lfIce) then + begin + vg:= AddVisualGear(Target.X+random(20)-10, Target.Y+random(40)-10, vgtDust, 1); + if vg <> nil then + begin + i:= random(100) + 155; + vg^.Tint:= IceColor or $FF; + vg^.Angle:= random(360); + vg^.dx:= 0.001 * random(80); + vg^.dy:= 0.001 * random(80) + end + end; +*) + +// freeze nearby hogs + hogs := GearsNear(int2hwFloat(Target.X), int2hwFloat(Target.Y), gtHedgehog, Gear^.Radius*2); + if hogs.size > 0 then + for i:= 0 to hogs.size - 1 do + if hogs.ar^[i] <> HHGear then + if GameTicks mod 5 = 0 then + begin + hogs.ar^[i]^.Active:= true; + if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] < 256 then + hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] := hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] + 1 + else if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] = 256 then + begin + hogs.ar^[i]^.Hedgehog^.Effects[heFrozen]:= 200000-1;//cHedgehogTurnTime + cReadyDelay + PlaySound(sndHogFreeze); + end; + end; + inc(Pos) + end + else if (t > 400) and ((gY > cWaterLine) or + (((gX and LAND_WIDTH_MASK = 0) and (gY and LAND_HEIGHT_MASK = 0)) + and (Land[gY, gX] <> 0))) then + begin + Target.X:= gX; + Target.Y:= gY; + X:= HHGear^.X; + Y:= HHGear^.Y + end; + if (gX > max(LAND_WIDTH,4096)*2) or + (gX < -max(LAND_WIDTH,4096)) or + (gY < -max(LAND_HEIGHT,4096)) or + (gY > max(LAND_HEIGHT,4096)+512) then + begin + //X:= HHGear^.X; + //Y:= HHGear^.Y + Target.X:= gX; + Target.Y:= gY; + end + end + end; +end; + +procedure doStepAddAmmo(Gear: PGear); +var a: TAmmoType; + gi: PGear; +begin +if Gear^.Timer > 0 then dec(Gear^.Timer) +else + begin + if Gear^.Pos = posCaseUtility then + a:= GetUtility(Gear^.Hedgehog) + else + a:= GetAmmo(Gear^.Hedgehog); + CheckSum:= CheckSum xor GameTicks; + gi := GearsList; + while gi <> nil do + begin + with gi^ do CheckSum:= CheckSum xor X.round xor X.frac xor dX.round xor dX.frac xor Y.round xor Y.frac xor dY.round xor dY.frac; + AddRandomness(CheckSum); + if gi^.Kind = gtGenericFaller then gi^.State:= gi^.State and not gstTmpFlag; + gi := gi^.NextGear + end; + AddPickup(Gear^.Hedgehog^, a, Gear^.Power, hwRound(Gear^.X), hwRound(Gear^.Y)); + DeleteGear(Gear) + end; +end; + +procedure doStepGenericFaller(Gear: PGear); +begin +if Gear^.Timer < $FFFFFFFF then + if Gear^.Timer > 0 then + dec(Gear^.Timer) + else + begin + DeleteGear(Gear); + exit + end; +if (Gear^.State and gstTmpFlag <> 0) or (GameTicks and $7 = 0) then + begin + doStepFallingGear(Gear); + if (Gear^.State and gstInvisible <> 0) and (GameTicks and $FF = 0) and (hwRound(Gear^.X) < LongInt(leftX)) or (hwRound(Gear^.X) > LongInt(rightX)) or (hwRound(Gear^.Y) < LongInt(topY)) then + begin + Gear^.X:= int2hwFloat(GetRandom(rightX-leftX)+leftX); + Gear^.Y:= int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY); + Gear^.dX:= _90-(GetRandomf*_360); + Gear^.dY:= _90-(GetRandomf*_360) + end; + end +end; + +procedure doStepCreeper(Gear: PGear); +var hogs: PGearArrayS; + HHGear: PGear; + tdX: hwFloat; + dir: LongInt; +begin +doStepFallingGear(Gear); +if Gear^.Timer > 0 then dec(Gear^.Timer); +// creeper sleep phase +if (Gear^.Hedgehog = nil) and (Gear^.Timer > 0) then exit; + +if Gear^.Hedgehog <> nil then HHGear:= Gear^.Hedgehog^.Gear +else HHGear:= nil; + +// creeper boom phase +if (Gear^.State and gstTmpFlag <> 0) then + begin + if (Gear^.Timer = 0) then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 300, CurrentHedgehog, EXPLAutoSound); + DeleteGear(Gear) + end; + // ssssss he essssscaped + if (Gear^.Timer > 250) and ((HHGear = nil) or + (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) > 180) and + (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > _180))) then + begin + Gear^.State:= Gear^.State and (not gstTmpFlag); + Gear^.Timer:= 0 + end; + exit + end; + +// Search out a new target, as target seek time has expired, target is dead, target is out of range, or we did not have a target +if (HHGear = nil) or (Gear^.Timer = 0) or + (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) > Gear^.Angle) and + (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > int2hwFloat(Gear^.Angle))) + then + begin + hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Angle); + if hogs.size > 1 then + Gear^.Hedgehog:= hogs.ar^[GetRandom(hogs.size)]^.Hedgehog + else if hogs.size = 1 then Gear^.Hedgehog:= hogs.ar^[0]^.Hedgehog + else Gear^.Hedgehog:= nil; + if Gear^.Hedgehog <> nil then Gear^.Timer:= 5000; + exit + end; + +// we have a target. move the creeper. +if HHGear <> nil then + begin + // GOTCHA + if ((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) < 50) and + (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) < _50) then + begin + // hisssssssssss + Gear^.State:= Gear^.State or gstTmpFlag; + Gear^.Timer:= 1500; + exit + end; + if (Gear^.State and gstMoving <> 0) then + begin + Gear^.dY:= _0; + Gear^.dX:= _0; + end + else if (GameTicks and $FF = 0) then + begin + tdX:= HHGear^.X-Gear^.X; + dir:= hwSign(tdX); + if not TestCollisionX(Gear, dir) then + Gear^.X:= Gear^.X + signAs(_1,tdX); + if TestCollisionXwithXYShift(Gear, signAs(_10,tdX), 0, dir) then + begin + Gear^.dX:= SignAs(_0_15, tdX); + Gear^.dY:= -_0_3; + Gear^.State:= Gear^.State or gstMoving + end + end; + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepKnife(Gear: PGear); +//var ox, oy: LongInt; +// la: hwFloat; +var a: real; +begin + // Gear is shrunk so it can actually escape the hog without carving into the terrain + if (Gear^.Radius = 4) and (Gear^.CollisionMask = $FFFF) then Gear^.Radius:= 7; + if Gear^.Damage > 100 then Gear^.CollisionMask:= 0 + else if Gear^.Damage > 30 then + if GetRandom(max(4,18-Gear^.Damage div 10)) < 3 then Gear^.CollisionMask:= 0; + Gear^.Damage:= 0; + if Gear^.Timer > 0 then dec(Gear^.Timer); + if (Gear^.State and gstMoving <> 0) and (Gear^.State and gstCollision = 0) then + begin + DeleteCI(Gear); + Gear^.Radius:= 7; + // used for damage and impact calc. needs balancing I think + Gear^.Health:= hwRound(hwSqr((hwAbs(Gear^.dY)+hwAbs(Gear^.dX))*_4)); + doStepFallingGear(Gear); + AllInactive := false; + a:= Gear^.DirAngle; + CalcRotationDirAngle(Gear); + Gear^.DirAngle:= a+(Gear^.DirAngle-a)*2*hwSign(Gear^.dX) // double rotation + end + else if (Gear^.CollisionIndex = -1) and (Gear^.Timer = 0) then + begin + (*ox:= 0; oy:= 0; + if TestCollisionYwithGear(Gear, -1) <> 0 then oy:= -1; + if TestCollisionXwithGear(Gear, 1) then ox:= 1; + if TestCollisionXwithGear(Gear, -1) then ox:= -1; + if TestCollisionYwithGear(Gear, 1) <> 0 then oy:= 1; + if Gear^.Health > 0 then + PlaySound(sndRopeAttach); + + la:= _10000; + if (ox <> 0) or (oy <> 0) then + la:= CalcSlopeNearGear(Gear, ox, oy); + if la = _10000 then + begin + // debug for when we couldn't get an angle + //AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite); +*) + Gear^.DirAngle:= DxDy2Angle(Gear^.dX, Gear^.dY) + (random(30)-15); + if (Gear^.dX.isNegative and Gear^.dY.isNegative) or + ((not Gear^.dX.isNegative) and (not Gear^.dY.isNegative)) then Gear^.DirAngle:= Gear^.DirAngle-90; + // end + // else Gear^.DirAngle:= hwFloat2Float(la)*90; // sheepluva's comment claims 45deg = 0.5 - yet orientation doesn't seem consistent? + // AddFileLog('la: '+floattostr(la)+' DirAngle: '+inttostr(round(Gear^.DirAngle))); + Gear^.dX:= _0; + Gear^.dY:= _0; + Gear^.State:= Gear^.State and (not gstMoving) or gstCollision; + Gear^.Radius:= 16; + if Gear^.Health > 0 then AmmoShove(Gear, Gear^.Health, 0); + Gear^.Health:= 0; + Gear^.Timer:= 500; + AddGearCI(Gear) + end + else if GameTicks and $3F = 0 then + begin + if (TestCollisionYwithGear(Gear, -1) = 0) + and (not TestCollisionXwithGear(Gear, 1)) + and (not TestCollisionXwithGear(Gear, -1)) + and (TestCollisionYwithGear(Gear, 1) = 0) then Gear^.State:= Gear^.State and (not gstCollision) or gstMoving; + end +end; +(* + This didn't end up getting used, but, who knows, might be reasonable for javellin or something +// Make the knife initial angle based on the hog attack angle, or is that too hard? +procedure doStepKnife(Gear: PGear); +var t, + gx, gy, ga, // gear x,y,angle + lx, ly, la, // land x,y,angle + ox, oy, // x,y offset + w, h, // wXh of clip area + tx, ty // tip position in sprite + : LongInt; + surf: PSDL_Surface; + s: hwFloat; + +begin + Gear^.dY := Gear^.dY + cGravity; + if (GameFlags and gfMoreWind) <> 0 then + Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density; + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + CheckGearDrowning(Gear); + gx:= hwRound(Gear^.X); + gy:= hwRound(Gear^.Y); + if Gear^.State and gstDrowning <> 0 then exit; + with Gear^ do + begin + if CheckLandValue(gx, gy, lfLandMask) then + begin + t:= Angle + hwRound((hwAbs(dX)+hwAbs(dY)) * _10); + + if t < 0 then inc(t, 4096) + else if 4095 < t then dec(t, 4096); + Angle:= t; + + DirAngle:= Angle / 4096 * 360 + end + else + begin +//This is the set of postions for the knife. +//Using FlipSurface and copyToXY the knife can be written to the LandPixels at 32 positions, and an appropriate line drawn in Land. + t:= Angle mod 1024; + case t div 128 of + 0: begin + ox:= 2; oy:= 5; + w := 25; h:= 5; + tx:= 0; ty:= 2 + end; + 1: begin + ox:= 2; oy:= 15; + w:= 24; h:= 8; + tx:= 0; ty:= 7 + end; + 2: begin + ox:= 2; oy:= 27; + w:= 23; h:= 12; + tx:= -12; ty:= -5 + end; + 3: begin + ox:= 2; oy:= 43; + w:= 21; h:= 15; + tx:= 0; ty:= 14 + end; + 4: begin + ox:= 29; oy:= 8; + w:= 19; h:= 19; + tx:= 0; ty:= 17 + end; + 5: begin + ox:= 29; oy:= 32; + w:= 15; h:= 21; + tx:= 0; ty:= 20 + end; + 6: begin + ox:= 51; oy:= 3; + w:= 11; h:= 23; + tx:= 0; ty:= 22 + end; + 7: begin + ox:= 51; oy:= 34; + w:= 7; h:= 24; + tx:= 0; ty:= 23 + end + end; + + surf:= SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, RMask, GMask, BMask, AMask); + copyToXYFromRect(SpritesData[sprKnife].Surface, surf, ox, oy, w, h, 0, 0); + // try to make the knife hit point first + lx := 0; + ly := 0; + if CalcSlopeTangent(Gear, gx, gy, lx, ly, 255) then + begin + la:= vector2Angle(int2hwFloat(lx), int2hwFloat(ly)); + ga:= vector2Angle(dX, dY); + AddFileLog('la: '+inttostr(la)+' ga: '+inttostr(ga)+' Angle: '+inttostr(Angle)); + // change to 0 to 4096 forced by LongWord in Gear + if la < 0 then la:= 4096+la; + if ga < 0 then ga:= 4096+ga; + if ((Angle > ga) and (Angle < la)) or ((Angle < ga) and (Angle > la)) then + begin + if Angle >= 2048 then dec(Angle, 2048) + else if Angle < 2048 then inc(Angle, 2048) + end; + AddFileLog('la: '+inttostr(la)+' ga: '+inttostr(ga)+' Angle: '+inttostr(Angle)) + end; + case Angle div 1024 of + 0: begin + flipSurface(surf, true); + flipSurface(surf, true); + BlitImageAndGenerateCollisionInfo(gx-(w-tx), gy-(h-ty), w, surf) + end; + 1: begin + flipSurface(surf, false); + BlitImageAndGenerateCollisionInfo(gx-(w-tx), gy-ty, w, surf) + end; + 2: begin // knife was actually drawn facing this way... + BlitImageAndGenerateCollisionInfo(gx-tx, gy-ty, w, surf) + end; + 3: begin + flipSurface(surf, true); + BlitImageAndGenerateCollisionInfo(gx-tx, gy-(h-ty), w, surf) + end + end; + SDL_FreeSurface(surf); + // this needs to calculate actual width/height + land clipping since update texture doesn't. + // i.e. this will crash if you fire near sides of map, but until I get the blit right, not going to put real values + UpdateLandTexture(hwRound(X)-32, 64, hwRound(Y)-32, 64, true); + DeleteGear(Gear); + exit + end + end; +end; +*) + +end. diff -r 76e68c136a11 -r 8e8b908970c2 hedgewars/uGearsHedgehog.pas --- a/hedgewars/uGearsHedgehog.pas Thu Jun 27 14:37:03 2013 +0400 +++ b/hedgewars/uGearsHedgehog.pas Thu Jun 27 15:33:02 2013 +0400 @@ -33,8 +33,8 @@ implementation uses uConsts, uVariables, uFloat, uAmmos, uSound, uCaptions, - uCommands, uLocale, uUtils, uVisualGears, uStats, uIO, uScript, - uGearsList, uGears, uCollisions, uRandom, uStore, uTeams, + uCommands, uLocale, uUtils, uStats, uIO, uScript, + uGearsList, uCollisions, uRandom, uStore, uTeams, uGearsUtils, uVisualGearsList; var GHStepTicks: LongWord = 0; diff -r 76e68c136a11 -r 8e8b908970c2 hedgewars/uGearsRender.pas --- a/hedgewars/uGearsRender.pas Thu Jun 27 14:37:03 2013 +0400 +++ b/hedgewars/uGearsRender.pas Thu Jun 27 15:33:02 2013 +0400 @@ -38,7 +38,7 @@ end; implementation -uses uRender, uUtils, uVariables, uAmmos, Math, uVisualGears, uVisualGearsList; +uses uRender, uUtils, uVariables, uAmmos, Math, uVisualGearsList; procedure DrawRopeLinesRQ(Gear: PGear); begin diff -r 76e68c136a11 -r 8e8b908970c2 hedgewars/uGearsUtils.pas --- a/hedgewars/uGearsUtils.pas Thu Jun 27 14:37:03 2013 +0400 +++ b/hedgewars/uGearsUtils.pas Thu Jun 27 15:33:02 2013 +0400 @@ -20,7 +20,7 @@ unit uGearsUtils; interface -uses uTypes; +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); @@ -41,16 +41,31 @@ procedure CheckCollision(Gear: PGear); inline; procedure CheckCollisionWithLand(Gear: PGear); inline; +procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); +function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): PGearArrayS; +procedure SpawnBoxOfSmth; +procedure ShotgunShot(Gear: PGear); + +procedure SetAllToActive; +procedure SetAllHHToActive; inline; +procedure SetAllHHToActive(Ice: boolean); + +function GetAmmo(Hedgehog: PHedgehog): TAmmoType; +function GetUtility(Hedgehog: PHedgehog): TAmmoType; + + + function MakeHedgehogsStep(Gear: PGear) : boolean; var doStepHandlers: array[TGearType] of TGearStepProcedure; implementation -uses uFloat, uSound, uCollisions, uUtils, uConsts, uVisualGears, uAIMisc, +uses uSound, uCollisions, uUtils, uConsts, uVisualGears, uAIMisc, uVariables, uLandGraphics, uScript, uStats, uCaptions, uTeams, uStore, - uLocale, uTextures, uRenderUtils, uRandom, SDLh, uDebug, uGears, - uGearsList, Math, uVisualGearsList; + uLocale, uTextures, uRenderUtils, uRandom, SDLh, uDebug, + uGearsList, Math, uVisualGearsList, uGearsHandlersMess, + uGearsHedgehog; procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); inline; begin @@ -766,4 +781,422 @@ end; end; + +procedure ShotgunShot(Gear: PGear); +var t: PGear; + dmg, r, dist: LongInt; + dx, dy: hwFloat; +begin +Gear^.Radius:= cShotgunRadius; +t:= GearsList; +while t <> nil do + begin + case t^.Kind of + gtHedgehog, + gtMine, + gtSMine, + gtKnife, + gtCase, + gtTarget, + gtExplosives: begin//, +// gtStructure: begin +//addFileLog('ShotgunShot radius: ' + inttostr(Gear^.Radius) + ', t^.Radius = ' + inttostr(t^.Radius) + ', distance = ' + inttostr(dist) + ', dmg = ' + inttostr(dmg)); + dmg:= 0; + r:= Gear^.Radius + t^.Radius; + dx:= Gear^.X-t^.X; + dx.isNegative:= false; + dy:= Gear^.Y-t^.Y; + dy.isNegative:= false; + if r-hwRound(dx+dy) > 0 then + begin + dist:= hwRound(Distance(dx, dy)); + dmg:= ModifyDamage(min(r - dist, 25), t); + end; + if dmg > 0 then + begin + if (not t^.Invulnerable) then + ApplyDamage(t, Gear^.Hedgehog, dmg, dsBullet) + else + Gear^.State:= Gear^.State or gstWinner; + + DeleteCI(t); + t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX); + t^.dY:= t^.dY + Gear^.dY * dmg * _0_01; + t^.State:= t^.State or gstMoving; + if t^.Kind = gtKnife then t^.State:= t^.State and (not gstCollision); + t^.Active:= true; + FollowGear:= t + end + end; + gtGrave: begin + dmg:= 0; + r:= Gear^.Radius + t^.Radius; + dx:= Gear^.X-t^.X; + dx.isNegative:= false; + dy:= Gear^.Y-t^.Y; + dy.isNegative:= false; + if r-hwRound(dx+dy) > 0 then + begin + dist:= hwRound(Distance(dx, dy)); + dmg:= ModifyDamage(min(r - dist, 25), t); + end; + if dmg > 0 then + begin + t^.dY:= - _0_1; + t^.Active:= true + end + end; + end; + t:= t^.NextGear + end; +if (GameFlags and gfSolidLand) = 0 then + DrawExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cShotgunRadius) +end; + +procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); +var t: PGearArray; + Gear: PGear; + i, j, tmpDmg: LongInt; + VGear: PVisualGear; +begin +t:= CheckGearsCollision(Ammo); +// Just to avoid hogs on rope dodging fire. +if (CurAmmoGear <> nil) and ((CurAmmoGear^.Kind = gtRope) or (CurAmmoGear^.Kind = gtJetpack) or (CurAmmoGear^.Kind = gtBirdy)) +and (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.CollisionIndex = -1) +and (sqr(hwRound(Ammo^.X) - hwRound(CurrentHedgehog^.Gear^.X)) + sqr(hwRound(Ammo^.Y) - hwRound(CurrentHedgehog^.Gear^.Y)) <= sqr(cHHRadius + Ammo^.Radius)) then + begin + t^.ar[t^.Count]:= CurrentHedgehog^.Gear; + inc(t^.Count) + end; + +i:= t^.Count; + +if (Ammo^.Kind = gtFlame) and (i > 0) then + Ammo^.Health:= 0; +while i > 0 do + begin + dec(i); + Gear:= t^.ar[i]; + 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 + begin + VGear := AddVisualGear(hwround(Ammo^.X), hwround(Ammo^.Y), vgtBulletHit); + if VGear <> nil then + VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY); + end; + + if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then + Gear^.FlightTime:= 1; + + + case Gear^.Kind of + gtHedgehog, + gtMine, + gtSMine, + gtKnife, + gtTarget, + gtCase, + gtExplosives: //, + //gtStructure: + begin + if (Ammo^.Kind = gtDrill) then + begin + Ammo^.Timer:= 0; + exit; + end; + if (not Gear^.Invulnerable) then + begin + if (Ammo^.Kind = gtKnife) and (tmpDmg > 0) then + for j:= 1 to max(1,min(3,tmpDmg div 5)) do + begin + VGear:= AddVisualGear(hwRound(Ammo^.X-((Ammo^.X-Gear^.X)/_2)), hwRound(Ammo^.Y-((Ammo^.Y-Gear^.Y)/_2)), vgtStraightShot); + if VGear <> nil then + with VGear^ do + begin + Tint:= $FFCC00FF; + Angle:= random(360); + dx:= 0.0005 * (random(100)); + dy:= 0.0005 * (random(100)); + if random(2) = 0 then + dx := -dx; + if random(2) = 0 then + dy := -dy; + FrameTicks:= 600+random(200); + State:= ord(sprStar) + end + end; + ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg, dsShove) + end + else + Gear^.State:= Gear^.State or gstWinner; + 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); + ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg * 100, dsUnknown); // crank up damage for explosives + blowtorch + end; + + if (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.King or (Gear^.Hedgehog^.Effects[heFrozen] > 0)) then + begin + Gear^.dX:= Ammo^.dX * Power * _0_005; + Gear^.dY:= Ammo^.dY * Power * _0_005 + end + else if ((Ammo^.Kind <> gtFlame) or (Gear^.Kind = gtHedgehog)) and (Power <> 0) then + begin + Gear^.dX:= Ammo^.dX * Power * _0_01; + Gear^.dY:= Ammo^.dY * Power * _0_01 + end; + + if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then + begin + Gear^.Active:= true; + DeleteCI(Gear); + Gear^.State:= Gear^.State or gstMoving; + if Gear^.Kind = gtKnife then Gear^.State:= Gear^.State and (not gstCollision); + // move the gear upwards a bit to throw it over tiny obstacles at start + if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then + begin + if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX)) + or (TestCollisionYwithGear(Gear, -1) <> 0)) then + Gear^.Y:= Gear^.Y - _1; + if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) + or (TestCollisionYwithGear(Gear, -1) <> 0)) then + Gear^.Y:= Gear^.Y - _1; + if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) + or (TestCollisionYwithGear(Gear, -1) <> 0)) then + Gear^.Y:= Gear^.Y - _1; + end + end; + + + if (Ammo^.Kind <> gtFlame) or ((Ammo^.State and gsttmpFlag) = 0) then + FollowGear:= Gear + end; + end + end; + end; +if i <> 0 then + SetAllToActive +end; + + +function CountGears(Kind: TGearType): Longword; +var t: PGear; + count: Longword = 0; +begin + +t:= GearsList; +while t <> nil do + begin + if t^.Kind = Kind then + inc(count); + t:= t^.NextGear + end; +CountGears:= count; +end; + +procedure SetAllToActive; +var t: PGear; +begin +AllInactive:= false; +t:= GearsList; +while t <> nil do + begin + t^.Active:= true; + t:= t^.NextGear + end +end; + +procedure SetAllHHToActive; inline; +begin +SetAllHHToActive(true) +end; + + +procedure SetAllHHToActive(Ice: boolean); +var t: PGear; +begin +AllInactive:= false; +t:= GearsList; +while t <> nil do + begin + if (t^.Kind = gtHedgehog) or (t^.Kind = gtExplosives) then + begin + if (t^.Kind = gtHedgehog) and Ice then CheckIce(t); + t^.Active:= true + end; + t:= t^.NextGear + end +end; + + +var GearsNearArray : TPGearArray; +function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): PGearArrayS; +var + t: PGear; + s: Longword; +begin + r:= r*r; + s:= 0; + SetLength(GearsNearArray, s); + t := GearsList; + while t <> nil do + begin + if (t^.Kind = Kind) + and ((X - t^.X)*(X - t^.X) + (Y - t^.Y)*(Y-t^.Y) < int2hwFloat(r)) then + begin + inc(s); + SetLength(GearsNearArray, s); + GearsNearArray[s - 1] := t; + end; + t := t^.NextGear; + end; + + GearsNear.size:= s; + GearsNear.ar:= @GearsNearArray +end; + + +procedure SpawnBoxOfSmth; +var t, aTot, uTot, a, h: LongInt; + i: TAmmoType; +begin +if (PlacingHogs) or + (cCaseFactor = 0) + or (CountGears(gtCase) >= 5) + or (GetRandom(cCaseFactor) <> 0) then + exit; + +FollowGear:= nil; +aTot:= 0; +uTot:= 0; +for i:= Low(TAmmoType) to High(TAmmoType) do + if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then + inc(aTot, Ammoz[i].Probability) + else + inc(uTot, Ammoz[i].Probability); + +t:=0; +a:=aTot; +h:= 1; + +if (aTot+uTot) <> 0 then + if ((GameFlags and gfInvulnerable) = 0) then + begin + h:= cHealthCaseProb * 100; + t:= GetRandom(10000); + a:= (10000-h)*aTot div (aTot+uTot) + end + else + begin + t:= GetRandom(aTot+uTot); + h:= 0 + end; + + +if t 0) then + begin + FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); + t:= GetRandom(t); + i:= Low(TAmmoType); + FollowGear^.Pos:= posCaseAmmo; + FollowGear^.AmmoType:= i; + AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo); + end + end +else + begin + t:= uTot; + if (t > 0) then + begin + FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); + t:= GetRandom(t); + i:= Low(TAmmoType); + FollowGear^.Pos:= posCaseUtility; + FollowGear^.AmmoType:= i; + AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo); + end + end; + +// handles case of no ammo or utility crates - considered also placing booleans in uAmmos and altering probabilities +if (FollowGear <> nil) then + begin + FindPlace(FollowGear, true, 0, LAND_WIDTH); + + if (FollowGear <> nil) then + AddVoice(sndReinforce, CurrentTeam^.voicepack) + end +end; + + +function GetAmmo(Hedgehog: PHedgehog): TAmmoType; +var t, aTot: LongInt; + i: TAmmoType; +begin +Hedgehog:= Hedgehog; // avoid hint + +aTot:= 0; +for i:= Low(TAmmoType) to High(TAmmoType) do + if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then + inc(aTot, Ammoz[i].Probability); + +t:= aTot; +i:= Low(TAmmoType); +if (t > 0) then + begin + t:= GetRandom(t); + while t >= 0 do + begin + inc(i); + if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then + dec(t, Ammoz[i].Probability) + end + end; +GetAmmo:= i +end; + +function GetUtility(Hedgehog: PHedgehog): TAmmoType; +var t, uTot: LongInt; + i: TAmmoType; +begin + +uTot:= 0; +for i:= Low(TAmmoType) to High(TAmmoType) do + if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) + and ((Hedgehog^.Team^.HedgehogsNumber > 1) or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then + inc(uTot, Ammoz[i].Probability); + +t:= uTot; +i:= Low(TAmmoType); +if (t > 0) then + begin + t:= GetRandom(t); + while t >= 0 do + begin + inc(i); + if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) and ((Hedgehog^.Team^.HedgehogsNumber > 1) + or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then + dec(t, Ammoz[i].Probability) + end + end; +GetUtility:= i +end; + + end. diff -r 76e68c136a11 -r 8e8b908970c2 hedgewars/uScript.pas --- a/hedgewars/uScript.pas Thu Jun 27 14:37:03 2013 +0400 +++ b/hedgewars/uScript.pas Thu Jun 27 15:33:02 2013 +0400 @@ -58,7 +58,6 @@ uses LuaPas, uConsole, uConsts, - uVisualGears, uGears, uGearsList, uGearsUtils, @@ -85,6 +84,7 @@ SysUtils, uIO, uVisualGearsList, + uGearsHandlersMess, uPhysFSLayer, typinfo ; diff -r 76e68c136a11 -r 8e8b908970c2 hedgewars/uTeams.pas --- a/hedgewars/uTeams.pas Thu Jun 27 14:37:03 2013 +0400 +++ b/hedgewars/uTeams.pas Thu Jun 27 15:33:02 2013 +0400 @@ -1,4 +1,4 @@ -(* + (* * Hedgewars, a free turn based strategy game * Copyright (c) 2004-2013 Andrey Korotaev * @@ -20,7 +20,7 @@ unit uTeams; interface -uses uConsts, uInputHandler, uRandom, uFloat, uStats, uVisualGears, +uses uConsts, uInputHandler, uRandom, uFloat, uStats, uCollisions, GLunit, uSound, uStore, uTypes, uScript {$IFDEF USE_TOUCH_INTERFACE}, uWorld{$ENDIF}; diff -r 76e68c136a11 -r 8e8b908970c2 hedgewars/uVisualGears.pas --- a/hedgewars/uVisualGears.pas Thu Jun 27 14:37:03 2013 +0400 +++ b/hedgewars/uVisualGears.pas Thu Jun 27 15:33:02 2013 +0400 @@ -29,7 +29,7 @@ * E.g.: background flakes, visual effects: explosion, smoke trails, etc. *) interface -uses uConsts, uFloat, GLunit, uTypes, uWorld; +uses uConsts, GLunit, uTypes; procedure initModule; procedure freeModule; @@ -47,8 +47,8 @@ procedure KickFlakes(Radius, X, Y: LongInt); implementation -uses uSound, uVariables, uTextures, uRender, Math, uRenderUtils, uStore, uUtils - , uVisualGearsHandlers, uVisualGearsList; +uses uVariables, uRender, Math, uRenderUtils, uStore, uUtils + , uVisualGearsList; procedure AddDamageTag(X, Y, Damage, Color: LongWord); var s: shortstring;