hedgewars/uGearsRender.pas
author Wuzzy <Wuzzy2@mail.ru>
Thu, 25 Apr 2019 23:01:05 +0200
changeset 14844 e239378a9400
parent 14783 bbec6b28d072
child 14924 e28ff0f1aca2
permissions -rw-r--r--
Prevent entering “/”, “\” and “:” in team and scheme names. The name of teams and schems is saved in the file name itself, so these characters would cause trouble as they are used in path names in Linux and Windows.

(*
 * Hedgewars, a free turn based strategy game
 * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *)

{$INCLUDE "options.inc"}

unit uGearsRender;

interface
uses uTypes, uConsts, GLunit, uFloat, SDLh;

type
   Tar = record
            X, Y: hwFloat;
            dLen: hwFloat;
            b : boolean;
         end;
   TRopePoints = record
            Count     : Longword;
            HookAngle : GLfloat;
            ar        : array[0..MAXROPEPOINTS] of Tar;
            rounded   : array[0..MAXROPEPOINTS + 2] of TVertex2f;
         end;
procedure RenderGear(Gear: PGear; x, y: LongInt);
procedure RenderGearTimer(Gear: PGear; x, y: LongInt);
procedure RenderGearHealth(Gear: PGear; x, y: LongInt);
procedure RenderHHGuiExtras(Gear: PGear; ox, oy: LongInt);
procedure DrawHHOrder();

var RopePoints: record
                Count: Longword;
                HookAngle: GLfloat;
                ar: array[0..MAXROPEPOINTS] of record
                                X, Y: hwFloat;
                                dLen: hwFloat;
                                b: boolean;
                                sx, sy, sb: boolean;
                                end;
                rounded: array[0..MAXROPEPOINTS + 2] of TVertex2f;
                end;

implementation
uses uRender, uRenderUtils, uUtils, uVariables, uAmmos, Math, uVisualGearsList;

procedure DrawRopeLinesRQ(Gear: PGear);
var n: LongInt;
begin
with RopePoints do
    begin
    rounded[Count].X:= hwRound(Gear^.X);
    rounded[Count].Y:= hwRound(Gear^.Y);
    rounded[Count + 1].X:= hwRound(Gear^.Hedgehog^.Gear^.X);
    rounded[Count + 1].Y:= hwRound(Gear^.Hedgehog^.Gear^.Y);
    end;

if (RopePoints.Count > 0) or (Gear^.Elasticity.QWordValue > 0) then
    begin
    EnableTexture(false);
    
    Tint(Gear^.Tint shr 24 div 3, Gear^.Tint shr 16 and $FF div 3, Gear^.Tint shr 8 and $FF div 3, Gear^.Tint and $FF);

    n:= RopePoints.Count + 2;

    SetVertexPointer(@RopePoints.rounded[0], n);

    openglPushMatrix();
    openglTranslatef(WorldDx, WorldDy, 0);

    glLineWidth(3.0 * cScaleFactor);
    glDrawArrays(GL_LINE_STRIP, 0, n);
    Tint(Gear^.Tint);
    glLineWidth(2.0 * cScaleFactor);
    glDrawArrays(GL_LINE_STRIP, 0, n);

    untint;

    openglPopMatrix();

    EnableTexture(true);
    end
end;


function DrawRopeLine(X1, Y1, X2, Y2, roplen: LongInt): LongInt;
var  eX, eY, dX, dY: LongInt;
    i, sX, sY, x, y, d: LongInt;
    b: boolean;
begin
    if (X1 = X2) and (Y1 = Y2) then
        begin
        //OutError('WARNING: zero length rope line!', false);
        DrawRopeLine:= 0;
        exit
        end;
    eX:= 0;
    eY:= 0;
    dX:= X2 - X1;
    dY:= Y2 - Y1;

    if (dX > 0) then
        sX:= 1
    else
        if (dX < 0) then
            begin
            sX:= -1;
            dX:= -dX
            end
        else sX:= dX;

    if (dY > 0) then
        sY:= 1
    else
        if (dY < 0) then
            begin
            sY:= -1;
            dY:= -dY
            end
        else
            sY:= dY;

    if (dX > dY) then
        d:= dX
    else
        d:= dY;

    x:= X1;
    y:= Y1;

    for i:= 0 to d do
        begin
        inc(eX, dX);
        inc(eY, dY);
        b:= false;
        if (eX > d) then
            begin
            dec(eX, d);
            inc(x, sX);
            b:= true
            end;
        if (eY > d) then
            begin
            dec(eY, d);
            inc(y, sY);
            b:= true
            end;
        if b then
            begin
            inc(roplen);
            if (roplen mod 4) = 0 then
                DrawSprite(sprRopeNode, x - 2, y - 2, 0)
            end
    end;
    DrawRopeLine:= roplen;
end;

procedure DrawRope(Gear: PGear);
var roplen, i: LongInt;
begin
    if Gear^.Hedgehog^.Gear = nil then exit;
    if (Gear^.Tag = 1) or ((cReducedQuality and rqSimpleRope) <> 0) then
        DrawRopeLinesRQ(Gear)
    else
        begin
        roplen:= 0;
        if RopePoints.Count > 0 then
            begin
            i:= 0;
            while i < Pred(RopePoints.Count) do
                    begin
                    roplen:= DrawRopeLine(hwRound(RopePoints.ar[i].X) + WorldDx, hwRound(RopePoints.ar[i].Y) + WorldDy,
                                hwRound(RopePoints.ar[Succ(i)].X) + WorldDx, hwRound(RopePoints.ar[Succ(i)].Y) + WorldDy, roplen);
                    inc(i)
                    end;
            roplen:= DrawRopeLine(hwRound(RopePoints.ar[i].X) + WorldDx, hwRound(RopePoints.ar[i].Y) + WorldDy,
                        hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, roplen);
            roplen:= DrawRopeLine(hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy,
                        hwRound(Gear^.Hedgehog^.Gear^.X) + WorldDx, hwRound(Gear^.Hedgehog^.Gear^.Y) + WorldDy, roplen);
            end
        else
            if Gear^.Elasticity.QWordValue > 0 then
            roplen:= DrawRopeLine(hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy,
                        hwRound(Gear^.Hedgehog^.Gear^.X) + WorldDx, hwRound(Gear^.Hedgehog^.Gear^.Y) + WorldDy, roplen);
        end;


if RopePoints.Count > 0 then
    DrawSpriteRotated(sprRopeHook, hwRound(RopePoints.ar[0].X) + WorldDx, hwRound(RopePoints.ar[0].Y) + WorldDy, 1, RopePoints.HookAngle)
