Basic test implementation of an ice flag. Allows for slick parts of terrain. Intended for ice gun, or "ice" mask on portions of land objects.
In this test variant it is triggered on girders/objects/bridges of the snow/christmas theme, or on a map that uses blue as a mask colour. Probably needs sheepluva's slope detection to make slopes more slippery to climb.
(*
* Hedgewars, a free turn based strategy game
* Copyright (c) 2004-2011 Andrey Korotaev <unC0Rr@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*)
{$INCLUDE "options.inc"}
unit uVisualGears;
(*
* This unit defines the behavior and the appearance of visual gears.
*
* Visual gears are "things"/"objects" in the game that do not need to be
* perfectly synchronized over all clients since their effect is only
* of visual nature.
*
* E.g.: background flakes, visual effects: explosion, smoke trails, etc.
*)
interface
uses uConsts, uFloat, GLunit, uTypes, uWorld;
procedure initModule;
procedure freeModule;
function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord = 0; Critical: Boolean = false): PVisualGear;
procedure ProcessVisualGears(Steps: Longword);
procedure KickFlakes(Radius, X, Y: LongInt);
procedure DrawVisualGears(Layer: LongWord);
procedure DeleteVisualGear(Gear: PVisualGear);
function VisualGearByUID(uid : Longword) : PVisualGear;
procedure AddClouds;
procedure ChangeToSDClouds;
procedure AddFlakes;
procedure ChangeToSDFlakes;
procedure AddDamageTag(X, Y, Damage, Color: LongWord);
implementation
uses uSound, uMobile, uVariables, uTextures, uRender, Math, uRenderUtils, uStore;
const cExplFrameTicks = 110;
// For better maintainability the step handlers of visual gears are stored
// in a separate file.
{$INCLUDE "VGSHandlers.inc"}
procedure AddDamageTag(X, Y, Damage, Color: LongWord);
var s: shortstring;
Gear: PVisualGear;
begin
if cAltDamage then
begin
Gear:= AddVisualGear(X, Y, vgtSmallDamageTag);
if Gear <> nil then
with Gear^ do
begin
str(Damage, s);
Tex:= RenderStringTex(s, Color, fntSmall);
end
end
end;
// ==================================================================
// ==================================================================
const doStepHandlers: array[TVisualGearType] of TVGearStepProcedure =
(
@doStepFlake,
@doStepCloud,
@doStepExpl,
@doStepExpl,
@doStepFire,
@doStepSmallDamage,
@doStepTeamHealthSorter,
@doStepSpeechBubble,
@doStepBubble,
@doStepSteam,
@doStepAmmo,
@doStepSmoke,
@doStepSmoke,
@doStepShell,
@doStepDust,
@doStepSplash,
@doStepDroplet,
@doStepSmokeRing,
@doStepBeeTrace,
@doStepEgg,
@doStepFeather,
@doStepHealthTag,
@doStepSmokeTrace,
@doStepSmokeTrace,
@doStepExplosion,
@doStepBigExplosion,
@doStepChunk,
@doStepNote,
@doStepLineTrail,
@doStepBulletHit,
@doStepCircle,
@doStepSmoothWindBar,
@doStepStraightShot
);
function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord = 0; Critical: Boolean = false): PVisualGear;
const VGCounter: Longword = 0;
var gear: PVisualGear;
t: Longword;
sp: real;
begin
AddVisualGear:= nil;
if ((GameType = gmtSave) or (fastUntilLag and (GameType = gmtNet))) and // we are scrolling now
((Kind <> vgtCloud) and not Critical) then exit;
if ((cReducedQuality and rqAntiBoom) <> 0) and
not Critical and
not (Kind in
[vgtTeamHealthSorter,
vgtSmallDamageTag,
vgtSpeechBubble,
vgtHealthTag,
vgtExplosion,
vgtSmokeTrace,
vgtEvilTrace,
vgtNote,
vgtSmoothWindBar]) then exit;
inc(VGCounter);
New(gear);
FillChar(gear^, sizeof(TVisualGear), 0);
gear^.X:= real(X);
gear^.Y:= real(Y);
gear^.Kind := Kind;
gear^.doStep:= doStepHandlers[Kind];
gear^.State:= 0;
gear^.Tint:= $FFFFFFFF;
gear^.uid:= VGCounter;
with gear^ do
case Kind of
vgtFlake: begin
Timer:= 0;
tdX:= 0;
tdY:= 0;
if SuddenDeathDmg then
begin
FrameTicks:= random(vobSDFrameTicks);
Frame:= random(vobSDFramesCount);
end
else
begin
FrameTicks:= random(vobFrameTicks);
Frame:= random(vobFramesCount);
end;
Angle:= random * 360;
dx:= 0.0000038654705 * random(10000);
dy:= 0.000003506096 * random(7000);
if random(2) = 0 then dx := -dx;
if SuddenDeathDmg then dAngle:= (random(2) * 2 - 1) * (1 + random) * vobSDVelocity / 1000
else dAngle:= (random(2) * 2 - 1) * (1 + random) * vobVelocity / 1000
end;
vgtCloud: begin
Frame:= random(4);
dx:= 0.5 + 0.1 * random(5); // how much the cloud will be affected by wind
timer:= random(4096);
end;
vgtExplPart,
vgtExplPart2: begin
t:= random(1024);
sp:= 0.001 * (random(95) + 70);
dx:= hwFloat2Float(AngleSin(t)) * sp;
dy:= hwFloat2Float(AngleCos(t)) * sp;
if random(2) = 0 then dx := -dx;
if random(2) = 0 then dy := -dy;
Frame:= 7 - random(3);
FrameTicks:= cExplFrameTicks
end;
vgtFire: begin
t:= random(1024);
sp:= 0.001 * (random(85) + 95);
dx:= hwFloat2Float(AngleSin(t)) * sp;
dy:= hwFloat2Float(AngleCos(t)) * sp;
if random(2) = 0 then dx := -dx;
if random(2) = 0 then dy := -dy;
FrameTicks:= 650 + random(250);
Frame:= random(8)
end;
vgtEgg: begin
t:= random(1024);
sp:= 0.001 * (random(85) + 95);
dx:= hwFloat2Float(AngleSin(t)) * sp;
dy:= hwFloat2Float(AngleCos(t)) * sp;
if random(2) = 0 then dx := -dx;
if random(2) = 0 then dy := -dy;
FrameTicks:= 650 + random(250);
Frame:= 1
end;
vgtShell: FrameTicks:= 500;
vgtSmallDamageTag: begin
gear^.FrameTicks:= 1100
end;
vgtBubble: begin
dx:= 0.0000038654705 * random(10000);
dy:= 0;
if random(2) = 0 then dx := -dx;
FrameTicks:= 250 + random(1751);
Frame:= random(5)
end;
vgtSteam: begin
dx:= 0.0000038654705 * random(10000);
dy:= 0.001 * (random(85) + 95);
if random(2) = 0 then dx := -dx;
Frame:= 7 - random(3);
FrameTicks:= cExplFrameTicks * 2;
end;
vgtAmmo: begin
alpha:= 1.0;
scale:= 1.0
end;
vgtSmokeWhite,
vgtSmoke: begin
Scale:= 1.0;
dx:= 0.0002 * (random(45) + 10);
dy:= 0.0002 * (random(45) + 10);
if random(2) = 0 then dx := -dx;
Frame:= 7 - random(2);
FrameTicks:= cExplFrameTicks * 2;
end;
vgtDust: begin
dx:= 0.005 * (random(15) + 10);
dy:= 0.001 * (random(40) + 20);
if random(2) = 0 then dx := -dx;
Frame:= 7 - random(2);
FrameTicks:= random(20) + 15;
end;
vgtSplash: begin
dx:= 0;
dy:= 0;
FrameTicks:= 740;
Frame:= 19;
end;
vgtDroplet: begin
dx:= 0.001 * (random(75) + 15);
dy:= -0.001 * (random(80) + 120);
if random(2) = 0 then dx := -dx;
FrameTicks:= 250 + random(1751);
Frame:= random(3)
end;
vgtBeeTrace: begin
FrameTicks:= 1000;
Frame:= random(16);
end;
vgtSmokeRing: begin
dx:= 0;
dy:= 0;
FrameTicks:= 600;
Timer:= 0;
Frame:= 0;
scale:= 0.6;
alpha:= 1;
angle:= random(360);
end;
vgtFeather: begin
t:= random(1024);
sp:= 0.001 * (random(85) + 95);
dx:= hwFloat2Float(AngleSin(t)) * sp;
dy:= hwFloat2Float(AngleCos(t)) * sp;
if random(2) = 0 then dx := -dx;
if random(2) = 0 then dy := -dy;
FrameTicks:= 650 + random(250);
Frame:= 1
end;
vgtHealthTag: begin
Frame:= 0;
Timer:= 1500;
dY:= -0.08;
dX:= 0;
//gear^.Z:= 2002;
end;
vgtSmokeTrace,
vgtEvilTrace: begin
gear^.X:= gear^.X - 16;
gear^.Y:= gear^.Y - 16;
gear^.State:= 8;
//gear^.Z:= cSmokeZ
end;
vgtBigExplosion: begin
gear^.Angle:= random(360);
end;
vgtChunk: begin
gear^.Frame:= random(4);
t:= random(1024);
sp:= 0.001 * (random(85) + 47);
dx:= hwFloat2Float(AngleSin(t)) * sp;
dy:= hwFloat2Float(AngleCos(t)) * sp * -2;
if random(2) = 0 then dx := -dx;
end;
vgtNote: begin
dx:= 0.005 * (random(15) + 10);
dy:= -0.001 * (random(40) + 20);
if random(2) = 0 then dx := -dx;
Frame:= random(4);
FrameTicks:= random(2000) + 1500;
end;
vgtBulletHit: begin
dx:= 0;
dy:= 0;
FrameTicks:= 350;
Frame:= 7;
Angle:= 0;
end;
vgtSmoothWindBar: Tag:= hwRound(cWindSpeed * 72 / cMaxWindSpeed);
vgtStraightShot: begin
Angle:= 0;
Scale:= 1.0;
dx:= 0.001 * random(45);
dy:= 0.001 * (random(20) + 25);
State:= ord(sprHealth);
if random(2) = 0 then dx := -dx;
Frame:= 0;
FrameTicks:= random(750) + 1250;
State:= ord(sprSnowDust);
end;
end;
if State <> 0 then gear^.State:= State;
case Gear^.Kind of
// 0: this layer is very distant in the background when stereo
vgtTeamHealthSorter,
vgtSmoothWindBar,
vgtFlake,
vgtCloud: begin
if VisualGearsLayer0 <> nil then
begin
VisualGearsLayer0^.PrevGear:= gear;
gear^.NextGear:= VisualGearsLayer0
end;
gear^.Layer:= 0;
VisualGearsLayer0:= gear
end;
// 1: this layer is on the land level (which is close but behind the screen plane) when stereo
vgtSmokeTrace,
vgtEvilTrace,
vgtLineTrail,
vgtSmoke,
vgtSmokeWhite,
vgtDust,
vgtFire,
vgtSplash,
vgtDroplet,
vgtBubble: begin
if VisualGearsLayer1 <> nil then
begin
VisualGearsLayer1^.PrevGear:= gear;
gear^.NextGear:= VisualGearsLayer1
end;
gear^.Layer:= 1;
VisualGearsLayer1:= gear
end;
// 3: this layer is on the screen plane (depth = 0) when stereo
vgtSpeechBubble,
vgtSmallDamageTag,
vgtHealthTag,
vgtStraightShot,
vgtChunk: begin
if VisualGearsLayer3 <> nil then
begin
VisualGearsLayer3^.PrevGear:= gear;
gear^.NextGear:= VisualGearsLayer3
end;
gear^.Layer:= 3;
VisualGearsLayer3:= gear
end;
// 2: this layer is outside the screen when stereo
vgtExplosion,
vgtBigExplosion,
vgtExplPart,
vgtExplPart2,
vgtSteam,
vgtAmmo,
vgtShell,
vgtFeather,
vgtEgg,
vgtBeeTrace,
vgtSmokeRing,
vgtNote,
vgtBulletHit,
vgtCircle: begin
if VisualGearsLayer2 <> nil then
begin
VisualGearsLayer2^.PrevGear:= gear;
gear^.NextGear:= VisualGearsLayer2
end;
gear^.Layer:= 2;
VisualGearsLayer2:= gear
end;
end;
AddVisualGear:= gear;
end;
procedure DeleteVisualGear(Gear: PVisualGear);
begin
if Gear^.Tex <> nil then
FreeTexture(Gear^.Tex);
Gear^.Tex:= nil;
if Gear^.NextGear <> nil then Gear^.NextGear^.PrevGear:= Gear^.PrevGear;
if Gear^.PrevGear <> nil then Gear^.PrevGear^.NextGear:= Gear^.NextGear
else
case Gear^.Layer of
0: VisualGearsLayer0:= Gear^.NextGear;
1: VisualGearsLayer1:= Gear^.NextGear;
2: VisualGearsLayer2:= Gear^.NextGear;
3: VisualGearsLayer3:= Gear^.NextGear;
end;
if lastVisualGearByUID = Gear then lastVisualGearByUID:= nil;
Dispose(Gear);
end;
procedure ProcessVisualGears(Steps: Longword);
var Gear, t: PVisualGear;
begin
if Steps = 0 then exit;
t:= VisualGearsLayer0;
while t <> nil do
begin
Gear:= t;
t:= Gear^.NextGear;
Gear^.doStep(Gear, Steps)
end;
t:= VisualGearsLayer1;
while t <> nil do
begin
Gear:= t;
t:= Gear^.NextGear;
Gear^.doStep(Gear, Steps)
end;
t:= VisualGearsLayer2;
while t <> nil do
begin
Gear:= t;
t:= Gear^.NextGear;
Gear^.doStep(Gear, Steps)
end;
t:= VisualGearsLayer3;
while t <> nil do
begin
Gear:= t;
t:= Gear^.NextGear;
Gear^.doStep(Gear, Steps)
end
end;
procedure KickFlakes(Radius, X, Y: LongInt);
var Gear, t: PVisualGear;
dmg: LongInt;
begin
if (vobCount = 0) or (vobCount > 200) then exit;
t:= VisualGearsLayer0;
while t <> nil do
begin
Gear:= t;
if Gear^.Kind = vgtFlake then
begin
// Damage calc from doMakeExplosion
dmg:= Min(101, Radius + cHHRadius div 2 - LongInt(abs(round(Gear^.X) - X) + abs(round(Gear^.Y) - Y)) div 5);
if dmg > 1 then
begin
Gear^.tdX:= 0.02 * dmg + 0.01;
if Gear^.X - X < 0 then Gear^.tdX := -Gear^.tdX;
Gear^.tdY:= 0.02 * dmg + 0.01;
if Gear^.Y - Y < 0 then Gear^.tdY := -Gear^.tdY;
Gear^.Timer:= 200
end
end;
t:= Gear^.NextGear
end
end;
procedure DrawVisualGears(Layer: LongWord);
var Gear: PVisualGear;
tinted: boolean;
tmp: real;
i: LongInt;
begin
case Layer of
// this layer is very distant in the background when stereo
0: begin
Gear:= VisualGearsLayer0;
while Gear <> nil do
begin
if Gear^.Tint <> $FFFFFFFF then Tint(Gear^.Tint);
case Gear^.Kind of
vgtFlake: if SuddenDeathDmg then
if vobSDVelocity = 0 then
DrawSprite(sprSDFlake, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy + SkyOffset, Gear^.Frame)
else
DrawRotatedF(sprSDFlake, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy + SkyOffset, Gear^.Frame, 1, Gear^.Angle)
else
if vobVelocity = 0 then
DrawSprite(sprFlake, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy + SkyOffset, Gear^.Frame)
else
DrawRotatedF(sprFlake, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy + SkyOffset, Gear^.Frame, 1, Gear^.Angle);
vgtCloud: if SuddenDeathDmg then
DrawSprite(sprSDCloud, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy + SkyOffset, Gear^.Frame)
else
DrawSprite(sprCloud, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy + SkyOffset, Gear^.Frame);
end;
if Gear^.Tint <> $FFFFFFFF then Tint($FF,$FF,$FF,$FF);
Gear:= Gear^.NextGear
end
end;
// this layer is on the land level (which is close but behind the screen plane) when stereo
1: begin
Gear:= VisualGearsLayer1;
while Gear <> nil do
begin
//tinted:= false;
if Gear^.Tint <> $FFFFFFFF then Tint(Gear^.Tint);
case Gear^.Kind of
vgtSmokeTrace: if Gear^.State < 8 then DrawSprite(sprSmokeTrace, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.State);
vgtEvilTrace: if Gear^.State < 8 then DrawSprite(sprEvilTrace, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.State);
vgtLineTrail: DrawLine(Gear^.X, Gear^.Y, Gear^.dX, Gear^.dY, 1.0, $FF, min(Gear^.Timer, $C0), min(Gear^.Timer, $80), min(Gear^.Timer, $FF));
end;
if (cReducedQuality and rqAntiBoom) = 0 then
case Gear^.Kind of
vgtSmoke: DrawTextureF(SpritesData[sprSmoke].Texture, Gear^.scale, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, 7 - Gear^.Frame, 0, SpritesData[sprSmoke].Width, SpritesData[sprSmoke].Height);
vgtSmokeWhite: DrawSprite(sprSmokeWhite, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame);
vgtDust: if Gear^.State = 1 then
DrawSprite(sprSnowDust, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame)
else
DrawSprite(sprDust, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame);
vgtFire: if (Gear^.State and gstTmpFlag) = 0 then
DrawSprite(sprFlame, round(Gear^.X) + WorldDx - 8, round(Gear^.Y) + WorldDy, (RealTicks shr 6 + Gear^.Frame) mod 8)
else
DrawTextureF(SpritesData[sprFlame].Texture, Gear^.FrameTicks / 900, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, (RealTicks shr 7 + Gear^.Frame) mod 8, 1, 16, 16);
vgtSplash: if SuddenDeathDmg then
DrawSprite(sprSDSplash, round(Gear^.X) + WorldDx - 40, round(Gear^.Y) + WorldDy - 58, 19 - (Gear^.FrameTicks div 37))
else
DrawSprite(sprSplash, round(Gear^.X) + WorldDx - 40, round(Gear^.Y) + WorldDy - 58, 19 - (Gear^.FrameTicks div 37));
vgtDroplet: if SuddenDeathDmg then
DrawSprite(sprSDDroplet, round(Gear^.X) + WorldDx - 8, round(Gear^.Y) + WorldDy - 8, Gear^.Frame)
else
DrawSprite(sprDroplet, round(Gear^.X) + WorldDx - 8, round(Gear^.Y) + WorldDy - 8, Gear^.Frame);
vgtBubble: DrawSprite(sprBubbles, round(Gear^.X) + WorldDx - 8, round(Gear^.Y) + WorldDy - 8, Gear^.Frame);//(RealTicks div 64 + Gear^.Frame) mod 8);
end;
//if (Gear^.Tint <> $FFFFFFFF) or tinted then Tint($FF,$FF,$FF,$FF);
if (Gear^.Tint <> $FFFFFFFF) then Tint($FF,$FF,$FF,$FF);
Gear:= Gear^.NextGear
end
end;
// this layer is on the screen plane (depth = 0) when stereo
3: begin
Gear:= VisualGearsLayer3;
while Gear <> nil do
begin
tinted:= false;
if Gear^.Tint <> $FFFFFFFF then Tint(Gear^.Tint);
case Gear^.Kind of
vgtSpeechBubble: begin
if (Gear^.Tex <> nil) and (((Gear^.State = 0) and (Gear^.Hedgehog^.Team <> CurrentTeam)) or (Gear^.State = 1)) then
begin
tinted:= true;
Tint($FF, $FF, $FF, $66);
DrawCentered(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Tex)
end
else if (Gear^.Tex <> nil) and (((Gear^.State = 0) and (Gear^.Hedgehog^.Team = CurrentTeam)) or (Gear^.State = 2)) then
DrawCentered(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Tex);
end;
vgtSmallDamageTag: DrawCentered(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Tex);
vgtHealthTag: if Gear^.Tex <> nil then
begin
if Gear^.Frame = 0 then
DrawCentered(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Tex)
else
begin
SetScale(cDefaultZoomLevel);
if Gear^.Angle = 0 then DrawTexture(round(Gear^.X), round(Gear^.Y), Gear^.Tex)
else DrawTexture(round(Gear^.X), round(Gear^.Y), Gear^.Tex, Gear^.Angle);
SetScale(zoom)
end
end;
vgtStraightShot: begin
if Gear^.dX < 0 then i:= -1 else i:= 1;
DrawRotatedTextureF(SpritesData[TSprite(Gear^.State)].Texture, Gear^.Scale, 0, 0, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Frame, i, SpritesData[TSprite(Gear^.State)].Width, SpritesData[TSprite(Gear^.State)].Height, Gear^.Angle);
end;
end;
if (cReducedQuality and rqAntiBoom) = 0 then
case Gear^.Kind of
vgtChunk: DrawRotatedF(sprChunk, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Frame, 1, Gear^.Angle);
end;
if (Gear^.Tint <> $FFFFFFFF) or tinted then Tint($FF,$FF,$FF,$FF);
Gear:= Gear^.NextGear
end
end;
// this layer is outside the screen when stereo
2: begin
Gear:= VisualGearsLayer2;
while Gear <> nil do
begin
tinted:= false;
if Gear^.Tint <> $FFFFFFFF then Tint(Gear^.Tint);
case Gear^.Kind of
vgtExplosion: DrawSprite(sprExplosion50, round(Gear^.X) - 32 + WorldDx, round(Gear^.Y) - 32 + WorldDy, Gear^.State);
vgtBigExplosion: begin
tinted:= true;
Tint($FF, $FF, $FF, round($FF * (1 - power(Gear^.Timer / 250, 4))));
DrawRotatedTextureF(SpritesData[sprBigExplosion].Texture, 0.85 * (-power(2, -10 * Int(Gear^.Timer)/250) + 1) + 0.4, 0, 0, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, 0, 1, 385, 385, Gear^.Angle);
end;
end;
if (cReducedQuality and rqAntiBoom) = 0 then
case Gear^.Kind of
vgtExplPart: DrawSprite(sprExplPart, round(Gear^.X) + WorldDx - 16, round(Gear^.Y) + WorldDy - 16, 7 - Gear^.Frame);
vgtExplPart2: DrawSprite(sprExplPart2, round(Gear^.X) + WorldDx - 16, round(Gear^.Y) + WorldDy - 16, 7 - Gear^.Frame);
vgtSteam: DrawSprite(sprSmokeWhite, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame);
vgtAmmo: begin
tinted:= true;
Tint($FF, $FF, $FF, round(Gear^.alpha * $FF));
DrawTextureF(ropeIconTex, Gear^.scale, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, 0, 1, 32, 32);
DrawTextureF(SpritesData[sprAMAmmos].Texture, Gear^.scale * 0.90, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Frame - 1, 1, 32, 32);
end;
vgtShell: begin
if Gear^.FrameTicks < $FF then
begin
Tint($FF, $FF, $FF, Gear^.FrameTicks);
tinted:= true
end;
DrawRotatedF(sprShell, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Frame, 1, Gear^.Angle);
end;
vgtFeather: begin
if Gear^.FrameTicks < 255 then
begin
Tint($FF, $FF, $FF, Gear^.FrameTicks);
tinted:= true
end;
DrawRotatedF(sprFeather, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Frame, 1, Gear^.Angle);
end;
vgtEgg: DrawRotatedF(sprEgg, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Frame, 1, Gear^.Angle);
vgtBeeTrace: begin
if Gear^.FrameTicks < $FF then
Tint($FF, $FF, $FF, Gear^.FrameTicks div 2)
else
Tint($FF, $FF, $FF, $80);
tinted:= true;
DrawRotatedF(sprBeeTrace, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Frame, 1, (RealTicks shr 4) mod cMaxAngle);
end;
vgtSmokeRing: begin
tinted:= true;
Tint($FF, $FF, $FF, round(Gear^.alpha * $FF));
DrawRotatedTextureF(SpritesData[sprSmokeRing].Texture, Gear^.scale, 0, 0, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, 0, 1, 200, 200, Gear^.Angle);
end;
vgtNote: DrawRotatedF(sprNote, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Frame, 1, Gear^.Angle);
vgtBulletHit: DrawRotatedF(sprBulletHit, round(Gear^.X) + WorldDx - 0, round(Gear^.Y) + WorldDy - 0, 7 - (Gear^.FrameTicks div 50), 1, Gear^.Angle);
end;
case Gear^.Kind of
vgtCircle: if gear^.Angle = 1 then
begin
tmp:= Gear^.State / 100;
DrawTexture(round(Gear^.X-24*tmp) + WorldDx, round(Gear^.Y-24*tmp) + WorldDy, SpritesData[sprVampiric].Texture, tmp)
end
else DrawCircle(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.State, Gear^.Timer);
end;
if (Gear^.Tint <> $FFFFFFFF) or tinted then Tint($FF,$FF,$FF,$FF);
Gear:= Gear^.NextGear
end
end
end;
end;
function VisualGearByUID(uid : Longword) : PVisualGear;
var vg: PVisualGear;
begin
VisualGearByUID:= nil;
if uid = 0 then exit;
if (lastVisualGearByUID <> nil) and (lastVisualGearByUID^.uid = uid) then
begin
VisualGearByUID:= lastVisualGearByUID;
exit
end;
vg:= VisualGearsLayer0;
while vg <> nil do
begin
if vg^.uid = uid then
begin
lastVisualGearByUID:= vg;
VisualGearByUID:= vg;
exit
end;
vg:= vg^.NextGear
end;
vg:= VisualGearsLayer1;
while vg <> nil do
begin
if vg^.uid = uid then
begin
lastVisualGearByUID:= vg;
VisualGearByUID:= vg;
exit
end;
vg:= vg^.NextGear
end;
vg:= VisualGearsLayer2;
while vg <> nil do
begin
if vg^.uid = uid then
begin
lastVisualGearByUID:= vg;
VisualGearByUID:= vg;
exit
end;
vg:= vg^.NextGear
end;
vg:= VisualGearsLayer3;
while vg <> nil do
begin
if vg^.uid = uid then
begin
lastVisualGearByUID:= vg;
VisualGearByUID:= vg;
exit
end;
vg:= vg^.NextGear
end
end;
procedure AddClouds;
var i: LongInt;
begin
for i:= 0 to cCloudsNumber - 1 do
AddVisualGear(cLeftScreenBorder + i * LongInt(cScreenSpace div (cCloudsNumber + 1)), LAND_HEIGHT-1184, vgtCloud)
end;
procedure ChangeToSDClouds;
var i: LongInt;
vg, tmp: PVisualGear;
begin
if cCloudsNumber = cSDCloudsNumber then exit;
vg:= VisualGearsLayer0;
while vg <> nil do
if vg^.Kind = vgtCloud then
begin
tmp:= vg^.NextGear;
DeleteVisualGear(vg);
vg:= tmp
end
else vg:= vg^.NextGear;
for i:= 0 to cSDCloudsNumber - 1 do
AddVisualGear(cLeftScreenBorder + i * LongInt(cScreenSpace div (cSDCloudsNumber + 1)), LAND_HEIGHT-1184, vgtCloud)
end;
procedure AddFlakes;
var i: LongInt;
begin
if (cReducedQuality and rqKillFlakes) <> 0 then exit;
if ((GameFlags and gfBorder) <> 0) or ((Theme <> 'Snow') and (Theme <> 'Christmas')) then
for i:= 0 to Pred(vobCount * cScreenSpace div LAND_WIDTH) do
AddVisualGear(cLeftScreenBorder + random(cScreenSpace), random(1024+200) - 100 + LAND_HEIGHT, vgtFlake)
else
for i:= 0 to Pred((vobCount * cScreenSpace div LAND_WIDTH) div 3) do
AddVisualGear(cLeftScreenBorder + random(cScreenSpace), random(1024+200) - 100 + LAND_HEIGHT, vgtFlake);
end;
procedure ChangeToSDFlakes;
var i: LongInt;
vg, tmp: PVisualGear;
begin
if (cReducedQuality and rqKillFlakes) <> 0 then exit;
if vobCount = vobSDCount then exit;
vg:= VisualGearsLayer0;
while vg <> nil do
if vg^.Kind = vgtFlake then
begin
tmp:= vg^.NextGear;
DeleteVisualGear(vg);
vg:= tmp
end
else vg:= vg^.NextGear;
if ((GameFlags and gfBorder) <> 0) or ((Theme <> 'Snow') and (Theme <> 'Christmas')) then
for i:= 0 to Pred(vobSDCount * cScreenSpace div LAND_WIDTH) do
AddVisualGear(cLeftScreenBorder + random(cScreenSpace), random(1024+200) - 100 + LAND_HEIGHT, vgtFlake)
else
for i:= 0 to Pred((vobSDCount * cScreenSpace div LAND_WIDTH) div 3) do
AddVisualGear(cLeftScreenBorder + random(cScreenSpace), random(1024+200) - 100 + LAND_HEIGHT, vgtFlake);
end;
procedure initModule;
begin
VisualGearsLayer0:= nil;
VisualGearsLayer1:= nil;
VisualGearsLayer2:= nil;
VisualGearsLayer3:= nil;
end;
procedure freeModule;
begin
while VisualGearsLayer0 <> nil do DeleteVisualGear(VisualGearsLayer0);
while VisualGearsLayer1 <> nil do DeleteVisualGear(VisualGearsLayer1);
while VisualGearsLayer2 <> nil do DeleteVisualGear(VisualGearsLayer2);
while VisualGearsLayer3 <> nil do DeleteVisualGear(VisualGearsLayer3);
end;
end.