FREE AT LAST!!! SDL came around a (mostly) sane way for implementing rotation events, so we can scrap all the workaround code that has been added to workaround it!! Also this allows us to use proper (internal) multitasking handling and can simplify optional settings and other yet unexplored features. Yay!
(* * 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. *)interfaceuses uConsts, uFloat, GLunit, uTypes;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);implementationuses 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;beginif 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 endend;// ==================================================================// ==================================================================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;beginAddVisualGear:= 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 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 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;beginif 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) endend;procedure KickFlakes(Radius, X, Y: LongInt);var Gear, t: PVisualGear; dmg: LongInt;beginif (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 endend;procedure DrawVisualGears(Layer: LongWord);var Gear: PVisualGear; tinted: boolean; tmp: real;begincase 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: DrawSprite(sprSmoke, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame); 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; //if Ger^.Tex <> nil then DrawCentered(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Tex); vgtStraightShot: DrawRotatedF(TSprite(Gear^.State), round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Frame, 1, Gear^.Angle); 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: begin if Gear^.FrameTicks < $FF then begin Tint($FF, $FF, $FF, Gear^.FrameTicks); tinted:= true end; DrawRotatedF(sprEgg, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Frame, 1, Gear^.Angle); end; 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;beginVisualGearByUID:= 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 endend;procedure AddClouds;var i: LongInt;beginfor 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;beginif 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;beginif (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;beginif (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.