else
    if Gear^.Elasticity.QWordValue > 0 then
        DrawSpriteRotated(sprRopeHook, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
end;


procedure DrawAltWeapon(Gear: PGear; sx, sy: LongInt);
begin
with Gear^.Hedgehog^ do
    begin
    if not (((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) <> 0) and ((Gear^.State and gstAttacked) = 0)) then
        exit;
    DrawTexture(sx + 16, sy + 16, ropeIconTex);
    DrawTextureF(SpritesData[sprAMAmmos].Texture, 0.75, sx + 30, sy + 30, ord(CurAmmoType) - 1, 1, 32, 32);
    end;
end;

procedure DrawHHOrder();
var HHGear: PGear;
    hh: PHedgehog;
    c, i, t, x, y, sprH, sprW, fSprOff: LongInt;
begin
t:= LocalTeam;

if not CurrentTeam^.ExtDriven then
    for i:= 0 to Pred(TeamsCount) do
        if (TeamsArray[i] = CurrentTeam) then
            t:= i;

if t < 0 then
    exit;

if TeamsArray[t] <> nil then
    begin
    sprH:= SpritesData[sprBigDigit].Height;
    sprW:= SpritesData[sprBigDigit].Width;
    fSprOff:= sprW div 4 + SpritesData[sprFrame].Width div 4 - 1; // - 1 for overlap to avoid artifacts
    i:= 0;
    c:= 0;
        repeat
        hh:= @TeamsArray[t]^.Hedgehogs[i];
        inc(i);
        if (hh <> nil) and (hh^.Gear <> nil) and (not hh^.Unplaced) then
            begin
            inc(c);
            HHGear:= hh^.Gear;
            x:= hwRound(HHGear^.X) + WorldDx;
            y:= hwRound(HHGear^.Y) + WorldDy - 2;
            DrawTextureF(SpritesData[sprFrame].Texture, 0.5, x - fSprOff, y, 0, 1, SpritesData[sprFrame].Width, SpritesData[sprFrame].Height);
            DrawTextureF(SpritesData[sprFrame].Texture, 0.5, x + fSprOff, y, 1, 1, SpritesData[sprFrame].Width, SpritesData[sprFrame].Height);
            DrawTextureF(SpritesData[sprBigDigit].Texture, 0.5, x, y, c, 1, sprW, sprH);
            if SpeechHogNumber = c then
                DrawCircle(x, y, 20, 3, 0, $FF, $FF, $80);
            end;
        until (i > cMaxHHIndex);
    end

end;

// Render some informational GUI next to hedgehog, like fuel and alternate weapon
procedure RenderHHGuiExtras(Gear: PGear; ox, oy: LongInt);
var HH: PHedgehog;
    sx, sy, tx, ty, t, sign, m: LongInt;
    dAngle: real;
begin
    HH:= Gear^.Hedgehog;
    sx:= ox + 1; // this offset is very common
    sy:= oy - 3;
    if HH^.Unplaced then
        exit;
    if (Gear^.State and gstHHDeath) <> 0 then
        exit;
    if (Gear^.State and gstHHGone) <> 0 then
        exit;
    if (CinematicScript) then
        exit;

    // render finger (pointing arrow)
    if bShowFinger and ((Gear^.State and gstHHDriven) <> 0) then
        begin
        ty := oy - 32;
        // move finger higher up if tags are above hog
        if (cTagsMask and htTeamName) <> 0 then
            ty := ty - HH^.Team^.NameTagTex^.h - 2;
        if (cTagsMask and htName) <> 0 then
            ty := ty - HH^.NameTagTex^.h - 2;
        if (cTagsMask and htHealth) <> 0 then
            ty := ty - HH^.HealthTagTex^.h - 2;
        tx := ox;

        // don't go offscreen
        t:= 32;
        tx := min(max(tx, ViewLeftX + t), ViewRightX - t);
        ty := min(ty, ViewBottomY - 96);
        // don't overlap with HH or HH tags
        if ty < ViewTopY + t then
            begin
            if abs(tx - ox) < abs(ty - oy)  then
                ty:= max(ViewTopY + t, oy + t)
            else
                ty:= max(ViewTopY + t, ty);
            end;

        dAngle := DxDy2Angle(int2hwfloat(ty - oy), int2hwfloat(tx - ox)) + 90;

        if (IsTooDarkToRead(HH^.Team^.Clan^.Color)) then
            DrawSpriteRotatedF(sprFingerBackInv, tx, ty, RealTicks div 32 mod 16, 1, dAngle)
        else
            DrawSpriteRotatedF(sprFingerBack, tx, ty, RealTicks div 32 mod 16, 1, dAngle);
        Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
        DrawSpriteRotatedF(sprFinger, tx, ty, RealTicks div 32 mod 16, 1, dAngle);
        untint;
        end;

    // render crosshair
    if (CrosshairGear <> nil) and (Gear = CrosshairGear) then
        begin
        sign:= hwSign(Gear^.dX);
        m:= 1;
        if ((Gear^.State and gstHHHJump) <> 0) and (HH^.Effects[heArtillery] = 0) then
            m:= -1;
        setTintAdd(true);
        Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
        DrawTextureRotated(CrosshairTexture,
                12, 12, CrosshairX + WorldDx, CrosshairY + WorldDy, 0,
                sign * m * (Gear^.Angle * 180.0) / cMaxAngle);
        untint;
        setTintAdd(false);
        end;

    // render gear-related extras: alt weapon, fuel, other
    if ((Gear^.State and gstHHDriven) <> 0) and (CurAmmoGear <> nil) then
        begin
        case CurAmmoGear^.Kind of
            gtJetpack:      begin
                            // render jetpack contour if underwater
                            if (((not SuddenDeathDmg) and (WaterOpacity > 179)) or (SuddenDeathDmg and (SDWaterOpacity > 179))) and
                                    ((cWaterLine < (hwRound(Gear^.Y) + Gear^.Radius - 16)) or
                                    ((WorldEdge = weSea) and ((hwRound(Gear^.X) < LeftX) or (hwRound(Gear^.X) > RightX)))) then
                                DrawSprite(sprJetpack, sx-32, sy-32, 4);
                            if CurAmmoGear^.Tex <> nil then
                                DrawTextureCentered(sx, sy - 40, CurAmmoGear^.Tex);
                            DrawAltWeapon(Gear, sx, sy);
                            end;
            gtRope:         DrawAltWeapon(Gear, sx, sy);
            gtParachute:    DrawAltWeapon(Gear, sx, sy);
            gtLandGun:      if CurAmmoGear^.Tex <> nil then
                                DrawTextureCentered(sx, sy - 40, CurAmmoGear^.Tex);
            gtFlamethrower: if CurAmmoGear^.Tex <> nil then
                                DrawTextureCentered(sx, sy - 40, CurAmmoGear^.Tex);
            gtIceGun:       if CurAmmoGear^.Tex <> nil then
                                DrawTextureCentered(sx, sy - 40, CurAmmoGear^.Tex);
        end;
        end;
end;

procedure DrawHH(Gear: PGear; ox, oy: LongInt);
var i, t: LongInt;
    amt: TAmmoType;
    sign, hx, hy, tx, ty, sx, sy, m: LongInt;  // hedgehog, crosshair, temp, sprite, direction
    dx, dy, ax, ay, aAngle, dAngle, hAngle, lx, ly: real;  // laser, change
    wraps: LongWord; // numbe of wraps for laser in world wrap
    defaultPos, HatVisible, inWorldBounds: boolean;
    HH: PHedgehog;
    CurWeapon: PAmmo;
    iceOffset:Longint;
    r:TSDL_Rect;
    curhat: PTexture;
begin
    HH:= Gear^.Hedgehog;
    CrosshairGear:= nil;
    if HH^.Unplaced then
        exit;
    if (HH^.CurAmmoType = amKnife) and (HH = CurrentHedgehog) then
         curhat:= ChefHatTexture
    else curhat:= HH^.HatTex;
    m:= 1;
    if ((Gear^.State and gstHHHJump) <> 0) and (HH^.Effects[heArtillery] = 0) then
        m:= -1;
    sx:= ox + 1; // this offset is very common
    sy:= oy - 3;
    sign:= hwSign(Gear^.dX);

    if (Gear^.State and gstHHDeath) <> 0 then
        begin
        DrawSprite(sprHHDeath, ox - 16, oy - 26, Gear^.Pos);
        Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
        DrawSprite(sprHHDeath, ox - 16, oy - 26, Gear^.Pos + 8);
        untint;
        exit
        end
    else if (Gear^.State and gstHHGone) <> 0 then
        begin
        DrawSpriteRotatedF(sprTeleport, sx, sy, Gear^.Pos, sign, 0);
        exit
        end;

    defaultPos:= true;
    HatVisible:= false;

    if HH^.Effects[heFrozen] > 0 then
        if HH^.Effects[heFrozen] < 150000 then
            begin
            DrawHedgehog(sx, sy,
                    sign,
                    0,
                    0,
                    0);
            defaultPos:= false;
            if HH^.Effects[heFrozen] < 256 then
                 HatVisible:= true
            else HatVisible:= false
            end
        else
            begin
            DrawHedgehog(sx, sy,
                    sign,
                    2,
                    4,
                    0);
            defaultPos:= false;
            HatVisible:= false
            end;


    if HH^.Effects[hePoisoned] <> 0 then
        begin
        Tint($00, $FF, $40, $40);
        DrawTextureRotatedF(SpritesData[sprSmokeWhite].texture, 2, 0, 0, sx, sy, 0, 1, 22, 22, (RealTicks shr 4) mod 360);
        untint
        end;


    if ((Gear^.State and gstWinner) <> 0) and
    ((CurAmmoGear = nil) or (CurAmmoGear^.Kind <> gtPickHammer)) then
        begin
        DrawHedgehog(sx, sy,
                sign,
                2,
                0,
                0);
        defaultPos:= false
        end;
    if (Gear^.State and gstDrowning) <> 0 then
        begin
        DrawHedgehog(sx, sy,
                sign,
                1,
                7,
                0);
        defaultPos:= false
        end else
    if (Gear^.State and gstLoser) <> 0 then
        begin
        DrawHedgehog(sx, sy,
                sign,
                2,
                3,
                0);
        defaultPos:= false
        end else

    if (Gear^.State and gstHHDriven) <> 0 then
        begin
        if ((Gear^.State and (gstHHThinking or gstAnimation)) = 0) and
/// If current ammo is active, and current ammo has alt attack and uses a crosshair  (rope, basically, right now, with no crosshair for parachute/saucer
            (((CurAmmoGear <> nil) and
            // don't render crosshair/laser during kamikaze
            ((CurAmmoGear^.AmmoType <> amKamikaze) or ((Gear^.State and gstAttacking) = 0)) and
             ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_NoCrossHair) = 0)) or
