diff -r 92af50454cf2 -r 8054d9d775fd hedgewars/GSHandlers.inc --- a/hedgewars/GSHandlers.inc Fri Oct 11 11:55:31 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5646 +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. -{$IFNDEF WEBGL} - if Gear^.dX.Round > 2 then - Gear^.dX.QWordValue:= 8589934592; - if Gear^.dY.Round > 2 then - Gear^.dY.QWordValue:= 8589934592; -{$ELSE} - if Gear^.dX.Round > 2 then - begin - Gear^.dX.Round:= 2; - Gear^.dX.Frac:= 0 - end; - if Gear^.dY.QWordValue > 2 then - begin - Gear^.dY.Round:= 2; - Gear^.dY.Frac:= 0 - end; -{$ENDIF} - 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: LongInt; - 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(true); - 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 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, 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); - {$IFNDEF PAS2C} - with mobileRecord do - if (performRumble <> nil) and (not fastUntilLag) then - performRumble(kSystemSoundID_Vibrate); - {$ENDIF} - 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(true); - 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; - {$IFNDEF PAS2C} - Gear^.dX := Gear^.dX; - {$ENDIF} - 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; - {$IFNDEF PAS2C} - with mobileRecord do - if (performRumble <> nil) and (not fastUntilLag) then - performRumble(kSystemSoundID_Vibrate); - {$ENDIF} -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 - FillRoundInLand2(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 - (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); - iter^.State:= iter^.State or gstFrozen; - if iter^.Kind = gtMine then // dud mine block - begin - 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); - AddGearCI(iter) - end - else // gtExplosives - iter^.Health:= iter^.Health + cBarrelHealth - end; - iter:= iter^.NextGear - end; - - // FillRoundInLandWithIce(Target.X, Target.Y, iceRadius); - SetAllHHToActive(true); - Timer := iceWaitCollision; - end; - - if (Timer = iceCollideWithWater) and ((GameTicks - Power) > groundFreezingTime) then - begin - PlaySound(sndHogFreeze); - DrawIceBreak(Target.X, cWaterLine - iceHeight, iceRadius, iceHeight); - SetAllHHToActive(true); - 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 - (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; -*)