Remove FindSDL2 find-module, use sdl2-config.cmake instead
This requires SDL >= 2.0.4.
Since <https://bugzilla.libsdl.org/show_bug.cgi?id=2464> was fixed in
SDL 2.0.4, SDL behaves as a CMake "config-file package", even if it was
not itself built using CMake: it installs a sdl2-config.cmake file to
${libdir}/cmake/SDL2, which tells CMake where to find SDL's headers and
library, analogous to a pkg-config .pc file.
As a result, we no longer need to copy/paste a "find-module package"
to be able to find a system copy of SDL >= 2.0.4 with find_package(SDL2).
Find-module packages are now discouraged by the CMake developers, in
favour of having upstream projects behave as config-file packages.
This results in a small API change: FindSDL2 used to set SDL2_INCLUDE_DIR
and SDL2_LIBRARY, but the standard behaviour for config-file packages is
to set <name>_INCLUDE_DIRS and <name>_LIBRARIES. Use the CONFIG keyword
to make sure we search in config-file package mode, and will not find a
FindSDL2.cmake in some other directory that implements the old interface.
In addition to deleting redundant code, this avoids some assumptions in
FindSDL2 about the layout of a SDL installation. The current libsdl2-dev
package in Debian breaks those assumptions; this is considered a bug
and will hopefully be fixed soon, but it illustrates how fragile these
assumptions can be. We can be more robust against different installation
layouts by relying on SDL's own CMake integration.
When linking to a copy of CMake in a non-standard location, users can
now set the SDL2_DIR or CMAKE_PREFIX_PATH environment variable to point
to it; previously, these users would have used the SDL2DIR environment
variable. This continues to be unnecessary if using matching system-wide
installations of CMake and SDL2, for example both from Debian.
(*
* 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 RenderFinger(Gear: PGear; ox, oy: LongInt);
procedure RenderHHGuiExtras(Gear: PGear; ox, oy: LongInt);
procedure RenderAirMineGuiExtras(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, uGearsUtils, 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;
procedure DrawRopeLine(X1, Y1, X2, Y2: Real; LayerIndex: Longword; var linesLength, ropeLength: Real);
var dX, dY, angle, lineLength: Real;
FrameIndex: LongWord;
begin
if (X1 = X2) and (Y1 = Y2) then
exit;
dX:= X2 - X1;
dY:= Y2 - Y1;
lineLength:= sqrt(sqr(dX) + sqr(dY));
angle:= arctan2(dY, dX) * 180 / PI - 90;
dX:= dX / lineLength;
dY:= dY / lineLength;
while (ropeLength - linesLength) <= lineLength do
begin
FrameIndex:= round(ropeLength / cRopeNodeStep);
if (FrameIndex mod cRopeLayers) = LayerIndex then
DrawSpriteRotatedFReal(sprRopeNode,
X1 + (ropeLength - linesLength) * dX,
Y1 + (ropeLength - linesLength) * dY,
FrameIndex, 1, angle);
ropeLength:= ropeLength + cRopeNodeStep;
end;
linesLength:= linesLength + lineLength
end;
procedure DrawRopeLayer(Gear: PGear; LayerIndex: LongWord);
var i: LongInt;
linesLength, ropeLength: Real;
begin
linesLength:= 0;
ropeLength:= cRopeNodeStep;
if RopePoints.Count > 0 then
begin
i:= 0;
while i < Pred(RopePoints.Count) do
begin
DrawRopeLine(hwFloat2Float(RopePoints.ar[i].X) + WorldDx, hwFloat2Float(RopePoints.ar[i].Y) + WorldDy,
hwFloat2Float(RopePoints.ar[Succ(i)].X) + WorldDx, hwFloat2Float(RopePoints.ar[Succ(i)].Y) + WorldDy,
LayerIndex, linesLength, ropeLength);
inc(i)
end;
DrawRopeLine(hwFloat2Float(RopePoints.ar[i].X) + WorldDx, hwFloat2Float(RopePoints.ar[i].Y) + WorldDy,
hwFloat2Float(Gear^.X) + WorldDx, hwFloat2Float(Gear^.Y) + WorldDy,
LayerIndex, linesLength, ropeLength);
DrawRopeLine(hwFloat2Float(Gear^.X) + WorldDx, hwFloat2Float(Gear^.Y) + WorldDy,
hwFloat2Float(Gear^.Hedgehog^.Gear^.X) + WorldDx, hwFloat2Float(Gear^.Hedgehog^.Gear^.Y) + WorldDy,
LayerIndex, linesLength, ropeLength);
end
else
if Gear^.Elasticity.QWordValue > 0 then
DrawRopeLine(hwFloat2Float(Gear^.X) + WorldDx, hwFloat2Float(Gear^.Y) + WorldDy,
hwFloat2Float(Gear^.Hedgehog^.Gear^.X) + WorldDx, hwFloat2Float(Gear^.Hedgehog^.Gear^.Y) + WorldDy,
LayerIndex, linesLength, ropeLength);
end;
procedure DrawRope(Gear: PGear);
var i: LongInt;
begin
if Gear^.Hedgehog^.Gear = nil then exit;
if (Gear^.Tag = 1) or ((cReducedQuality and rqSimpleRope) <> 0) then
DrawRopeLinesRQ(Gear)
else
for i := 0 to cRopeLayers - 1 do
DrawRopeLayer(Gear, i);
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 DrawSelectedWeapon(Gear: PGear; sx, sy: LongInt; isAltWeapon: boolean);
begin
with Gear^.Hedgehog^ do
begin
if ((Gear^.State and gstAttacked) <> 0) then
exit;
if (isAltWeapon and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) = 0)) then
exit;
if (not isAltWeapon) and (((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_ShowSelIcon) = 0) or (
(((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AttackInMove) = 0) and ((Gear^.State and gstMoving) <> 0)))) then
exit;
if (not isAltWeapon) then
begin
sy:= sy - 64;
if (IsHogFacingLeft(Gear)) then
sx:= sx - 61;
end;
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;
procedure RenderFinger(Gear: PGear; ox, oy: LongInt);
var HH: PHedgehog;
tx, ty, t: LongInt;
dAngle: real;
begin
HH:= Gear^.Hedgehog;
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 (arrow pointing to hog)
if bShowFinger and ((Gear^.State and gstHHDriven) <> 0) then
begin
ty := oy - 32;
// move finger higher up if tags or switching arrows 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;
if bShowSwitcher then
ty := ty - SpritesData[sprSwitch].Height - 4;
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;
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, hogLR: LongInt;
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 crosshair
if (CrosshairGear <> nil) and (Gear = CrosshairGear) then
begin
hogLR:= 1;
if IsHogFacingLeft(Gear) then
hogLR:= -1;
setTintAdd(true);
Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
DrawTextureRotated(CrosshairTexture,
12, 12, CrosshairX + WorldDx, CrosshairY + WorldDy, 0,
hogLR * (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 > cGearContourThreshold)) or (SuddenDeathDmg and (SDWaterOpacity > cGearContourThreshold))) 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);
DrawSelectedWeapon(Gear, sx, sy, true);
end;
gtRope: DrawSelectedWeapon(Gear, sx, sy, true);
gtParachute: DrawSelectedWeapon(Gear, sx, sy, true);
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
else if ((Gear^.State and gstHHDriven) <> 0) then
begin
DrawSelectedWeapon(Gear, sx, sy, false);
end
end;
procedure RenderAirMineGuiExtras(Gear: PGear; ox, oy: LongInt);
var tinted: boolean;
begin
// render air mine contour, if underwater
if (((not SuddenDeathDmg) and (WaterOpacity > cGearContourThreshold)) or (SuddenDeathDmg and (SDWaterOpacity > cGearContourThreshold))) and
((cWaterLine < (hwRound(Gear^.Y) + Gear^.Radius + 16)) or
((WorldEdge = weSea) and ((hwRound(Gear^.X) < LeftX + 24) or (hwRound(Gear^.X) > RightX - 24)))) then
begin
tinted:= true;
// tint contour based on air mine state:
// not seeking or chasing (frozen, stunned or just launched)
if ((Gear^.State and gstFrozen) <> 0) or ((Gear^.State and gstTmpFlag) = 0) or (Gear^.Tag <> 0) then
// more transparent
Tint($FF, $FF, $FF, $80)
// chasing hog
else if (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) then
// reddish
Tint($FF, $30, $30, $FF)
// not seeking or chasing (no target)
else if (Gear^.State and gstChooseTarget) = 0 then
// more transparent
Tint($FF, $FF, $FF, $80)
// seeking
else
// default color
tinted:= false;
DrawSprite(sprAirMine, ox-16, oy-16, 32);
if tinted then
untint;
end;
end;
procedure DrawHH(Gear: PGear; ox, oy: LongInt);
var i, t: LongInt;
amt: TAmmoType;
sign, hx, hy, tx, ty, sx, sy, hogLR: 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;
sx:= ox + 1; // this offset is very common
sy:= oy - 3;
sign:= hwSign(Gear^.dX);
if IsHogFacingLeft(Gear) then
hogLR:= -1
else
hogLR:= 1;
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:= hogLR * Sin(Gear^.Angle * pi / cMaxAngle);
dy:= -Cos(Gear^.Angle * pi / cMaxAngle);
if cLaserSighting or cLaserSightingSniper then
begin
lx:= GetLaunchX(HH^.CurAmmoType, hogLR, 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)) or (not inWorldBounds) 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) and (((ty < LAND_HEIGHT) and (ay < 0)) or ((ty >= TopY) and (ay > 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 ((hogLR < 0) and (tx < LeftX)) or ((hogLR > 0) and (tx >= RightX)) then
if (WorldEdge = weWrap) then
// wrap beam
begin
if hogLR < 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) and (((ax > 0) and (tx >= RightX)) or ((ax < 0) and (tx <= LeftX))) 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;
inWorldBounds := ((ty and LAND_HEIGHT_MASK) or (tx and LAND_WIDTH_MASK)) = 0;
end;
DrawLineWrapped(hx, hy, tx, ty, 1.0, hogLR < 0, wraps, $FF, $00, $00, $C0);
end;
// calculate crosshair position
CrosshairX := Round(hwRound(Gear^.X) + dx * 80 + GetLaunchX(HH^.CurAmmoType, hogLR, 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 + 1, 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;
gtParachute:
begin
DrawSpriteRotatedF(sprHHIdle,
sx,
sy,
0,
CurAmmoGear^.Tag,
0);
HatVisible:= true;
defaultPos:= false;
end;
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(sprHandLandGun, 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,
hogLR,
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: if ((Gear^.State and gstMoving) = 0) then
begin
DrawSpriteRotated(sprHandSeduction, hx, hy, sign, aangle);
DrawCircle(ox, oy, cSeductionDist - 2, 4, $FF, $00, $00, $AA);
end;
amVampiric: DrawSpriteRotatedF(sprHandVamp, hx, hy, (RealTicks div 125) mod 4, sign, aangle);
amRubber,
amGirder: if ((Gear^.State and gstMoving) = 0) then
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(sprHandLandGun, hx, hy, sign, aangle);
amIceGun: DrawSpriteRotated(sprIceGun, hx, hy, sign, aangle);
amResurrector: if ((Gear^.State and gstMoving) = 0) then
DrawCircle(ox, oy, cResurrectorDist - 2, 4, $F5, $DB, $35, $AA);
amFirePunch: DrawSpriteRotatedF(sprFirePunch, hx + 6 * sign + 1, hy - 5, (RealTicks div 50) mod 16, sign, 0);
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 + sign,
sy,
0,
sign,
0);
amRCPlane:
begin
DrawSpriteRotated(sprHandPlane, hx + 1, hy, sign, 0);
defaultPos:= false
end;
amBaseballBat, amMinigun:
begin
HatVisible:= true;
DrawHedgehog(sx, sy,
sign,
0,
5,
0);
end
else
// Special hog sprite that makes hog "look" towards the selection icon.
// Only works without hat for now since it would look weird/creepy for many hats.
if ((HH^.Hat = 'NoHat') or (HH^.HatTex = nil)) and ((Gear^.State and (gstMoving or gstAttacking)) = 0) and ((Ammoz[amt].Ammo.Propz and ammoprop_ShowSelIcon) <> 0) then
DrawHedgehog(sx, sy,
sign,
0,
6,
0)
// Default idle hedgehog
else
begin
DrawHedgehog(sx, sy,
sign,
0,
4,
0);
HatVisible:= true;
end;
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,
hogLR,
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,
hogLR,
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,
hogLR,
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, ty: 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: begin
if (Gear^.Pos = 1) then
i:= (RealTicks shr 5 + Gear^.uid) mod 4
else
i:= Gear^.uid mod 4;
if (Gear^.State and gsttmpFlag) <> 0 then
DrawTextureRotatedF(SpritesData[sprAirDrill].texture, 0.5, 0, 0, x, y, i, 0, 64, 64, DxDy2Angle(Gear^.dY, Gear^.dX))
else
DrawTextureRotatedF(SpritesData[sprDrill].texture, 0.5, 0, 0, x, y, i, 0, 64, 64, DxDy2Angle(Gear^.dY, Gear^.dX));
end;
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:
// render air mine based on its state:
// frozen
if (Gear^.State and gstFrozen <> 0) then
// frozen air mine sprite
DrawSprite(sprFrozenAirMine, x-16, y-16, 0)
// stunned (after being shot)
else if (Gear^.Tag <> 0) then
// sparks animation
DrawSprite(sprAirMine, x-16, y-16, 16 + ((RealTicks div 50 + Gear^.Uid) mod 16))
// inactive / initialization phase (shortly after launched by hog)
else if (Gear^.State and gstTmpFlag = 0) then
begin
// dark air mine, signal lamp off
Tint(150,150,150,255);
DrawSprite(sprAirMine, x-16, y-16, 15);
untint
end
// actively chasing a hog
else if (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) then
// signal lamp rapidly flashes
DrawSprite(sprAirMine, x-16, y-16, (RealTicks div 25 + Gear^.Uid) mod 16)
// seeking for hogs
else if Gear^.State and gstChooseTarget <> 0 then
// signal lamp on
DrawSprite(sprAirMine, x-16, y-16, 3)
// active, but not seeking for hogs
else
// signal lamp off
DrawSprite(sprAirMine, x-16, y-16, 15);
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);
if WorldEdge <> weSea then
DrawSpriteRotatedF(sprAirplane, x, y, 2, Gear^.Tag, 0)
else
DrawSpriteRotatedF(sprAirplane, x, y, 3, 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);
if IsTooDarkToRead(Gear^.Hedgehog^.Team^.Clan^.Color) then
Tint($FFFFFFFF)
else
Tint($000000FF);
ty := y - SpritesData[sprSwitch].Height;
// Move higher up if hedgehog tags are visible.
// This happens when finger is active. The finger is then moved above the switching arrows.
if bShowFinger then
begin
if (cTagsMask and htTeamName) <> 0 then
ty := ty - Gear^.Hedgehog^.Team^.NameTagTex^.h - 2;
if (cTagsMask and htName) <> 0 then
ty := ty - Gear^.Hedgehog^.NameTagTex^.h - 2;
if (cTagsMask and htHealth) <> 0 then
ty := ty - Gear^.Hedgehog^.HealthTagTex^.h - 2;
if (cTagsMask and (htTeamName or htName or htHealth)) <> 0 then
ty := ty - 4;
end;
DrawSpriteRotatedF(sprSwitch, x + 1, ty, 1, 0, (RealTicks div 5) mod 360);
Tint(Gear^.Hedgehog^.Team^.Clan^.Color shl 8 or $FF);
DrawSpriteRotatedF(sprSwitch, x + 1, ty, 0, 0, (RealTicks div 5) mod 360);
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 posCaseHealth) <> 0) then
DrawTextureCentered(x, y - 38, Gear^.Tex);
if (Gear^.Kind = gtExplosives) then
DrawTextureCentered(x, y - 38, Gear^.Tex);
end;
end;
end.