/// If no current ammo is active, and the selected ammo uses a crosshair
            ((CurAmmoGear = nil) and ((Ammoz[HH^.CurAmmoType].Ammo.Propz and ammoprop_NoCrosshair) = 0) and ((Gear^.State and gstAttacked) = 0))) then
            begin
    (* These calculations are a little complex for a few reasons:
    1: I need to draw the laser from weapon origin to nearest land
    2: I need to start the beam outside the hedgie for attractiveness.
    3: I need to extend the beam beyond land.
    This routine perhaps should be pushed into uStore or somesuch instead of continuuing the increase in size of this function.
    *)
            dx:= sign * m * Sin(Gear^.Angle * pi / cMaxAngle);
            dy:= -Cos(Gear^.Angle * pi / cMaxAngle);
            if cLaserSighting or cLaserSightingSniper then
                begin
                lx:= GetLaunchX(HH^.CurAmmoType, sign * m, Gear^.Angle);
                ly:= GetLaunchY(HH^.CurAmmoType, Gear^.Angle);

                // ensure we start outside the hedgehog (he's solid after all)
                while abs(lx * lx + ly * ly) < (Gear^.radius * Gear^.radius) do
                    begin
                    lx:= lx + dx;
                    ly:= ly + dy
                    end;

                // add hog's position
                lx:= lx + ox - WorldDx;
                ly:= ly + oy - WorldDy;

                // decrease number of iterations required
                ax:= dx * 4;
                ay:= dy * 4;

                tx:= round(lx);
                ty:= round(ly);
                hx:= tx;
                hy:= ty;
                wraps:= 0;
                inWorldBounds := ((ty and LAND_HEIGHT_MASK) or (tx and LAND_WIDTH_MASK)) = 0;
                while inWorldBounds and ((Land[ty, tx] and lfAll) = 0) do
                    begin
                    if wraps > cMaxLaserSightWraps then
                        break;
                    lx:= lx + ax;
                    ly:= ly + ay;
                    tx:= round(lx);
                    ty:= round(ly);
                    // reached edge of land.
                    if ((ty and LAND_HEIGHT_MASK) <> 0) then
                        begin
                        // assume infinite beam. Extend it way out past camera
                        tx:= round(lx + ax * (max(LAND_WIDTH,4096) div 2));
                        ty:= round(ly + ay * (max(LAND_WIDTH,4096) div 2));
                        break;
                        end;

                    if ((sign*m < 0) and (tx < LeftX)) or ((sign*m > 0) and (tx >= RightX)) then
                        if (WorldEdge = weWrap) then
                            // wrap beam
                            begin
                            if (sign*m) < 0 then
                                lx:= RightX - (ax - (lx - LeftX))
                            else
                                lx:= LeftX + (ax - (RightX - lx));
                            tx:= round(lx);
                            inc(wraps);
                            end
                        else if (WorldEdge = weBounce) then
                            // just stop
                            break;

                    if ((tx and LAND_WIDTH_MASK) <> 0) then
                        begin
                        if (WorldEdge <> weWrap) and (WorldEdge <> weBounce) then
                            // assume infinite beam. Extend it way out past camera
                            begin
                            tx:= round(lx + ax * (max(LAND_WIDTH,4096) div 2));
                            ty:= round(ly + ay * (max(LAND_WIDTH,4096) div 2));
                            end;
                        break;
                        end;
                    end;

                DrawLineWrapped(hx, hy, tx, ty, 1.0, (sign*m) < 0, wraps, $FF, $00, $00, $C0);
                end;

            // calculate crosshair position
            CrosshairX := Round(hwRound(Gear^.X) + dx * 80 + GetLaunchX(HH^.CurAmmoType, sign * m, Gear^.Angle));
            CrosshairY := Round(hwRound(Gear^.Y) + dy * 80 + GetLaunchY(HH^.CurAmmoType, Gear^.Angle));
            // crosshair will be rendered in RenderHHGuiExtras
            CrosshairGear := Gear;
            end;

        hx:= ox + 8 * sign;
        hy:= oy - 2;
        aangle:= Gear^.Angle * 180 / cMaxAngle - 90;
        if (CurAmmoGear <> nil) and (CurAmmoGear^.Kind <> gtTardis) then
        begin
            case CurAmmoGear^.Kind of
                gtShotgunShot: begin
                        if (CurAmmoGear^.State and gstAnimation <> 0) then
                            DrawSpriteRotated(sprShotgun, hx, hy, sign, aangle)
                        else
                            DrawSpriteRotated(sprHandShotgun, hx, hy, sign, aangle);
                    end;
                gtDEagleShot: DrawSpriteRotated(sprDEagle, hx, hy, sign, aangle);
                gtSniperRifleShot: begin
                        if (CurAmmoGear^.State and gstAnimation <> 0) then
                            DrawSpriteRotatedF(sprSniperRifle, hx, hy, 1, sign, aangle)
                        else
                            DrawSpriteRotatedF(sprSniperRifle, hx, hy, 0, sign, aangle)
                    end;
                gtBallgun: DrawSpriteRotated(sprHandBallgun, hx, hy, sign, aangle);
                gtRCPlane: begin
                    DrawSpriteRotated(sprHandPlane, hx, hy, sign, 0);
                    defaultPos:= false
                    end;
                gtRope: begin
                    if Gear^.X < CurAmmoGear^.X then
                        begin
                        dAngle:= 0;
                        hAngle:= 180;
                        i:= 1
                        end
                    else
                        begin
                        dAngle:= 180;
                        hAngle:= 0;
                        i:= -1
                        end;
                    if ((Gear^.State and gstWinner) = 0) then
                        begin
                        DrawHedgehog(ox, oy,
                                i,
                                1,
                                0,
                                DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + dAngle);
                        with HH^ do
                            if (curhat <> nil) then
                                begin
                                DrawTextureRotatedF(curhat, 1.0, -1.0, -6.0, ox, oy, 0, i, 32, 32,
                                    i*DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + hAngle);
                                if (curhat^.w > 64) or ((curhat^.w = 64) and (curhat^.h = 32)) then
                                    begin
                                    if ((curhat^.w = 64) and (curhat^.h = 32)) then
                                        tx := 1
                                    else
                                        tx := 32;
                                    Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
                                    DrawTextureRotatedF(curhat, 1.0, -1.0, -6.0, ox, oy, tx, i, 32, 32,
                                        i*DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + hAngle);
                                    untint
                                    end
                                end
                    end;
                    defaultPos:= false
                    end;
                gtBlowTorch:
                    begin
                    DrawSpriteRotated(sprBlowTorch, hx, hy, sign, aangle);
                    DrawHedgehog(sx, sy,
                            sign,
                            3,
                            HH^.visStepPos div 2,
                            0);
                    with HH^ do
                        if (curhat <> nil) then
                            begin
                            DrawTextureF(curhat,
                                1,
                                sx,
                                sy - 5,
                                0,
                                sign,
                                32,
                                32);
                            if (curhat^.w > 64) or ((curhat^.w = 64) and (curhat^.h = 32)) then
                                begin
                                if ((curhat^.w = 64) and (curhat^.h = 32)) then
                                    tx := 1
                                else
                                    tx := 32;
                                Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
                                DrawTextureF(curhat,
                                    1,
                                    sx,
                                    sy - 5,
                                    tx,
                                    sign,
                                    32,
                                    32);
                                untint
                                end
                            end;
                    defaultPos:= false
                    end;
                gtFirePunch:
                    begin
                    DrawHedgehog(sx, sy,
                            sign,
                            1,
                            4,
                            0);
                    defaultPos:= false
                    end;
                gtPickHammer:
                    begin
                    defaultPos:= false;
                    dec(sy,20);
                    end;
                gtTeleport: defaultPos:= false;
                gtWhip:
                    begin
                    DrawSpriteRotatedF(sprWhip,
                            sx,
                            sy,
                            1,
                            sign,
                            0);
                    defaultPos:= false
                    end;
                gtHammer:
                    begin
                    DrawSpriteRotatedF(sprHammer,
                            sx,
                            sy,
                            1,
                            sign,
                            0);
                    defaultPos:= false
                    end;
                gtResurrector:
                    begin
                    DrawSpriteRotated(sprHandResurrector, sx, sy, 0, 0);
                    defaultPos:= false
                    end;
                gtKamikaze:
                    begin
                    if CurAmmoGear^.Pos = 0 then
                        DrawHedgehog(sx, sy,
                                sign,
                                1,
                                6,
                                0)
                    else
                        DrawSpriteRotatedF(sprKamikaze,
                                ox, oy,
                                CurAmmoGear^.Pos - 1,
                                sign,
                                aangle);
                    defaultPos:= false
                    end;
                gtSeduction:
                    begin
                    if CurAmmoGear^.Pos >= 6 then
                        DrawHedgehog(sx, sy,
                                sign,
                                2,
                                2,
                                0)
                    else
                        begin
                        DrawSpriteRotatedF(sprDress,
                                ox, oy,
                                CurAmmoGear^.Pos,
                                sign,
                                0);
                        // sprCensored contains English text, so only show it for English locales
                        // TODO: Make text translatable. But how?
                        if Copy(cLanguage, 1, 2) = 'en' then
                            DrawSprite(sprCensored, ox - 32, oy - 20, 0);
                        end;
                    defaultPos:= false
                    end;
                gtFlamethrower: DrawSpriteRotatedF(sprHandFlamethrower, hx, hy, (RealTicks div 125) mod 4, sign, aangle);
                gtLandGun: DrawSpriteRotated(sprHandBallgun, hx, hy, sign, aangle);
                gtIceGun: DrawSpriteRotated(sprIceGun, hx, hy, sign, aangle);
            end;

            case CurAmmoGear^.Kind of
                gtShotgunShot,
                gtDEagleShot,
                gtSniperRifleShot:
                    begin
                    DrawHedgehog(sx, sy, sign, 0, 4, 0);
                    defaultPos:= false;
                    HatVisible:= true
                    end;
                gtShover, gtMinigun:
                    begin
                    DrawHedgehog(sx, sy, sign, 0, 5, 0);
                    defaultPos:= false;
                    HatVisible:= true
                    end
            end
        end else

        if ((Gear^.State and gstHHJumping) <> 0) then
        begin
        DrawHedgehog(sx, sy,
            sign*m,
            1,
            1,
            0);
        HatVisible:= true;
        defaultPos:= false
        end else

        if (Gear^.Message and (gmLeft or gmRight) <> 0) and (not isCursorVisible) then
            begin
            DrawHedgehog(sx, sy,
                sign,
                0,
                HH^.visStepPos div 2,
                0);
            defaultPos:= false;
            HatVisible:= true
            end
        else

        if ((Gear^.State and gstAnimation) <> 0) then
            begin
            if (Gear^.Tag < LongInt(ord(Low(TWave)))) or (Gear^.Tag > LongInt(ord(High(TWave)))) then
                begin
                Gear^.State:= Gear^.State and (not gstAnimation);
                end
            else
                begin
                DrawSpriteRotatedF(Wavez[TWave(Gear^.Tag)].Sprite,
                        sx,
                        sy,
                        Gear^.Pos,
                        sign,
                        0.0);
                defaultPos:= false
                end
            end
        else
        if ((Gear^.State and gstAttacked) = 0) then
            begin
            if HH^.Timer > 0 then
                begin
                // There must be a tidier way to do this. Anyone?
                if aangle <= 90 then
                    aangle:= aangle+360;
                if Gear^.dX > _0 then
                    aangle:= aangle-((aangle-240)*HH^.Timer/10)
                else
                    aangle:= aangle+((240-aangle)*HH^.Timer/10);
                dec(HH^.Timer)
                end;
            amt:= CurrentHedgehog^.CurAmmoType;
            CurWeapon:= GetCurAmmoEntry(HH^);
            case amt of
                amBazooka: DrawSpriteRotated(sprHandBazooka, hx, hy, sign, aangle);
                amSnowball: DrawSpriteRotated(sprHandSnowball, hx, hy, sign, aangle);
                amMortar: DrawSpriteRotated(sprHandMortar, hx, hy, sign, aangle);
                amMolotov: DrawSpriteRotated(sprHandMolotov, hx, hy, sign, aangle);
                amBallgun: DrawSpriteRotated(sprHandBallgun, hx, hy, sign, aangle);
                amDrill: DrawSpriteRotated(sprHandDrill, hx, hy, sign, aangle);
                amRope: DrawSpriteRotated(sprHandRope, hx, hy, sign, aangle);
                amShotgun: DrawSpriteRotated(sprHandShotgun, hx, hy, sign, aangle);
                amDEagle: DrawSpriteRotated(sprHandDEagle, hx, hy, sign, aangle);
                amSineGun: DrawSpriteRotatedF(sprHandSinegun, hx, hy, 73 + (sign * LongInt(RealTicks div 73)) mod 8, sign, aangle);

                amPortalGun:
                    if (CurWeapon^.Timer and 2) <> 0 then // Add a new Hedgehog value instead of abusing timer?
                        DrawSpriteRotatedF(sprPortalGun, hx, hy, 0, sign, aangle)
                    else
                        DrawSpriteRotatedF(sprPortalGun, hx, hy, 1+CurWeapon^.Pos, sign, aangle);

                amSniperRifle: DrawSpriteRotatedF(sprSniperRifle, hx, hy, 0, sign, aangle);
                amBlowTorch: DrawSpriteRotated(sprHandBlowTorch, hx, hy, sign, aangle);
                amCake: DrawSpriteRotated(sprHandCake, hx, hy, sign, aangle);
                amGrenade: DrawSpriteRotated(sprHandGrenade, hx, hy, sign, aangle);
                amWatermelon: DrawSpriteRotated(sprHandMelon, hx, hy, sign, aangle);
                amSkip: DrawSpriteRotated(sprHandSkip, hx, hy, sign, aangle);
                amClusterBomb: DrawSpriteRotated(sprHandCluster, hx, hy, sign, aangle);
                amDynamite: DrawSpriteRotated(sprHandDynamite, hx, hy, sign, aangle);
                amCreeper: DrawSpriteRotatedF(sprHandCreeper, hx, hy, 0, sign, aangle);
                amHellishBomb: DrawSpriteRotated(sprHandHellish, hx, hy, sign, aangle);
                amGasBomb: DrawSpriteRotated(sprHandCheese, hx, hy, sign, aangle);
                amMine: DrawSpriteRotated(sprHandMine, hx, hy, sign, aangle);
                amAirMine: DrawSpriteRotated(sprHandAirMine, hx, hy, sign, aangle);
                amSMine: DrawSpriteRotated(sprHandSMine, hx, hy, sign, aangle);
                amKnife: DrawSpriteRotatedF(sprHandKnife, hx, hy, 0, sign, aangle);
                amSeduction: begin
                             DrawSpriteRotated(sprHandSeduction, hx, hy, sign, aangle);
                             DrawCircle(ox, oy, 248, 4, $FF, $00, $00, $AA);
                             end;
                amVampiric: DrawSpriteRotatedF(sprHandVamp, hx, hy, (RealTicks div 125) mod 4, sign, aangle);
                amRCPlane: begin
                    DrawSpriteRotated(sprHandPlane, hx, hy, sign, 0);
                    defaultPos:= false
                    end;
                amRubber,
                amGirder: begin
                    DrawSpriteRotated(sprHandConstruction, hx, hy, sign, aangle);
                    if cBuildMaxDist = cDefaultBuildMaxDist then
                        begin
                        if WorldEdge = weWrap then
                            begin
                            if hwRound(Gear^.X) < leftX + 256 then
                                DrawSpriteClipped(sprGirder,
                                                rightX+(ox-leftX)-256,
                                                oy-256,
                                                topY+WorldDy,
                                                rightX+WorldDx,
                                                cWaterLine+WorldDy,
                                                leftX+WorldDx);
                            if hwRound(Gear^.X) > rightX - 256 then
                                DrawSpriteClipped(sprGirder,
                                                leftX-(rightX-ox)-256,
                                                oy-256,
                                                topY+WorldDy,
                                                rightX+WorldDx,
                                                cWaterLine+WorldDy,
                                                leftX+WorldDx)
                            end;
                        DrawSpriteClipped(sprGirder,
                                        ox-256,
                                        oy-256,
                                        topY+WorldDy,
                                        rightX+WorldDx,
                                        cWaterLine+WorldDy,
                                        leftX+WorldDx)
                        end
                    else if cBuildMaxDist > 0 then
                        begin
                            DrawCircle(hx, hy, cBuildMaxDist, 3, $FF, 0, 0, $80);
                        end;
                    end;
                amBee: DrawSpriteRotatedF(sprHandBee, hx, hy, (RealTicks div 125) mod 4, sign, aangle);
                amFlamethrower: DrawSpriteRotatedF(sprHandFlamethrower, hx, hy, (RealTicks div 125) mod 4, sign, aangle);
                amLandGun: DrawSpriteRotated(sprHandBallgun, hx, hy, sign, aangle);
                amIceGun: DrawSpriteRotated(sprIceGun, hx, hy, sign, aangle);
                amResurrector: DrawCircle(ox, oy, 98, 4, $F5, $DB, $35, $AA); // I'd rather not like to hardcode 100 here
            end;

            case amt of
                amAirAttack,
                amNapalm,
                amMineStrike,
                amDrillStrike: DrawSpriteRotated(sprHandAirAttack, sx, oy, sign, 0);
                amPickHammer: DrawHedgehog(sx, sy,
                            sign,
                            1,
                            2,
                            0);
                amTeleport,
                amPiano: DrawSpriteRotatedF(sprTeleport, sx, sy, 0, sign, 0);
                amKamikaze: DrawHedgehog(sx, sy,
                            sign,
                            1,
                            5,
                            0);
                amWhip: DrawSpriteRotatedF(sprWhip,
                            sx,
                            sy,
                            0,
                            sign,
                            0);
                amHammer: DrawSpriteRotatedF(sprHammer,
                            sx,
                            sy,
                            0,
                            sign,
                            0);
                amBaseballBat, amMinigun:
                    begin
                    HatVisible:= true;
                    DrawHedgehog(sx, sy,
                            sign,
                            0,
                            5,
                            0);
                    end
            else
                DrawHedgehog(sx, sy,
                    sign,
                    0,
                    4,
                    0);

                HatVisible:= true;
            end;

            defaultPos:= false
        end;

    end else // not gstHHDriven
        begin
        // check if hedgehog is sliding/rolling
        if (Gear^.Damage > 0) and (HH^.Effects[heFrozen] = 0)
        and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > _0_003) then
            begin
            defaultPos:= false;
                DrawHedgehog(sx, sy,
                    sign,
                    2,
                    1,
                    Gear^.DirAngle);

            // dust effect
            // TODO fix: this gives different results based on framerate
            if (sx mod 8) = 0 then
                begin
                if Gear^.dX.isNegative then
                    tx := hwRound(Gear^.X) + cHHRadius
                else
                    tx := hwRound(Gear^.X) - cHHRadius;
                ty:= hwRound(Gear^.Y) + cHHRadius + 2;
                if ((tx and LAND_WIDTH_MASK) = 0) and
                    ((ty and LAND_HEIGHT_MASK) = 0) and
                        (Land[ty, tx] <> 0) then
                            AddVisualGear(tx - 2 + Random(4), ty - 8, vgtDust);
                end;

            // draw april's fool hat
            if AprilOne and (curhat <> nil) then
                DrawTextureRotatedF(curhat, 1.0, -1.0, 0, sx, sy, 18, sign, 32, 32,
                    sign*Gear^.DirAngle)
            end;


        if ((Gear^.State and gstHHJumping) <> 0) then
            begin
            DrawHedgehog(sx, sy,
                sign*m,
                1,
                1,
                0);
            defaultPos:= false
            end;
        end;

    with HH^ do
        begin
        if defaultPos then
            begin
            if HH^.Team^.hasGone then Tint($FFFFFF80);
            DrawSpriteRotatedF(sprHHIdle,
                sx,
                sy,
                (RealTicks div 128 + Gear^.Pos) mod 19,
                sign,
                0);
            HatVisible:= true;
            end;

        if HatVisible then
            if HatVisibility < 1.0 then
                HatVisibility:= HatVisibility + 0.2
            else
        else
            if HatVisibility > 0.0 then
                HatVisibility:= HatVisibility - 0.2;

        if (curhat <> nil)
        and (HatVisibility > 0) then
            if DefaultPos then
                begin
                // Simple hat with automatic offset
                if (curhat^.h = 32) and ((curhat^.w = 32) or (curhat^.w = 64)) then
                    begin
                    // Frame
                    tx := (RealTicks div 128 + Gear^.Pos) mod 19;
                    // Hat offset
                    ty := 0;
                    if (tx = 2) or (tx = 7) or (tx = 12) then
                        ty := 1
                    else if tx = 16 then
                        ty := -1;
                    // First frame: No tint
                    DrawTextureF(curhat,
                        HatVisibility,
                        sx,
                        sy - 5 + ty,
                        0,
                        sign,
                        32,
                        32);
                    // Second frame: Clan tint (if present)
                    if (curhat^.w = 64) then
                        begin
                        Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
                        DrawTextureF(curhat,
                            HatVisibility,
                            sx,
                            sy - 5 + ty,
                            1,
                            sign,
                            32,
                            32);
                        untint
                        end
                    end
                else
                    // Classic animated hat (all frames drawn manually)
                    begin
                    DrawTextureF(curhat,
                        HatVisibility,
                        sx,
                        sy - 5,
                        (RealTicks div 128 + Gear^.Pos) mod 19,
                        sign,
                        32,
                        32);
                    // Apply clan tint
                    if curhat^.w > 64 then
                        begin
                        Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
                        DrawTextureF(curhat,
                            HatVisibility,
                            sx,
                            sy - 5,
                            (RealTicks div 128 + Gear^.Pos) mod 19 + 32,
                            sign,
                            32,
                            32);
                        untint
                        end
                    end;
                if HH^.Team^.hasGone then untint
                end
            else
                begin
                DrawTextureF(curhat,
                    HatVisibility,
                    sx,
                    sy - 5,
                    0,
                    sign*m,
                    32,
                    32);
                if (curhat^.w > 64) or ((curhat^.w = 64) and (curhat^.h = 32)) then
                    begin
                    if ((curhat^.w = 64) and (curhat^.h = 32)) then
                        tx := 1
                    else
                        tx := 32;
                    Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
                    DrawTextureF(curhat,
                        HatVisibility,
                        sx,
                        sy - 5,
                        tx,
                        sign*m,
                        32,
                        32);
                    untint
                    end
                end
        end;

    if (Gear^.State and gstHHDriven) <> 0 then
        begin
        if (CurAmmoGear = nil) then
            begin
                if ((Gear^.State and (gstAttacked or gstAnimation or gstHHJumping)) = 0)
                and (Gear^.Message and (gmLeft or gmRight) = 0) then
                begin
                amt:= CurrentHedgehog^.CurAmmoType;
                    case amt of
                        amBaseballBat: DrawSpritePivotedF(sprHandBaseball,
                            sx + 9 * sign, sy + 2, 0, sign, -8, 1, aangle);
                        amMinigun: DrawSpritePivotedF(sprMinigun,
                            sx + 20 * sign, sy + 4, 0, sign, -18, -2, aangle);
                    end;
                end;
            end
        else
            begin
            aangle:= Gear^.Angle * 180 / cMaxAngle - 90;
            case CurAmmoGear^.Kind of
                gtJetpack: begin
                        DrawSprite(sprJetpack, sx-32, sy-32, 0);
                        if cWaterLine > hwRound(Gear^.Y) + Gear^.Radius then
                            begin
                            if (CurAmmoGear^.MsgParam and gmUp) <> 0 then
                                DrawSprite(sprJetpack, sx-32, sy-28, 1);
                            if (CurAmmoGear^.MsgParam and gmLeft) <> 0 then
                                DrawSprite(sprJetpack, sx-28, sy-28, 2);
                            if (CurAmmoGear^.MsgParam and gmRight) <> 0 then
                                DrawSprite(sprJetpack, sx-36, sy-28, 3)
                            end;
                        end;
                gtShover: DrawSpritePivotedF(sprHandBaseball,
                    sx + 9 * sign, sy + 2, CurAmmoGear^.Tag, sign, -8, 1, aangle);
                gtMinigun: DrawSpritePivotedF(sprMinigun,
                    sx + 20 * sign, sy + 4, CurAmmoGear^.Tag, sign, -18, -2, aangle);
                end;
            end
        end;

    with HH^ do
        begin
        if ((Gear^.State and (not gstWinner)) = 0)
            or ((Gear^.State = gstWait) and (Gear^.dY.QWordValue = 0))
            or (bShowFinger and ((Gear^.State and gstHHDriven) <> 0)) then
            begin
            t:= sy - cHHRadius - 9;
            if (cTagsMask and htTransparent) <> 0 then
                Tint($FF, $FF, $FF, $80);
            if ((cTagsMask and htHealth) <> 0) then
                begin
                dec(t, HealthTagTex^.h + 2);
                DrawTextureCentered(ox, t, HealthTagTex)
                end;
            if (cTagsMask and htName) <> 0 then
                begin
                dec(t, NameTagTex^.h + 2);
                DrawTextureCentered(ox, t, NameTagTex)
                end;
            if (cTagsMask and htTeamName) <> 0 then
                begin
                dec(t, Team^.NameTagTex^.h + 2);
                DrawTextureCentered(ox, t, Team^.NameTagTex)
                end;
            if (cTagsMask and htTransparent) <> 0 then
                untint
            end;
        if (Gear^.State and gstHHDriven) <> 0 then // Current hedgehog
            begin
            if (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtResurrector) then
                DrawTextureCentered(ox, sy - cHHRadius - 7 - HealthTagTex^.h, HealthTagTex);

            if (Gear^.State and gstDrowning) = 0 then
                if ((Gear^.State and gstHHThinking) <> 0) and (not CinematicScript) then
                    DrawSprite(sprQuestion, ox - 10, oy - cHHRadius - 34, (RealTicks shr 9) mod 8)
            end
        end;

    if HH^.Effects[hePoisoned] <> 0 then
        begin
        Tint($00, $FF, $40, $80);
        DrawTextureRotatedF(SpritesData[sprSmokeWhite].texture, 1.5, 0, 0, sx, sy, 0, 1, 22, 22, 360 - (RealTicks shr 5) mod 360);
        end;
    if HH^.Effects[heResurrected] <> 0 then
        begin
        Tint($f5, $db, $35, $20);
        DrawSprite(sprVampiric, sx - 24, sy - 24, 0);
        end;

    if (Gear^.Hedgehog^.Effects[heInvulnerable] <> 0) then
        begin
        Tint($FF, $FF, $FF, max($40, round($FF * abs(1 - ((RealTicks div 2 + Gear^.uid * 491) mod 1500) / 750))));
        DrawSprite(sprInvulnerable, sx - 24, sy - 24, 0);
        end;

    if HH^.Effects[heFrozen] < 150000 then
        begin
        if HH^.Effects[heFrozen] < 150000 then
            Tint($FF, $FF, $FF, min(255,127+HH^.Effects[heFrozen] div 800));

        iceOffset:= min(32, HH^.Effects[heFrozen] div 8);
        r.x := 128;
        r.y := 96 - iceOffset;
        r.w := 32;
        r.h := iceOffset;
        DrawTextureFromRectDir(sx - 16 + sign*2, sy + 16 - iceoffset, r.w, r.h, @r, HHTexture, sign);


        if HH^.Effects[heFrozen] < 150000 then
            untint;
        end;


    if cVampiric and
    (CurrentHedgehog^.Gear <> nil) and
    (CurrentHedgehog^.Gear = Gear) then
        begin
        Tint($FF, 0, 0, max($40, round($FF * abs(1 - (RealTicks mod 1500) / 750))));
        DrawSprite(sprVampiric, sx - 24, sy - 24, 0);
        end;
        untint
