1 (* Copyright (C) DooM 2D:Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 {$INCLUDE ../shared/a_modes.inc}
38 MARK_BLOCKED
= MARK_WALL
+ MARK_DOOR
;
39 MARK_LIQUID
= MARK_WATER
+ MARK_ACID
;
40 MARK_LIFT
= MARK_LIFTDOWN
+ MARK_LIFTUP
+ MARK_LIFTLEFT
+ MARK_LIFTRIGHT
;
42 procedure g_GFX_Init();
43 procedure g_GFX_Free();
45 procedure g_GFX_Blood(fX
, fY
: Integer; Count
: Word; vx
, vy
: Integer;
46 DevX
, DevY
: Word; CR
, CG
, CB
: Byte; Kind
: Byte = BLOOD_NORMAL
);
47 procedure g_GFX_Spark(fX
, fY
: Integer; Count
: Word; Angle
: SmallInt; DevX
, DevY
: Byte);
48 procedure g_GFX_Water(fX
, fY
: Integer; Count
: Word; fVelX
, fVelY
: Single; DevX
, DevY
, Color
: Byte);
49 procedure g_GFX_SimpleWater(fX
, fY
: Integer; Count
: Word; fVelX
, fVelY
: Single; DefColor
, CR
, CG
, CB
: Byte);
50 procedure g_GFX_Bubbles(fX
, fY
: Integer; Count
: Word; DevX
, DevY
: Byte);
51 procedure g_GFX_SetMax(Count
: Integer);
52 function g_GFX_GetMax(): Integer;
54 procedure g_GFX_OnceAnim(X
, Y
: Integer; Anim
: TAnimation
; AnimType
: Byte = 0);
56 procedure g_Mark(x
, y
, Width
, Height
: Integer; t
: Byte; st
: Boolean);
58 procedure g_GFX_Update();
59 procedure g_GFX_Draw();
63 gpart_dbg_enabled
: Boolean = true;
64 gpart_dbg_phys_enabled
: Boolean = true;
70 g_map
, g_basic
, Math
, e_graphics
, GL
, GLExt
,
71 g_options
, g_console
, SysUtils
, g_triggers
, MAPDEF
,
72 g_game
, g_language
, g_net
;
75 PParticle
= ^TParticle
;
80 AccelX
, AccelY
: Single;
81 Red
, Green
, Blue
: Byte;
86 offsetX
, offsetY
: ShortInt;
88 liquidTopY
: Integer; // don't float higher than this
90 //k8: sorry, i have to emulate virtual methods this way, 'cause i haet `Object`
92 procedure thinkerBlood ();
93 procedure thinkerSpark ();
94 procedure thinkerBubble ();
95 procedure thinkerWater ();
97 function alive (): Boolean; inline;
98 procedure die (); inline;
99 procedure think (); inline;
105 Animation
: TAnimation
;
111 PARTICLE_BUBBLES
= 2;
118 Particles
: array of TParticle
;
119 OnceAnims
: array of TOnceAnim
;
120 MaxParticles
: Integer;
121 CurrentParticle
: Integer;
124 // ////////////////////////////////////////////////////////////////////////// //
125 function TParticle
.alive (): Boolean; inline; begin result
:= (State
<> STATE_FREE
); end;
126 procedure TParticle
.die (); inline; begin State
:= STATE_FREE
; end;
128 procedure TParticle
.think (); inline;
131 PARTICLE_BLOOD
: thinkerBlood();
132 PARTICLE_SPARK
: thinkerSpark();
133 PARTICLE_BUBBLES
: thinkerBubble();
134 PARTICLE_WATER
: thinkerWater();
139 // ////////////////////////////////////////////////////////////////////////// //
140 function isBlockedAt (x
, y
: Integer): Boolean; inline;
142 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
143 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
147 function isWallAt (x
, y
: Integer): Boolean; inline;
149 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
150 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, (PANEL_WALL
or PANEL_STEP
));
153 function isLiftUpAt (x
, y
: Integer): Boolean; inline;
155 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
156 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, PANEL_LIFTUP
);
159 function isLiftDownAt (x
, y
: Integer): Boolean; inline;
161 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
162 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, PANEL_LIFTDOWN
);
165 function isLiftLeftAt (x
, y
: Integer): Boolean; inline;
167 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
168 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, PANEL_LIFTLEFT
);
171 function isLiftRightAt (x
, y
: Integer): Boolean; inline;
173 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
174 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, PANEL_LIFTRIGHT
);
177 function isLiquidAt (x
, y
: Integer): Boolean; inline;
179 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
180 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, (PANEL_WATER
or PANEL_ACID1
or PANEL_ACID2
));
183 function isAnythingAt (x
, y
: Integer): Boolean; inline;
185 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
186 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_OPENDOOR
or PANEL_WATER
or PANEL_ACID1
or PANEL_ACID2
or PANEL_STEP
or PANEL_LIFTUP
or PANEL_LIFTDOWN
or PANEL_LIFTLEFT
or PANEL_LIFTRIGHT
));
190 procedure g_Mark(x
, y
, Width
, Height
: Integer; t
: Byte; st
: Boolean);
191 {$IF not DEFINED(HAS_COLLIDE_BITMAP)}
196 yy
, y2
, xx
, x2
: Integer;
209 Height
:= Height
+ y
;
216 if x
> gMapInfo
.Width
then
218 if y
> gMapInfo
.Height
then
221 y2
:= y
+ Height
- 1;
222 if y2
> gMapInfo
.Height
then
223 y2
:= gMapInfo
.Height
;
226 if x2
> gMapInfo
.Width
then
227 x2
:= gMapInfo
.Width
;
230 begin // Óñòàíîâèòü ïðèçíàê
233 gCollideMap
[yy
][xx
] := gCollideMap
[yy
][xx
] or t
;
236 begin // Óáðàòü ïðèçíàê
240 gCollideMap
[yy
][xx
] := gCollideMap
[yy
][xx
] and t
;
246 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
247 procedure CreateCollideMap();
251 g_Game_SetLoadingText(_lc
[I_LOAD_COLLIDE_MAP
]+' 1/6', 0, False);
252 SetLength(gCollideMap
, gMapInfo
.Height
+1);
253 for a
:= 0 to High(gCollideMap
) do
254 SetLength(gCollideMap
[a
], gMapInfo
.Width
+1);
256 if gWater
<> nil then
258 g_Game_SetLoadingText(_lc
[I_LOAD_COLLIDE_MAP
]+' 2/6', 0, True);
259 for a
:= 0 to High(gWater
) do
261 g_Mark(X
, Y
, Width
, Height
, MARK_WATER
, True);
264 if gAcid1
<> nil then
266 g_Game_SetLoadingText(_lc
[I_LOAD_COLLIDE_MAP
]+' 3/6', 0, True);
267 for a
:= 0 to High(gAcid1
) do
269 g_Mark(X
, Y
, Width
, Height
, MARK_ACID
, True);
272 if gAcid2
<> nil then
274 g_Game_SetLoadingText(_lc
[I_LOAD_COLLIDE_MAP
]+' 4/6', 0, True);
275 for a
:= 0 to High(gAcid2
) do
277 g_Mark(X
, Y
, Width
, Height
, MARK_ACID
, True);
280 if gLifts
<> nil then
282 g_Game_SetLoadingText(_lc
[I_LOAD_COLLIDE_MAP
]+' 5/6', 0, True);
283 for a
:= 0 to High(gLifts
) do
286 g_Mark(X
, Y
, Width
, Height
, MARK_LIFT
, False);
289 g_Mark(X
, Y
, Width
, Height
, MARK_LIFTUP
, True)
290 else if LiftType
= 1 then
291 g_Mark(X
, Y
, Width
, Height
, MARK_LIFTDOWN
, True)
292 else if LiftType
= 2 then
293 g_Mark(X
, Y
, Width
, Height
, MARK_LIFTLEFT
, True)
294 else if LiftType
= 3 then
295 g_Mark(X
, Y
, Width
, Height
, MARK_LIFTRIGHT
, True)
299 if gWalls
<> nil then
301 g_Game_SetLoadingText(_lc
[I_LOAD_COLLIDE_MAP
]+' 6/6', 0, True);
302 for a
:= 0 to High(gWalls
) do
304 if gWalls
[a
].Door
then
307 if gWalls
[a
].Enabled
then
309 g_Mark(X
, Y
, Width
, Height
, MARK_DOOR
, True)
310 else // Îòêðûòàÿ äâåðü:
311 if gWalls
[a
].Enabled
then
313 g_Mark(X
, Y
, Width
, Height
, MARK_DOOR
, False);
317 g_Mark(X
, Y
, Width
, Height
, MARK_WALL
, True);
324 procedure g_GFX_Init();
326 //CreateCollideMap();
330 procedure g_GFX_Free();
335 SetLength(Particles
, MaxParticles
);
336 for a
:= 0 to High(Particles
) do Particles
[a
].die();
337 CurrentParticle
:= 0;
339 if OnceAnims
<> nil then
341 for a
:= 0 to High(OnceAnims
) do
342 OnceAnims
[a
].Animation
.Free();
350 procedure CorrectOffsets(id: Integer); inline;
354 part := @Particles[id];
357 // check for upper wall
358 if isBlockedAt(part.X, part.Y-1) then part.offsetY := 1;
359 // check for left wall
360 if isBlockedAt(part.X-1, part.Y) then part.offsetX := 1;
365 // ////////////////////////////////////////////////////////////////////////// //
366 procedure TParticle
.thinkerBlood ();
374 h
:= gMapInfo
.Height
;
378 if (State
= STATE_STICK
) then
380 if (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
381 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) and
382 (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
383 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED))
386 if (not isBlockedAt(X
, Y
-1)) and
387 (not isBlockedAt(X
, Y
+1)) and
388 (not isBlockedAt(X
-1, Y
)) and
389 (not isBlockedAt(X
+1, Y
))
391 begin // Îòëèïëà - êàïàåò
394 State
:= STATE_NORMAL
;
397 if Random(200) = 100 then
398 begin // Ïðèëåïëåíà - íî âîçìîæíî ñòåêàåò
404 if not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
406 if isLiftUpAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
408 if VelY
> -4-Random(3) then
410 if Abs(VelX
) > 0.1 then
411 VelX
:= VelX
- VelX
/10.0;
412 VelX
:= VelX
+ (Random
-Random
)*0.2;
415 if isLiftLeftAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
417 if VelX
> -8-Random(3) then
421 if isLiftRightAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
422 begin // Ïîòîê âïðàâî
423 if VelX
< 8+Random(3) then
432 if (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) then
433 if (State
<> STATE_STICK
) and
434 (not isBlockedAt(X
, Y
-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
435 (not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
436 (not isBlockedAt(X
, Y
+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
437 begin // Âèñèò â âîçäóõå - êàïàåò
440 State
:= STATE_NORMAL
;
454 if (X
+s
>= w
) or (X
+s
<= 0) then begin die(); break
; end;
456 //c := gCollideMap[Y, X+s];
458 if isBlockedAt(X
+s
, Y
) {ByteBool(c and MARK_BLOCKED)} then
464 State
:= STATE_STICK
;
483 if (Y
+s
>= h
) or (Y
+s
<= 0) then begin die(); break
; end;
485 //c := gCollideMap[Y+s, X];
487 if isBlockedAt(X
, Y
+s
) {ByteBool(c and MARK_BLOCKED)} then
493 if (s
> 0) and (State
<> STATE_STICK
) then
494 State
:= STATE_NORMAL
496 State
:= STATE_STICK
;
509 if (X
+dX
>= w
) or (Y
+dY
>= h
) or
510 (X
+dX
<= 0) or (Y
+dY
<= 0) or
511 isBlockedAt(X
+dX
, Y
+dY
) {ByteBool(gCollideMap[Y+dY, X+dX] and MARK_BLOCKED)} then
512 begin // Ñòåíà/äâåðü/ãðàíèöà
524 VelX
:= VelX
+ AccelX
;
525 VelY
:= VelY
+ AccelY
;
527 // Êðîâü ðàñòâîðÿåòñÿ â æèäêîñòè:
528 if isLiquidAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
532 Alpha
:= 255 - Trunc((255.0 * Time
) / LiveTime
);
537 // ////////////////////////////////////////////////////////////////////////// //
538 procedure TParticle
.thinkerSpark ();
546 h
:= gMapInfo
.Height
;
551 if (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) and
552 (not isBlockedAt(X
, Y
-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
553 (not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
554 (not isBlockedAt(X
, Y
+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
555 begin // Âèñèò â âîçäóõå
571 if (X
+s
>= w
) or (X
+s
<= 0) then begin die(); break
; end;
573 //c := gCollideMap[Y, X+s];
575 if isBlockedAt(X
+s
, Y
) {ByteBool(c and MARK_BLOCKED)} then
576 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
582 if not isAnythingAt(X
+s
, Y
) {c = MARK_FREE} then
603 if (Y
+s
>= h
) or (Y
+s
<= 0) then begin die(); break
; end;
605 //c := gCollideMap[Y+s, X];
607 if isBlockedAt(X
, Y
+s
) {ByteBool(c and MARK_BLOCKED)} then
608 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
612 AccelY
:= Abs(AccelY
);
614 else // Èëè íå ïàäàåò
625 if not isAnythingAt(X
, Y
+s
) {c = MARK_FREE} then
636 VelX
:= VelX
+ AccelX
;
640 AccelY
:= AccelY
+ 0.08;
641 VelY
:= VelY
+ AccelY
;
648 // ////////////////////////////////////////////////////////////////////////// //
649 procedure TParticle
.thinkerBubble ();
656 h
:= gMapInfo
.Height
;
667 for b
:= 1 to Abs(dY
) do
669 if (Y
+s
>= h
) or (Y
+s
<= 0) then begin die(); break
; end;
672 if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
673 begin // Óæå íå æèäêîñòü
678 // we traced liquid before, so don't bother checking
679 if (Y
+s
<= liquidTopY
) then begin die(); break
; end;
686 VelY
:= VelY
+ AccelY
;
692 // ////////////////////////////////////////////////////////////////////////// //
693 procedure TParticle
.thinkerWater ();
701 h
:= gMapInfo
.Height
;
703 if (State
= STATE_STICK
) and (Random(30) = 15) then
704 begin // Ñòåêàåò/îòëèïàåò
707 if (not isBlockedAt(X
-1, Y
) {ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)}) and
708 (not isBlockedAt(X
+1, Y
) {ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)}) then
709 State
:= STATE_NORMAL
;
713 if not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
715 if isLiftUpAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
717 if VelY
> -4-Random(3) then
719 if Abs(VelX
) > 0.1 then
720 VelX
:= VelX
- VelX
/10.0;
721 VelX
:= VelX
+ (Random
-Random
)*0.2;
724 if isLiftLeftAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
726 if VelX
> -8-Random(3) then
730 if isLiftRightAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
731 begin // Ïîòîê âïðàâî
732 if VelX
< 8+Random(3) then
741 if (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) then
742 if (State
<> STATE_STICK
) and
743 (not isBlockedAt(X
, Y
-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
744 (not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
745 (not isBlockedAt(X
, Y
+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
746 begin // Âèñèò â âîçäóõå - êàïàåò
749 State
:= STATE_NORMAL
;
759 for b
:= 1 to Abs(dX
) do
762 if (X
+s
>= w
) or (X
+s
<= 0) then begin die(); break
;end;
764 //c := gCollideMap[Y, X+s];
766 // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò?
767 if isLiquidAt(X
+s
, Y
) {ByteBool(c and MARK_LIQUID)} and (dY
> 0) then begin die(); break
; end;
769 if isBlockedAt(X
+s
, Y
) {ByteBool(c and MARK_BLOCKED)} then
775 State
:= STATE_STICK
;
790 for b
:= 1 to Abs(dY
) do
792 // Ñíèçó/ñâåðõó ãðàíèöà
793 if (Y
+s
>= h
) or (Y
+s
<= 0) then begin die(); break
; end;
795 //c := gCollideMap[Y+s, X];
797 // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
798 if isLiquidAt(X
, Y
+s
) {ByteBool(c and MARK_LIQUID)} and (dY
> 0) then begin die(); break
; end;
800 if isBlockedAt(X
, Y
+s
) {ByteBool(c and MARK_BLOCKED)} then
806 if (s
> 0) and (State
<> STATE_STICK
) then
807 State
:= STATE_NORMAL
809 State
:= STATE_STICK
;
817 VelX
:= VelX
+ AccelX
;
818 VelY
:= VelY
+ AccelY
;
824 // ////////////////////////////////////////////////////////////////////////// //
825 procedure g_GFX_SparkVel (fX
, fY
: Integer; Count
: Word; VX
, VY
: Integer; DevX
, DevY
: Byte);
832 l
:= Length(Particles
);
834 if Count
> l
then Count
:= l
;
841 for a
:= 1 to Count
do
843 with Particles
[CurrentParticle
] do
845 X
:= fX
-DevX1
+Random(DevX2
);
846 Y
:= fY
-DevY1
+Random(DevY2
);
848 VelX
:= VX
+ (Random
-Random
)*3;
849 VelY
:= VY
+ (Random
-Random
)*3;
857 AccelX
:= -Sign(VelX
)*Random
/100;
861 Green
:= 100+Random(155);
865 State
:= STATE_NORMAL
;
867 LiveTime
:= 30+Random(60);
868 ParticleType
:= PARTICLE_SPARK
;
870 {CorrectOffsets(CurrentParticle);}
873 if CurrentParticle
+2 > MaxParticles
then
876 CurrentParticle
:= CurrentParticle
+1;
881 procedure g_GFX_Blood(fX
, fY
: Integer; Count
: Word; vx
, vy
: Integer;
882 DevX
, DevY
: Word; CR
, CG
, CB
: Byte; Kind
: Byte = BLOOD_NORMAL
);
891 if Kind
= BLOOD_SPARKS
then
893 g_GFX_SparkVel(fX
, fY
, 2 + Random(2), -VX
div 2, -VY
div 2, DevX
, DevY
);
896 l
:= Length(Particles
);
907 for a
:= 1 to Count
do
909 with Particles
[CurrentParticle
] do
911 X
:= fX
- DevX1
+ Random(DevX2
);
912 Y
:= fY
- DevY1
+ Random(DevY2
);
915 if (X < 0) or (X > gMapInfo.Width-1) or
916 (Y < 0) or (Y > gMapInfo.Height-1) or
917 ByteBool(gCollideMap[Y, X] and MARK_WALL) then
920 if isWallAt(X
, Y
) then continue
;
922 VelX
:= vx
+ (Random
-Random
)*3;
923 VelY
:= vy
+ (Random
-Random
)*3;
931 AccelX
:= -Sign(VelX
)*Random
/100;
934 CRnd
:= 20*Random(6);
937 CC
:= CR
+ CRnd
- 50;
938 if CC
< 0 then CC
:= 0;
939 if CC
> 255 then CC
:= 255;
945 CC
:= CG
+ CRnd
- 50;
946 if CC
< 0 then CC
:= 0;
947 if CC
> 255 then CC
:= 255;
953 CC
:= CB
+ CRnd
- 50;
954 if CC
< 0 then CC
:= 0;
955 if CC
> 255 then CC
:= 255;
962 State
:= STATE_NORMAL
;
964 LiveTime
:= 120+Random(40);
965 ParticleType
:= PARTICLE_BLOOD
;
967 {CorrectOffsets(CurrentParticle);}
970 if CurrentParticle
>= MaxParticles
-1 then
973 CurrentParticle
:= CurrentParticle
+1;
978 procedure g_GFX_Spark(fX
, fY
: Integer; Count
: Word; Angle
: SmallInt; DevX
, DevY
: Byte);
984 BaseVelX
, BaseVelY
: Single;
987 l
:= Length(Particles
);
993 Angle
:= 360 - Angle
;
1000 b
:= DegToRad(Angle
);
1002 BaseVelY
:= 1.6*sin(b
);
1003 if Abs(BaseVelX
) < 0.01 then
1005 if Abs(BaseVelY
) < 0.01 then
1007 for a
:= 1 to Count
do
1009 with Particles
[CurrentParticle
] do
1011 X
:= fX
-DevX1
+Random(DevX2
);
1012 Y
:= fY
-DevY1
+Random(DevY2
);
1014 VelX
:= BaseVelX
*Random
;
1015 VelY
:= BaseVelY
-Random
;
1020 Green
:= 100+Random(155);
1024 State
:= STATE_NORMAL
;
1026 LiveTime
:= 30+Random(60);
1027 ParticleType
:= PARTICLE_SPARK
;
1029 {CorrectOffsets(CurrentParticle);}
1032 if CurrentParticle
+2 > MaxParticles
then
1033 CurrentParticle
:= 0
1035 CurrentParticle
:= CurrentParticle
+1;
1039 procedure g_GFX_Water(fX
, fY
: Integer; Count
: Word; fVelX
, fVelY
: Single; DevX
, DevY
, Color
: Byte);
1046 l
:= Length(Particles
);
1052 if Abs(fVelX
) < 3.0 then
1053 fVelX
:= 3.0 - 6.0*Random
;
1055 DevX1
:= DevX
div 2;
1057 DevY1
:= DevY
div 2;
1060 for a
:= 1 to Count
do
1062 with Particles
[CurrentParticle
] do
1064 X
:= fX
-DevX1
+Random(DevX2
);
1065 Y
:= fY
-DevY1
+Random(DevY2
);
1067 if Abs(fVelX
) < 0.5 then
1068 VelX
:= 1.0 - 2.0*Random
1070 VelX
:= fVelX
*Random
;
1071 if Random(10) < 7 then
1073 VelY
:= fVelY
*Random
;
1080 Red
:= 155 + Random(9)*10;
1081 Green
:= Trunc(150*Random
);
1086 Red
:= Trunc(150*Random
);
1087 Green
:= 175 + Random(9)*10;
1092 Red
:= Trunc(200*Random
);
1094 Blue
:= 175 + Random(9)*10;
1098 Red
:= 90 + Random(12)*10;
1106 State
:= STATE_NORMAL
;
1108 LiveTime
:= 60+Random(60);
1109 ParticleType
:= PARTICLE_WATER
;
1111 {CorrectOffsets(CurrentParticle);}
1114 if CurrentParticle
+2 > MaxParticles
then
1115 CurrentParticle
:= 0
1117 CurrentParticle
:= CurrentParticle
+1;
1121 procedure g_GFX_SimpleWater(fX
, fY
: Integer; Count
: Word; fVelX
, fVelY
: Single; DefColor
, CR
, CG
, CB
: Byte);
1126 l
:= Length(Particles
);
1132 for a
:= 1 to Count
do
1134 with Particles
[CurrentParticle
] do
1147 Red
:= 155 + Random(9)*10;
1148 Green
:= Trunc(150*Random
);
1153 Red
:= Trunc(150*Random
);
1154 Green
:= 175 + Random(9)*10;
1159 Red
:= Trunc(200*Random
);
1161 Blue
:= 175 + Random(9)*10;
1163 4: // Ñâîé öâåò, ñâåòëåå
1165 Red
:= 20 + Random(19)*10;
1168 Red
:= Min(Red
+ CR
, 255);
1169 Green
:= Min(Green
+ CG
, 255);
1170 Blue
:= Min(Blue
+ CB
, 255);
1172 5: // Ñâîé öâåò, òåìíåå
1174 Red
:= 20 + Random(19)*10;
1177 Red
:= Max(CR
- Red
, 0);
1178 Green
:= Max(CG
- Green
, 0);
1179 Blue
:= Max(CB
- Blue
, 0);
1183 Red
:= 90 + Random(12)*10;
1191 State
:= STATE_NORMAL
;
1193 LiveTime
:= 60+Random(60);
1194 ParticleType
:= PARTICLE_WATER
;
1196 {CorrectOffsets(CurrentParticle);}
1199 if CurrentParticle
+2 > MaxParticles
then
1200 CurrentParticle
:= 0
1202 CurrentParticle
:= CurrentParticle
+1;
1207 procedure g_GFX_Bubbles(fX
, fY
: Integer; Count
: Word; DevX
, DevY
: Byte);
1212 l
, liquidx
: Integer;
1214 l
:= Length(Particles
);
1220 DevX1
:= DevX
div 2;
1222 DevY1
:= DevY
div 2;
1225 for a
:= 1 to Count
do
1227 with Particles
[CurrentParticle
] do
1229 X
:= fX
-DevX1
+Random(DevX2
);
1230 Y
:= fY
-DevY1
+Random(DevY2
);
1232 if (X
>= gMapInfo
.Width
) or (X
<= 0) or
1233 (Y
>= gMapInfo
.Height
) or (Y
<= 0) then
1237 // don't spawn bubbles outside of the liquid
1238 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1242 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1243 // tracer will return `false` if we started outside of the liquid
1244 if not g_Map_TraceLiquid(X
, Y
, 0, -8, liquidx
, liquidTopY
) then continue
;
1256 State
:= STATE_NORMAL
;
1259 ParticleType
:= PARTICLE_BUBBLES
;
1261 {CorrectOffsets(CurrentParticle);}
1264 if CurrentParticle
+2 > MaxParticles
then
1265 CurrentParticle
:= 0
1267 CurrentParticle
:= CurrentParticle
+1;
1271 procedure g_GFX_SetMax(Count
: Integer);
1275 if Count
> 50000 then Count
:= 50000;
1276 if (Count
< 1) then Count
:= 1;
1278 SetLength(Particles
, Count
);
1279 for a
:= 0 to High(Particles
) do Particles
[a
].die();
1280 MaxParticles
:= Count
;
1281 //if CurrentParticle >= Count then
1282 CurrentParticle
:= 0;
1285 function g_GFX_GetMax(): Integer;
1287 Result
:= MaxParticles
;
1290 function FindOnceAnim
: DWORD
;
1294 if OnceAnims
<> nil then
1295 for i
:= 0 to High(OnceAnims
) do
1296 if OnceAnims
[i
].Animation
= nil then
1302 if OnceAnims
= nil then
1304 SetLength(OnceAnims
, 16);
1309 Result
:= High(OnceAnims
) + 1;
1310 SetLength(OnceAnims
, Length(OnceAnims
) + 16);
1314 procedure g_GFX_OnceAnim(X
, Y
: Integer; Anim
: TAnimation
; AnimType
: Byte = 0);
1321 find_id
:= FindOnceAnim();
1323 OnceAnims
[find_id
].AnimType
:= AnimType
;
1324 OnceAnims
[find_id
].Animation
:= TAnimation
.Create(Anim
.FramesID
, Anim
.Loop
, Anim
.Speed
);
1325 OnceAnims
[find_id
].Animation
.Blending
:= Anim
.Blending
;
1326 OnceAnims
[find_id
].Animation
.Alpha
:= Anim
.Alpha
;
1327 OnceAnims
[find_id
].X
:= X
;
1328 OnceAnims
[find_id
].Y
:= Y
;
1331 procedure g_GFX_Update();
1337 if not gpart_dbg_enabled
then exit
;
1338 if Particles
<> nil then
1340 w
:= gMapInfo
.Width
;
1341 h
:= gMapInfo
.Height
;
1343 len
:= High(Particles
);
1345 for a
:= 0 to len
do
1347 if Particles
[a
].alive
then
1349 with Particles
[a
] do
1351 if (Time
= LiveTime
) then begin die(); continue
; end;
1352 if (X
+1 >= w
) or (Y
+1 >= h
) or (X
<= 0) or (Y
<= 0) then begin die(); end;
1353 //if not alive then Continue;
1354 //e_WriteLog(Format('particle #%d: %d', [State, ParticleType]), MSG_NOTIFY);
1356 {CorrectOffsets(a);}
1360 end; // Particles <> nil
1362 if OnceAnims
<> nil then
1364 for a
:= 0 to High(OnceAnims
) do
1365 if OnceAnims
[a
].Animation
<> nil then
1367 case OnceAnims
[a
].AnimType
of
1370 if Random(3) = 0 then
1371 OnceAnims
[a
].X
:= OnceAnims
[a
].X
-1+Random(3);
1372 if Random(2) = 0 then
1373 OnceAnims
[a
].Y
:= OnceAnims
[a
].Y
-Random(2);
1377 if OnceAnims
[a
].Animation
.Played
then
1379 OnceAnims
[a
].Animation
.Free();
1380 OnceAnims
[a
].Animation
:= nil;
1383 OnceAnims
[a
].Animation
.Update();
1388 procedure g_GFX_Draw();
1392 if Particles
<> nil then
1394 glDisable(GL_TEXTURE_2D
);
1398 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1402 len
:= High(Particles
);
1404 for a
:= 0 to len
do
1405 with Particles
[a
] do
1406 if alive
and (X
>= sX
) and (Y
>= sY
) and (X
<= sX
+sWidth
) and (sY
<= sY
+sHeight
) then
1408 glColor4ub(Red
, Green
, Blue
, Alpha
);
1409 glVertex2i(X
+ offsetX
, Y
+ offsetY
);
1414 glDisable(GL_BLEND
);
1417 if OnceAnims
<> nil then
1418 for a
:= 0 to High(OnceAnims
) do
1419 if OnceAnims
[a
].Animation
<> nil then
1420 with OnceAnims
[a
] do
1421 Animation
.Draw(X
, Y
, M_NONE
);