end;


procedure RenderGear(Gear: PGear; x, y: LongInt);
var
    HHGear: PGear;
    vg: PVisualGear;
    i: Longword;
    aAngle: real;
    startX, endX, startY, endY: LongInt;
begin
    // airmine has its own sprite
    if (Gear^.State and gstFrozen <> 0) and (Gear^.Kind <> gtAirMine) then Tint($A0, $A0, $FF, $FF);
    if Gear^.Target.X <> NoPointX then
        if Gear^.AmmoType = amBee then
            DrawSpriteRotatedF(sprTargetBee, Gear^.Target.X + WorldDx, Gear^.Target.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360)
    else if Gear^.AmmoType = amIceGun then
        DrawTextureRotatedF(SpritesData[sprSnowDust].Texture, 1/(1+(RealTicks shr 8) mod 5), 0, 0, Gear^.Target.X + WorldDx, Gear^.Target.Y + WorldDy, (RealTicks shr 2) mod 8, 1, 22, 22, (RealTicks shr 3) mod 360)
    else
        begin
        if CurrentHedgehog <> nil then
            begin
            if (IsTooDarkToRead(CurrentHedgehog^.Team^.Clan^.Color)) then
                DrawSpriteRotatedF(sprTargetPBackInv, Gear^.Target.X + WorldDx, Gear^.Target.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360)
            else
                DrawSpriteRotatedF(sprTargetPBack, Gear^.Target.X + WorldDx, Gear^.Target.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
            Tint(CurrentHedgehog^.Team^.Clan^.Color shl 8 or $FF);
            end;
        DrawSpriteRotatedF(sprTargetP, Gear^.Target.X + WorldDx, Gear^.Target.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
        if CurrentHedgehog <> nil then
            untint;
        end;

    case Gear^.Kind of
          gtGrenade: DrawSpriteRotated(sprBomb, x, y, 0, Gear^.DirAngle);
      gtSnowball: DrawSpriteRotated(sprSnowball, x, y, 0, Gear^.DirAngle);
       gtGasBomb: DrawSpriteRotated(sprCheese, x, y, 0, Gear^.DirAngle);

       gtMolotov: if (Gear^.State and gstDrowning) = 0 then
                       DrawSpriteRotatedF(sprMolotov, x, y, (RealTicks div 125) mod 8, hwSign(Gear^.dX), Gear^.DirAngle * hwSign(Gear^.dX))
                  else DrawSprite(sprMolotov, x, y, 8);

       gtRCPlane: begin
                  aangle:= Gear^.Angle * 360 / 4096;
                  if Gear^.Tag < 0 then aangle:= 360-aangle;
                  Tint(Gear^.Tint);
                  DrawSpriteRotatedF(sprPlane, x, y, 0, Gear^.Tag, aangle - 90);
                  untint;
                  DrawSpriteRotatedF(sprPlane, x, y, 1, Gear^.Tag, aangle - 90)
                  end;
       gtBall: DrawSpriteRotatedF(sprBalls, x, y, Gear^.Tag,0, Gear^.DirAngle);

       gtPortal: begin
                 if ((Gear^.Tag and 1) = 0) // still moving?
                 or (Gear^.LinkedGear = nil) or (Gear^.LinkedGear^.LinkedGear <> Gear) // not linked&backlinked?
                 or ((Gear^.LinkedGear^.Tag and 1) = 0) then // linked portal still moving?
                     DrawSpriteRotatedF(sprPortal, x, y, Gear^.Tag, hwSign(Gear^.dX), Gear^.DirAngle)
                 else
                     DrawSpriteRotatedF(sprPortal, x, y, 4 + Gear^.Tag div 2, hwSign(Gear^.dX), Gear^.DirAngle);

                 // Portal ball trace effects
                 if ((Gear^.Tag and 1) = 0) and ((GameTicks mod 4) = 0) and (not isPaused) then
                     begin
                     vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust, 1);
                     if vg <> nil then
                         if Gear^.Tag = 0 then
                             vg^.Tint:= $fab02ab0
                         else if Gear^.Tag = 2 then
                             vg^.Tint:= $364df7b0;
                     end;
                 end;

           gtDrill: if (Gear^.State and gsttmpFlag) <> 0 then
                        DrawSpriteRotated(sprAirDrill, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX))
                    else
                        DrawSpriteRotated(sprDrill, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX));

        gtHedgehog: DrawHH(Gear, x, y);

           gtShell: DrawSpriteRotated(sprBazookaShell, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX));

           gtGrave: begin
                    DrawTextureF(Gear^.Hedgehog^.Team^.GraveTex, 1, x, y, (RealTicks shr 7+Gear^.uid) and 15, 1, 32, 32);
                    if Gear^.Health > 0 then
                        begin
                        Tint($f5, $db, $35, max($40, round($FF * abs(1 - (RealTicks mod 1500) / (750 + Gear^.Health)))));
                        DrawSprite(sprVampiric, x - 24, y - 24, 0);
                        untint
                        end
                    end;
             gtBee: DrawSpriteRotatedF(sprBee, x, y, (RealTicks shr 5) mod 2, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
      gtPickHammer: DrawSprite(sprPHammer, x - 16, y - 50 + LongInt(((GameTicks shr 5) and 1) * 2), 0);
            gtRope: DrawRope(Gear);

            gtMine: begin
                    if (((Gear^.State and gstAttacking) = 0)or((Gear^.Timer and $3FF) < 420)) and (Gear^.Health <> 0) then
                           DrawSpriteRotated(sprMineOff, x, y, 0, Gear^.DirAngle)
                    else if Gear^.Health <> 0 then
                       DrawSpriteRotated(sprMineOn, x, y, 0, Gear^.DirAngle)
                    else DrawSpriteRotated(sprMineDead, x, y, 0, Gear^.DirAngle);
                    end;
         gtAirMine: 
					if (Gear^.State and gstFrozen <> 0) then
                        DrawSprite(sprFrozenAirMine, x-16, y-16, 0)
					else if (Gear^.Tag <> 0) then
                        DrawSprite(sprAirMine, x-16, y-16, 16 + ((RealTicks div 50 + Gear^.Uid) mod 16))
					else if (Gear^.State and gstTmpFlag = 0) then                // mine is inactive
                        begin
						if (Gear^.State and gstTmpFlag = 0) then Tint(150,150,150,255);
                        DrawSprite(sprAirMine, x-16, y-16, 15);
                        untint
                        end
                    else if (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) then  // mine is chasing a hog
                         DrawSprite(sprAirMine, x-16, y-16, (RealTicks div 25 + Gear^.Uid) mod 16)
                    else if Gear^.State and gstChooseTarget <> 0 then   // mine is seeking for hogs
                         DrawSprite(sprAirMine, x-16, y-16, (RealTicks div 125 + Gear^.Uid) mod 16)
                    else
                         DrawSprite(sprAirMine, x-16, y-16, 4);           // mine is active but not seeking

           gtSMine: if (((Gear^.State and gstAttacking) = 0)or((Gear^.Timer and $3FF) < 420)) and (Gear^.Health <> 0) then
                           DrawSpriteRotated(sprSMineOff, x, y, 0, Gear^.DirAngle)
                       else if Gear^.Health <> 0 then
                           DrawSpriteRotated(sprSMineOn, x, y, 0, Gear^.DirAngle)
                       else DrawSpriteRotated(sprMineDead, x, y, 0, Gear^.DirAngle);
           gtKnife: DrawSpriteRotatedF(sprKnife, x, y, 0, hwSign(Gear^.dX), Gear^.DirAngle);

            gtCase: begin
                    if Gear^.Timer > 1000 then
                        begin
                        if ((Gear^.Pos and posCaseAmmo) <> 0) then
                            begin
                            if Gear^.State and gstFrozen <> 0 then
                                DrawSprite(sprCase, x - 24, y - 28, 0)
                            else
                                begin
                                i:= (RealTicks shr 6) mod 64;
                                if i > 18 then i:= 0;
                                DrawSprite(sprCase, x - 24, y - 24, i)
                                end
                            end
                        else if ((Gear^.Pos and posCaseHealth) <> 0) then
                            begin
                            if Gear^.State and gstFrozen <> 0 then
                                DrawSprite(sprFAid, x - 24, y - 28, 0)
                            else
                                begin
                                i:= ((RealTicks shr 6) + 38) mod 64;
                                if i > 13 then i:= 0;
                                DrawSprite(sprFAid, x - 24, y - 24, i)
                                end
                            end
                        else if ((Gear^.Pos and posCaseUtility) <> 0) then
                            begin
                            if Gear^.State and gstFrozen <> 0 then
                                DrawSprite(sprUtility, x - 24, y - 28, 0)
                            else
                                begin
                                i:= (RealTicks shr 6) mod 70;
                                if i > 23 then i:= 0;
                                i:= i mod 12;
                                DrawSprite(sprUtility, x - 24, y - 24, i)
                                end
                            end
                        end;
                    if Gear^.Timer < 1833 then
                        begin
                        DrawTextureRotatedF(SpritesData[sprPortal].texture, MinD(abs(1.25 - (Gear^.Timer mod 1333) / 400), 1.25), 0, 0,
                                            x, LongInt(Gear^.Angle) + WorldDy - 16, 4 + Gear^.Tag, 1, 32, 32, 270);
                        end
                    end;
      gtExplosives: begin
                    if ((Gear^.State and gstDrowning) <> 0) then
                        DrawSprite(sprExplosivesRoll, x - 24, y - 24, 0)
                    else if Gear^.State and gstAnimation = 0 then
                        begin
                        i:= (RealTicks shr 6 + Gear^.uid*3) mod 64;
                        if i > 18 then
                            i:= 0;
                        DrawSprite(sprExplosives, x - 24, y - 24, i)
                        end
                    else if Gear^.State and gsttmpFlag = 0 then
                        DrawSpriteRotatedF(sprExplosivesRoll, x, y + 4, 0, 0, Gear^.DirAngle)
                    else
                        DrawSpriteRotatedF(sprExplosivesRoll, x, y + 4, 1, 0, Gear^.DirAngle)
                    end;
        gtDynamite: begin
                    DrawSprite(sprDynamite, x - 16, y - 25, Gear^.Tag and 1, Gear^.Tag shr 1);
                    if (random(3) = 0) and ((Gear^.State and gstDrowning) = 0) then
                        begin
                        vg:= AddVisualGear(hwRound(Gear^.X)+12-(Gear^.Tag shr 1), hwRound(Gear^.Y)-16, vgtStraightShot);
                        if vg <> nil then
                            with vg^ do
                                begin
                                Tint:= $FFCC00FF;
                                Angle:= random(360);
                                dx:= 0.0005 * (random(200));
                                dy:= 0.0005 * (random(200));
                                if random(2) = 0 then
                                    dx := -dx;
                                if random(2) = 0 then
                                    dy := -dy;
                                FrameTicks:= 100+random(300);
                                Scale:= 0.1+1/(random(3)+3);
                                State:= ord(sprStar)
                                end
                        end;

                    end;
     gtClusterBomb: DrawSpriteRotated(sprClusterBomb, x, y, 0, Gear^.DirAngle);
         gtCluster: DrawSprite(sprClusterParticle, x - 8, y - 8, 0);
           gtFlame: if Gear^.Tag and 1 = 0 then
                         DrawTextureF(SpritesData[sprFlame].Texture, 2 / (Gear^.Tag mod 3 + 2), x, y, (RealTicks shr 7 + LongWord(Gear^.Tag)) mod 8, 1, 16, 16)
                    else DrawTextureF(SpritesData[sprFlame].Texture, 2 / (Gear^.Tag mod 3 + 2), x, y, (RealTicks shr 7 + LongWord(Gear^.Tag)) mod 8, -1, 16, 16);
       gtParachute: begin
                    DrawSprite(sprParachute, x - 24, y - 48, 0);
                    end;
       gtAirAttack: begin
                    Tint(Gear^.Tint);
                    DrawSpriteRotatedF(sprAirplane, x, y, 0, Gear^.Tag, 0);
                    untint;
                    DrawSpriteRotatedF(sprAirplane, x, y, 1, Gear^.Tag, 0);
                    end;
         gtAirBomb: DrawSpriteRotated(sprAirBomb, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
        gtTeleport: begin
                    HHGear:= Gear^.Hedgehog^.Gear;
                    if HHGear <> nil then
                        begin
                        if ((Gear^.State and gstAnimation) <> 0) then
                            DrawSpriteRotatedF(sprTeleport, x + 1, y - 3, Gear^.Pos, hwSign(Gear^.dX), 0);
                        DrawSpriteRotatedF(sprTeleport, hwRound(HHGear^.X) + 1 + WorldDx, hwRound(HHGear^.Y) - 3 + WorldDy, 11 - Gear^.Pos, hwSign(HHGear^.dX), 0)
                        end
                    end;
        gtSwitcher: begin
                    setTintAdd(true);
                    Tint(Gear^.Hedgehog^.Team^.Clan^.Color shl 8 or $FF);
                    DrawSprite(sprSwitch, x - 16, y - 56, (RealTicks shr 6) mod 12);
                    untint;
                    setTintAdd(false);
                    end;
          gtTarget: begin
                    Tint($FF, $FF, $FF, round($FF * Gear^.Timer / 1000));
                    DrawSprite(sprTarget, x - 16, y - 16, 0);
                    untint;
                    end;
          gtMortar: DrawSpriteRotated(sprMortar, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
          gtCake: if Gear^.Pos = 6 then
                     DrawSpriteRotatedF(sprCakeWalk, x, y, (GameTicks div 40) mod 6, hwSign(Gear^.dX), Gear^.DirAngle * hwSign(Gear^.dX) + 90)
                  else
                     DrawSpriteRotatedF(sprCakeDown, x, y, 5 - Gear^.Pos, hwSign(Gear^.dX), Gear^.DirAngle * hwSign(Gear^.dX) + 90);
       gtSeduction: if Gear^.Pos >= 14 then
           DrawSprite(sprSeduction, x - 16, y - 16, 0);

      gtWatermelon: DrawSpriteRotatedF(sprWatermelon, x, y, 0, 0, Gear^.DirAngle);
      gtMelonPiece: DrawSpriteRotatedF(sprWatermelon, x, y, 1, 0, Gear^.DirAngle);
     gtHellishBomb: DrawSpriteRotated(sprHellishBomb, x, y, 0, Gear^.DirAngle);
           gtBirdy: begin
                    if Gear^.State and gstAnimation = gstAnimation then
                        begin
                        if Gear^.State and gstTmpFlag = 0 then // Appearing
                            begin
                            endX:= x - WorldDx;
                            endY:= y - WorldDy;
                            if Gear^.Tag < 0 then
                                startX:= max(max(LAND_WIDTH,4096) + 1024, endX + 2048)
                            else
                                startX:= max(-max(LAND_WIDTH,4096) - 1024, endX - 2048);
                            startY:= endY - 1024;
                            DrawTextureF(SpritesData[sprBirdy].Texture, min(Gear^.Timer/750,1), startX + WorldDx + LongInt(round((endX - startX) * (-power(2, -10 * LongInt(Gear^.Timer)/2000) + 1))), startY + WorldDy + LongInt(round((endY - startY) * sqrt(1 - power((LongInt(Gear^.Timer)/2000)-1, 2)))), ((Gear^.Pos shr 6) or (RealTicks shr 8)) mod 2, Gear^.Tag, 75, 75);
                            end
                        else // Disappearing
                            begin
                            startX:= x - WorldDx;
                            startY:= y - WorldDy;
                            if Gear^.Tag > 0 then
                                endX:= max(max(LAND_WIDTH,4096) + 1024, startX + 2048)
                            else
                                endX:= max(-max(LAND_WIDTH,4096) - 1024, startX - 2048);
                            endY:= startY + 1024;
                            DrawTextureF(SpritesData[sprBirdy].Texture, min((2000-Gear^.Timer)/750,1), startX + WorldDx + LongInt(round((endX - startX) * power(2, 10 * (LongInt(Gear^.Timer)/2000 - 1)))) + hwRound(Gear^.dX * Gear^.Timer), startY + WorldDy + LongInt(round((endY - startY) * cos(LongInt(Gear^.Timer)/2000 * (Pi/2)) - (endY - startY))) + hwRound(Gear^.dY * Gear^.Timer), ((Gear^.Pos shr 6) or (RealTicks shr 8)) mod 2, Gear^.Tag, 75, 75);
                            end;
                        end
                    else
                        begin
                        if Gear^.Health < 250 then
                            DrawTextureF(SpritesData[sprBirdy].Texture, 1, x, y, ((Gear^.Pos shr 6) or (RealTicks shr 7)) mod 2, Gear^.Tag, 75, 75)
                        else
                            DrawTextureF(SpritesData[sprBirdy].Texture, 1, x, y, ((Gear^.Pos shr 6) or (RealTicks shr 8)) mod 2, Gear^.Tag, 75, 75);
                        end;
                    end;
             gtEgg: DrawTextureRotatedF(SpritesData[sprEgg].Texture, 1, 0, 0, x, y, 0, 1, 16, 16, Gear^.DirAngle);
           gtPiano: begin
                    if (Gear^.State and gstDrowning) = 0 then
                        begin
                        Tint($FF, $FF, $FF, $10);
                        for i:= 8 downto 1 do
                            DrawTextureF(SpritesData[sprPiano].Texture, 1, x, y - hwRound(Gear^.dY * 4 * i), 0, 1, 128, 128);
                        untint
                        end;
                    DrawTextureF(SpritesData[sprPiano].Texture, 1, x, y, 0, 1, 128, 128);
                    end;
     gtPoisonCloud: begin
                    if Gear^.Timer < 1020 then
                        Tint(Gear^.Tint and $FFFFFF00 or Gear^.Timer div 8)
                    else if Gear^.Timer > 3980 then
                        Tint(Gear^.Tint and $FFFFFF00 or (5000 - Gear^.Timer) div 8)
                    else
                        Tint(Gear^.Tint);
                    DrawTextureRotatedF(SpritesData[sprSmokeWhite].texture, 3, 0, 0, x, y, 0, 1, 22, 22, (RealTicks shr 4 + Gear^.UID * 100) mod 360);
                    untint
                    end;
     gtResurrector: begin
                    DrawSpriteRotated(sprCross, x, y, 0, 0);
                    Tint(Gear^.Tint and $FFFFFF00 or max($00, round($C0 * abs(1 - (GameTicks mod 6000) / 3000))));
                    DrawTexture(x - 108, y - 108, SpritesData[sprVampiric].Texture, 4.5);
                    untint;
                    end;
      gtNapalmBomb: DrawSpriteRotated(sprNapalmBomb, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
           gtFlake: if Gear^.State and (gstDrowning or gstTmpFlag) <> 0  then
                        begin
                        Tint(Gear^.Tint);
                        // Needs a nicer white texture to tint
                        DrawTextureRotatedF(SpritesData[sprSnowDust].Texture, 1, 0, 0, x, y, 0, 1, 8, 8, Gear^.DirAngle);
                        untint;
                        end
                    else //if not isInLag then
                        begin
                        if isInLag and (Gear^.FlightTime < 256) then
                            inc(Gear^.FlightTime, 8)
                        else if (not isInLag) and (Gear^.FlightTime > 0) then
                            dec(Gear^.FlightTime, 8);
                        if Gear^.FlightTime > 0 then
                            Tint($FF, $FF, $FF, $FF-min(255,Gear^.FlightTime));
                        if vobVelocity = 0 then
                            DrawSprite(sprFlake, x, y, Gear^.Timer)
                        else
                            DrawSpriteRotatedF(sprFlake, x, y, Gear^.Timer, 1, Gear^.DirAngle);
                        if Gear^.FlightTime > 0 then
                            untint;
                        end;
          gtTardis: if Gear^.Pos <> 4 then
                        begin
                        if Gear^.Pos = 2 then
                            Tint(Gear^.Hedgehog^.Team^.Clan^.Color shl 8 or $FF)
                        else
                            Tint(Gear^.Hedgehog^.Team^.Clan^.Color shl 8 or max($00, round(Gear^.Power * (1-abs(0.5 - (GameTicks mod 2000) / 2000)))));
                        DrawSprite(sprTardis, x-25, y-64,0);
                        if Gear^.Pos = 2 then
                            untint
                        else
                            Tint($FF,$FF,$FF,max($00, round(Gear^.Power * (1-abs(0.5 - (GameTicks mod 2000) / 2000)))));
                        DrawSprite(sprTardis, x-25, y-64,1);
                        if Gear^.Pos <> 2 then
                            untint
                        end;
            gtIceGun: begin
                      HHGear := Gear^.Hedgehog^.Gear;
                      if HHGear <> nil then
                          begin
                          i:= hwRound(hwSqr(Gear^.X - HHGear^.X) + hwSqr(Gear^.Y - HHGear^.Y));
                          if RealTicks mod max(1,50 - (round(sqrt(i)) div 4)) = 0 then // experiment in "intensifying" might not get used
                            begin
                            vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust, 1);
                            if vg <> nil then
                                begin
                                i:= random(100) + 155;
                                vg^.Tint:= i shl 24 or i shl 16 or $FF shl 8 or Longword(random(200) + 55);
                                vg^.Angle:= random(360);
                                vg^.dx:= 0.001 * random(80);
                                vg^.dy:= 0.001 * random(80)
                                end
                            end;
                          if RealTicks mod 2 = 0 then
                                begin
                                i:= random(100)+100;
                                if Gear^.Target.X <> NoPointX then
                                    begin
                                    DrawLineWrapped(hwRound(HHGear^.X), hwRound(HHGear^.Y), Gear^.Target.X, Gear^.Target.Y, 4.0, hwSign(HHGear^.dX) < 0, Gear^.FlightTime, i, i, $FF, $40);
                                    end
                                else
                                    begin
                                    DrawLineWrapped(hwRound(HHGear^.X), hwRound(HHGear^.Y), hwRound(Gear^.X), hwRound(Gear^.Y), 4.0, hwSign(HHGear^.dX) < 0, Gear^.FlightTime, i, i, $FF, $40);
                                    end;
                                end
                          end
                      end;
            gtCreeper: if (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) then
					     DrawSpriteRotatedF(sprCreeper, x, y, 1, hwRound(SignAs(_1,Gear^.Hedgehog^.Gear^.X-Gear^.X)), 0)
					else DrawSpriteRotatedF(sprCreeper, x, y, 1, hwRound(SignAs(_1,Gear^.dX)), 0);

            gtGenericFaller: begin
                             // DEBUG: draw gtGenericFaller
                             if Gear^.Tag <> 0 then
                                 DrawCircle(x, y, max(3, Gear^.Radius), 3, $FF, $00, $00, $FF)
                             else
                                 DrawCircle(x, y, max(3, Gear^.Radius), 3, $80, $FF, $80, $8F);
                             end;
         end;
    if Gear^.State and gstFrozen <> 0 then untint
end;

procedure RenderGearTimer(Gear: PGear; x, y: LongInt);
begin
if Gear^.RenderTimer and (Gear^.Tex <> nil) and (isShowGearInfo or (not (Gear^.Kind in [gtMine, gtSMine, gtAirMine]))) then
    DrawTextureCentered(x + 8, y + 8, Gear^.Tex);
end;

procedure RenderGearHealth(Gear: PGear; x, y: LongInt);
begin
if isShowGearInfo and (Gear^.RenderHealth) and (Gear^.Tex <> nil) then
    begin
    if (Gear^.Kind = gtCase) and ((Gear^.Pos and $02) <> 0) then
        DrawTextureCentered(x, y - 38, Gear^.Tex);
    if (Gear^.Kind = gtExplosives) then
        DrawTextureCentered(x, y - 38, Gear^.Tex);
    end;
end;

end.