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}
17 {$DEFINE D2F_NEW_SPARK_THINKER}
39 MARK_BLOCKED
= MARK_WALL
+ MARK_DOOR
;
40 MARK_LIQUID
= MARK_WATER
+ MARK_ACID
;
41 MARK_LIFT
= MARK_LIFTDOWN
+ MARK_LIFTUP
+ MARK_LIFTLEFT
+ MARK_LIFTRIGHT
;
43 procedure g_GFX_Init();
44 procedure g_GFX_Free();
46 procedure g_GFX_Blood(fX
, fY
: Integer; Count
: Word; vx
, vy
: Integer;
47 DevX
, DevY
: Word; CR
, CG
, CB
: Byte; Kind
: Byte = BLOOD_NORMAL
);
48 procedure g_GFX_Spark(fX
, fY
: Integer; Count
: Word; Angle
: SmallInt; DevX
, DevY
: Byte);
49 procedure g_GFX_Water(fX
, fY
: Integer; Count
: Word; fVelX
, fVelY
: Single; DevX
, DevY
, Color
: Byte);
50 procedure g_GFX_SimpleWater(fX
, fY
: Integer; Count
: Word; fVelX
, fVelY
: Single; DefColor
, CR
, CG
, CB
: Byte);
51 procedure g_GFX_Bubbles(fX
, fY
: Integer; Count
: Word; DevX
, DevY
: Byte);
52 procedure g_GFX_SetMax(Count
: Integer);
53 function g_GFX_GetMax(): Integer;
55 procedure g_GFX_OnceAnim(X
, Y
: Integer; Anim
: TAnimation
; AnimType
: Byte = 0);
57 procedure g_Mark (x
, y
, Width
, Height
: Integer; t
: Byte; st
: Boolean=true);
59 procedure g_GFX_Update ();
60 procedure g_GFX_Draw ();
64 gpart_dbg_enabled
: Boolean = true;
65 gpart_dbg_phys_enabled
: Boolean = true;
71 g_map
, g_panel
, g_basic
, Math
, e_graphics
, GL
, GLExt
,
72 g_options
, g_console
, SysUtils
, g_triggers
, MAPDEF
,
73 g_game
, g_language
, g_net
, xprofiler
;
76 PParticle
= ^TParticle
;
81 AccelX
, AccelY
: Single;
82 Red
, Green
, Blue
: Byte;
87 offsetX
, offsetY
: ShortInt;
89 liquidTopY
: Integer; // don't float higher than this
99 //k8: sorry, i have to emulate virtual methods this way, 'cause i haet `Object`
101 procedure thinkerBlood ();
102 procedure thinkerSpark ();
103 procedure thinkerBubble ();
104 procedure thinkerWater ();
106 function isSleeping (): Boolean; inline;
107 procedure awake (); inline;
109 function alive (): Boolean; inline;
110 procedure die (); inline;
111 procedure think (); inline;
117 Animation
: TAnimation
;
123 PARTICLE_BUBBLES
= 2;
130 Particles
: array of TParticle
;
131 OnceAnims
: array of TOnceAnim
;
132 MaxParticles
: Integer;
133 CurrentParticle
: Integer;
134 // awakeMap has one bit for each map grid cell; on g_Mark,
135 // corresponding bits will be set, and in `think()` all particles
136 // in marked cells will be awaken
137 awakeMap
: packed array of LongWord = nil;
138 awakeMapH
: Integer = -1;
139 awakeMapW
: Integer = -1;
140 awakeMinX
, awakeMinY
: Integer;
143 // ////////////////////////////////////////////////////////////////////////// //
144 // HACK! using mapgrid
145 procedure awmClear (); inline;
147 if (awakeMapW
> 0) then FillDWord(awakeMap
[0], Length(awakeMap
), 0);
151 procedure awmSetup ();
153 assert(mapGrid
<> nil);
154 awakeMapW
:= (mapGrid
.gridWidth
+mapGrid
.tileSize
-1) div mapGrid
.tileSize
;
155 awakeMapW
:= (awakeMapW
+31) div 32; // LongWord has 32 bits ;-)
156 awakeMapH
:= (mapGrid
.gridHeight
+mapGrid
.tileSize
-1) div mapGrid
.tileSize
;
157 awakeMinX
:= mapGrid
.gridX0
;
158 awakeMinY
:= mapGrid
.gridY0
;
159 SetLength(awakeMap
, awakeMapW
*awakeMapH
);
160 {$IF DEFINED(D2F_DEBUG)}
161 e_LogWritefln('particle awake map: %sx%s (for grid of size %sx%s)', [awakeMapW
, awakeMapH
, mapGrid
.gridWidth
, mapGrid
.gridHeight
]);
167 function awmIsSet (x
, y
: Integer): Boolean; inline;
169 x
:= (x
-awakeMinX
) div mapGrid
.tileSize
;
170 y
:= (y
-awakeMinY
) div mapGrid
.tileSize
;
171 if (x
>= 0) and (y
>= 0) and (x
div 32 < awakeMapW
) and (y
< awakeMapH
) then
173 {$IF DEFINED(D2F_DEBUG)}
174 assert(y
*awakeMapW
+x
div 32 < Length(awakeMap
));
176 result
:= ((awakeMap
[y
*awakeMapW
+x
div 32] and (LongWord(1) shl (x
mod 32))) <> 0);
185 procedure awmSet (x
, y
: Integer); inline;
189 x
:= (x
-awakeMinX
) div mapGrid
.tileSize
;
190 y
:= (y
-awakeMinY
) div mapGrid
.tileSize
;
191 if (x
>= 0) and (y
>= 0) and (x
div 32 < awakeMapW
) and (y
< awakeMapH
) then
193 {$IF DEFINED(D2F_DEBUG)}
194 assert(y
*awakeMapW
+x
div 32 < Length(awakeMap
));
196 v
:= @awakeMap
[y
*awakeMapW
+x
div 32];
197 v
^ := v
^ or (LongWord(1) shl (x
mod 32));
202 // ////////////////////////////////////////////////////////////////////////// //
203 function TParticle
.alive (): Boolean; inline; begin result
:= (State
<> STATE_FREE
); end;
204 procedure TParticle
.die (); inline; begin State
:= STATE_FREE
; end;
206 function TParticle
.isSleeping (): Boolean; inline;
208 result
:= alive
and (onGround
or (not justSticked
and (State
= STATE_STICK
)));
211 procedure TParticle
.awake (); inline;
213 if {alive and} (onGround
or (not justSticked
and (State
= STATE_STICK
))) then
215 // wakeup this particle
217 if (part.ParticleType = PARTICLE_SPARK) then
219 e_LogWritefln('waking up particle of type %s; justSticked=%s; onGround=%s; VelY=%s; AccelY=%s', [part.ParticleType, part.justSticked, part.onGround, part.VelY, part.AccelY]);
222 justSticked
:= true; // so sticked state will be re-evaluated
225 if (VelY
= 0) then VelY
:= 0.1;
226 if (AccelY
= 0) then AccelY
:= 0.5;
228 onGround
:= false; // so onground state will be re-evaluated
234 procedure TParticle
.think (); inline;
236 // awake sleeping particle, if necessary
239 if awmIsSet(X
, Y
) then awake();
242 PARTICLE_BLOOD
: thinkerBlood();
243 PARTICLE_SPARK
: thinkerSpark();
244 PARTICLE_BUBBLES
: thinkerBubble();
245 PARTICLE_WATER
: thinkerWater();
250 // ////////////////////////////////////////////////////////////////////////// //
251 function isBlockedAt (x
, y
: Integer): Boolean; inline;
253 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
254 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
258 function isWallAt (x
, y
: Integer): Boolean; inline;
260 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
261 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, (PANEL_WALL
or PANEL_STEP
));
264 function isLiftUpAt (x
, y
: Integer): Boolean; inline;
266 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
267 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, PANEL_LIFTUP
);
270 function isLiftDownAt (x
, y
: Integer): Boolean; inline;
272 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
273 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, PANEL_LIFTDOWN
);
276 function isLiftLeftAt (x
, y
: Integer): Boolean; inline;
278 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
279 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, PANEL_LIFTLEFT
);
282 function isLiftRightAt (x
, y
: Integer): Boolean; inline;
284 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
285 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, PANEL_LIFTRIGHT
);
288 function isLiquidAt (x
, y
: Integer): Boolean; inline;
290 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
291 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, (PANEL_WATER
or PANEL_ACID1
or PANEL_ACID2
));
294 function isAnythingAt (x
, y
: Integer): Boolean; inline;
296 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
297 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
));
304 procedure g_Mark(x
, y
, Width
, Height
: Integer; t
: Byte; st
: Boolean=true);
309 if (Width
< 1) or (Height
< 1) then exit
;
310 // make some border, so we'll hit particles lying around the panel
315 ts
:= mapGrid
.tileSize
;
329 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
330 procedure CreateCollideMap();
334 //g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
335 //SetLength(gCollideMap, gMapInfo.Height+1);
336 //for a := 0 to High(gCollideMap) do SetLength(gCollideMap[a], gMapInfo.Width+1);
341 procedure g_GFX_Init();
343 //CreateCollideMap();
346 gpart_dbg_enabled
:= False;
351 procedure g_GFX_Free();
356 SetLength(Particles
, MaxParticles
);
357 for a
:= 0 to High(Particles
) do Particles
[a
].die();
358 CurrentParticle
:= 0;
360 if (OnceAnims
<> nil) then
362 for a
:= 0 to High(OnceAnims
) do OnceAnims
[a
].Animation
.Free();
373 // ////////////////////////////////////////////////////////////////////////// //
374 procedure TParticle
.thinkerBlood ();
378 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
387 h
:= gMapInfo
.Height
;
391 if (State
= STATE_STICK
) then
393 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
395 if (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
396 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) and
397 (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
398 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED))
401 if (not isBlockedAt(X
, Y
-1)) and
402 (not isBlockedAt(X
, Y
+1)) and
403 (not isBlockedAt(X
-1, Y
)) and
404 (not isBlockedAt(X
+1, Y
))
408 if not mapGrid
.traceOrthoRayWhileIn(ex
, ey
, X
+stickDX
, Y
, X
+stickDX
, mapGrid
.gridY0
+mapGrid
.gridHeight
, GridTagWall
or GridTagDoor
or GridTagStep
) then
411 State
:= STATE_NORMAL
;
412 //e_LogWritefln('juststicked unsticked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s', [X, X+stickDX, stickDX, Y]);
417 if (nil <> g_Map_traceToNearest(X
, Y
, X
, stickEY
, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
)) then
419 if (ey
> stickEY
) then stickEY
:= ey
-1;
421 justSticked
:= false;
422 //e_LogWritefln('juststicked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s; stickEY=%s', [X, X+stickDX, stickDX, Y, stickEY]);
425 if (State
<> STATE_STICK
) or (Y
>= stickEY
)
426 //if not g_Map_CollidePanel(X-1, Y-1, 3, 3, (PANEL_STEP or PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR))
429 begin // Îòëèïëà - êàïàåò
432 State
:= STATE_NORMAL
;
434 else if (Random(200) = 100) then
435 begin // Ïðèëåïëåíà - íî âîçìîæíî ñòåêàåò
442 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
443 if not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
445 if isLiftUpAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
447 if (VelY
> -4-Random(3)) then VelY
-= 0.8;
448 if (abs(VelX
) > 0.1) then VelX
-= VelX
/10.0;
449 VelX
+= (Random
-Random
)*0.2;
452 if isLiftLeftAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
454 if (VelX
> -8-Random(3)) then VelX
-= 0.8;
457 if isLiftRightAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
458 begin // Ïîòîê âïðàâî
459 if (VelX
< 8+Random(3)) then VelX
+= 0.8;
464 pan
:= g_Map_PanelAtPoint(X
, Y
, GridTagLift
);
467 if ((pan
.PanelType
and PANEL_LIFTUP
) <> 0) then
469 if (VelY
> -4-Random(3)) then VelY
-= 0.8;
470 if (abs(VelX
) > 0.1) then VelX
-= VelX
/10.0;
471 VelX
+= (Random
-Random
)*0.2;
474 if ((pan
.PanelType
and PANEL_LIFTLEFT
) <> 0) then
476 if (VelX
> -8-Random(3)) then VelX
-= 0.8;
479 if ((pan
.PanelType
and PANEL_LIFTRIGHT
) <> 0) then
481 if (VelX
< 8+Random(3)) then VelX
+= 0.8;
490 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
491 if (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) then
493 if (State
<> STATE_STICK
) and
494 (not isBlockedAt(X
, Y
-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
495 (not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
496 (not isBlockedAt(X
, Y
+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
497 begin // Âèñèò â âîçäóõå - êàïàåò
500 State
:= STATE_NORMAL
;
504 if (State
<> STATE_STICK
) and (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) then
506 // Âèñèò â âîçäóõå - êàïàåò
507 if (nil = g_Map_traceToNearest(X
, Y
-1, X
, Y
+1, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
)) then
511 State
:= STATE_NORMAL
;
516 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
520 pan
:= g_Map_traceToNearest(X
, Y
, X
+dX
, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
), @ex
, @ey
);
530 State
:= STATE_STICK
;
532 if (dX
> 0) then stickDX
:= 1 else stickDX
:= -1;
534 if (X
< 0) or (X
>= w
) then begin die(); exit
; end;
539 if (dY
< 0) or not onGround
then
541 pan
:= g_Map_traceToNearest(X
, Y
, X
, Y
+dY
, (GridTagWall
or GridTagDoor
or GridTagStep
), @ex
, @ey
);
551 if (dY
> 0) and (State
<> STATE_STICK
) then
553 State
:= STATE_NORMAL
;
557 State
:= STATE_STICK
;
558 if (g_Map_PanelAtPoint(X
-1, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
)) <> nil) then stickDX
:= -1
559 else if (g_Map_PanelAtPoint(X
+1, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
)) <> nil) then stickDX
:= 1
564 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
566 if (Y
< 0) or (Y
>= h
) then begin die(); exit
; end;
572 if (dX
> 0) then s
:= 1 else s
:= -1;
576 if (X
+s
>= w
) or (X
+s
<= 0) then begin die(); break
; end;
577 //c := gCollideMap[Y, X+s];
578 if isBlockedAt(X
+s
, Y
) {ByteBool(c and MARK_BLOCKED)} then
584 State
:= STATE_STICK
;
594 if (dY
> 0) then s
:= 1 else s
:= -1;
598 if (Y
+s
>= h
) or (Y
+s
<= 0) then begin die(); break
; end;
599 //c := gCollideMap[Y+s, X];
600 if isBlockedAt(X
, Y
+s
) {ByteBool(c and MARK_BLOCKED)} then
606 if (s
> 0) and (State
<> STATE_STICK
) then State
:= STATE_NORMAL
else State
:= STATE_STICK
;
607 justSticked
:= (State
= STATE_STICK
);
619 if (X
+dX
>= w
) or (Y
+dY
>= h
) or (X
+dX
<= 0) or (Y
+dY
<= 0) or isBlockedAt(X
+dX
, Y
+dY
) {ByteBool(gCollideMap[Y+dY, X+dX] and MARK_BLOCKED)} then
620 begin // Ñòåíà/äâåðü/ãðàíèöà
636 // Êðîâü ðàñòâîðÿåòñÿ â æèäêîñòè:
637 if isLiquidAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
640 Alpha
:= 255-trunc((255.0*Time
)/LiveTime
);
646 // ////////////////////////////////////////////////////////////////////////// //
647 procedure TParticle
.thinkerWater ();
650 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
659 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
661 h
:= gMapInfo
.Height
;
664 //TODO: trace wall end when water becomes stick
665 if (State
= STATE_STICK
) and (Random(30) = 15) then
666 begin // Ñòåêàåò/îòëèïàåò
669 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
670 if (not isBlockedAt(X
-1, Y
) {ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)}) and
671 (not isBlockedAt(X
+1, Y
) {ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)}) then
672 State
:= STATE_NORMAL
;
674 if (stickDX
= 0) then
676 // no walls around, drop
677 State
:= STATE_NORMAL
;
683 if not mapGrid
.traceOrthoRayWhileIn(ex
, ey
, X
+stickDX
, Y
, X
+stickDX
, mapGrid
.gridY0
+mapGrid
.gridHeight
, GridTagWall
or GridTagDoor
or GridTagStep
) then
686 State
:= STATE_NORMAL
;
687 //e_LogWritefln('juststicked unsticked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s', [X, X+stickDX, stickDX, Y]);
692 justSticked
:= false;
693 if (nil <> g_Map_traceToNearest(X
, Y
, X
, stickEY
, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
)) then
695 if (ey
> stickEY
) then stickEY
:= ey
-1;
697 //e_LogWritefln('juststicked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s; stickEY=%s', [X, X+stickDX, stickDX, Y, stickEY]);
702 if (Y
>= stickEY
) then State
:= STATE_NORMAL
;
704 //if not g_Map_CollidePanel(X-1, Y-1, 3, 3, (PANEL_STEP or PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR))
710 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
711 if not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
713 if isLiftUpAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
715 if VelY
> -4-Random(3) then
717 if Abs(VelX
) > 0.1 then
718 VelX
:= VelX
- VelX
/10.0;
719 VelX
:= VelX
+ (Random
-Random
)*0.2;
722 if isLiftLeftAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
724 if VelX
> -8-Random(3) then
728 if isLiftRightAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
729 begin // Ïîòîê âïðàâî
730 if VelX
< 8+Random(3) then
736 pan
:= g_Map_PanelAtPoint(X
, Y
, (GridTagAcid1
or GridTagAcid2
or GridTagWater
or GridTagLift
));
739 if ((pan
.tag
and (GridTagAcid1
or GridTagAcid2
or GridTagWater
)) <> 0) then begin die(); exit
; end;
740 if ((pan
.PanelType
and PANEL_LIFTUP
) <> 0) then
742 if (VelY
> -4-Random(3)) then VelY
-= 0.8;
743 if (Abs(VelX
) > 0.1) then VelX
-= VelX
/10.0;
744 VelX
+= (Random
-Random
)*0.2;
747 if ((pan
.PanelType
and PANEL_LIFTLEFT
) <> 0) then
749 if (VelX
> -8-Random(3)) then VelX
-= 0.8;
752 if ((pan
.PanelType
and PANEL_LIFTRIGHT
) <> 0) then
754 if (VelX
< 8+Random(3)) then VelX
+= 0.8;
763 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
764 if (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) then
766 if (State
<> STATE_STICK
) and
767 (not isBlockedAt(X
, Y
-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
768 (not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
769 (not isBlockedAt(X
, Y
+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
770 begin // Âèñèò â âîçäóõå - êàïàåò
773 State
:= STATE_NORMAL
;
777 if (State
<> STATE_STICK
) and (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) then
779 // Âèñèò â âîçäóõå - êàïàåò
780 if (nil = g_Map_traceToNearest(X
, Y
-1, X
, Y
+1, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
)) then
784 State
:= STATE_NORMAL
;
789 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
793 pan
:= g_Map_traceToNearest(X
, Y
, X
+dX
, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
);
799 if (dY
> 0) and ((pan
.tag
and (GridTagAcid1
or GridTagAcid2
or GridTagWater
)) <> 0) then begin die(); exit
; end;
801 if ((pan
.tag
and (GridTagWall
or GridTagDoor
or GridTagStep
)) <> 0) then
807 State
:= STATE_STICK
;
809 if (dX
> 0) then stickDX
:= 1 else stickDX
:= -1;
812 if (X
< 0) or (X
>= gMapInfo
.Width
) then begin die(); exit
; end;
817 if (dY
< 0) or not onGround
then
819 pan
:= g_Map_traceToNearest(X
, Y
, X
, Y
+dY
, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
);
825 if (dY
> 0) and ((pan
.tag
and (GridTagAcid1
or GridTagAcid2
or GridTagWater
)) <> 0) then begin die(); exit
; end;
827 if ((pan
.tag
and (GridTagWall
or GridTagDoor
or GridTagStep
)) <> 0) then
833 if (dY
> 0) and (State
<> STATE_STICK
) then
835 State
:= STATE_NORMAL
;
839 State
:= STATE_STICK
;
840 if (g_Map_PanelAtPoint(X
-1, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
)) <> nil) then stickDX
:= -1
841 else if (g_Map_PanelAtPoint(X
+1, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
)) <> nil) then stickDX
:= 1
847 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
849 if (Y
< 0) or (Y
>= gMapInfo
.Height
) then begin die(); exit
; end;
855 if (dX
> 0) then s
:= 1 else s
:= -1;
856 for b
:= 1 to Abs(dX
) do
859 if (X
+s
>= w
) or (X
+s
<= 0) then begin die(); break
;end;
860 //c := gCollideMap[Y, X+s];
861 // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò?
862 if isLiquidAt(X
+s
, Y
) {ByteBool(c and MARK_LIQUID)} and (dY
> 0) then begin die(); break
; end;
863 if isBlockedAt(X
+s
, Y
) {ByteBool(c and MARK_BLOCKED)} then
869 State
:= STATE_STICK
;
879 if (dY
> 0) then s
:= 1 else s
:= -1;
880 for b
:= 1 to Abs(dY
) do
882 // Ñíèçó/ñâåðõó ãðàíèöà
883 if (Y
+s
>= h
) or (Y
+s
<= 0) then begin die(); break
; end;
884 //c := gCollideMap[Y+s, X];
885 // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
886 if isLiquidAt(X
, Y
+s
) {ByteBool(c and MARK_LIQUID)} and (dY
> 0) then begin die(); break
; end;
887 if isBlockedAt(X
, Y
+s
) {ByteBool(c and MARK_BLOCKED)} then
893 if (s
> 0) and (State
<> STATE_STICK
) then State
:= STATE_NORMAL
else State
:= STATE_STICK
;
894 justSticked
:= (State
= STATE_STICK
);
909 // ////////////////////////////////////////////////////////////////////////// //
910 procedure TParticle
.thinkerSpark ();
913 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
924 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
925 if (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) then
927 pan
:= g_Map_traceToNearest(X
, Y
-1, X
, Y
+1, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
);
930 if (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) and
931 (not isBlockedAt(X
, Y
-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
932 (not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
933 (not isBlockedAt(X
, Y
+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
934 begin // Âèñèò â âîçäóõå
942 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
943 pan
:= g_Map_traceToNearest(X
, Y
, X
+dX
, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
);
944 //e_WriteLog(Format('spark h-trace: (%d,%d)-(%d,%d); dx=%d; end=(%d,%d); hit=%d', [X, Y, X+dX, Y, dX, ex, ey, Integer(pan <> nil)]), MSG_NOTIFY);
950 if ((pan
.tag
and (GridTagAcid1
or GridTagAcid2
or GridTagWater
)) <> 0) then begin die(); exit
; end;
954 if (X
< 0) or (X
>= gMapInfo
.Width
) then begin die(); exit
; end;
956 if (dX
> 0) then s
:= 1 else s
:= -1;
960 if (X
+s
>= gMapInfo
.Width
) or (X
+s
<= 0) then begin die(); break
; end;
961 //c := gCollideMap[Y, X+s];
962 if isBlockedAt(X
+s
, Y
) {ByteBool(c and MARK_BLOCKED)} then
963 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
969 if not isAnythingAt(X
+s
, Y
) {c = MARK_FREE} then
982 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
983 if (dY
< 0) or not onGround
then
985 pan
:= g_Map_traceToNearest(X
, Y
, X
, Y
+dY
, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
);
991 e_LogWritefln('AWAKEN particle of type %s; justSticked=%s; onGround=%s; VelY=%s; AccelY=%s; Y=%s; ey=%s', [ParticleType, justSticked, onGround, VelY, AccelY, Y, ey]);
998 if ((pan
.tag
and (GridTagAcid1
or GridTagAcid2
or GridTagWater
)) <> 0) then begin die(); exit
; end;
1002 AccelY
:= abs(AccelY
);
1012 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1014 if (Y
< 0) or (Y
>= gMapInfo
.Height
) then begin die(); exit
; end;
1016 if (dY
> 0) then s
:= 1 else s
:= -1;
1020 if (Y
+s
>= gMapInfo
.Height
) or (Y
+s
<= 0) then begin die(); break
; end;
1021 //c := gCollideMap[Y+s, X];
1022 if isBlockedAt(X
, Y
+s
) {ByteBool(c and MARK_BLOCKED)} then
1023 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1027 AccelY
:= Abs(AccelY
);
1029 else // Èëè íå ïàäàåò
1040 if not isAnythingAt(X
, Y
+s
) {c = MARK_FREE} then
1051 if (VelX
<> 0.0) then VelX
+= AccelX
;
1053 if (VelY
<> 0.0) then
1055 if (AccelY
< 10) then AccelY
+= 0.08;
1062 // ////////////////////////////////////////////////////////////////////////// //
1063 procedure TParticle
.thinkerBubble ();
1070 h
:= gMapInfo
.Height
;
1081 for b
:= 1 to Abs(dY
) do
1083 if (Y
+s
>= h
) or (Y
+s
<= 0) then begin die(); break
; end;
1086 if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
1087 begin // Óæå íå æèäêîñòü
1088 State := STATE_FREE;
1092 // we traced liquid before, so don't bother checking
1093 if (Y
+s
<= liquidTopY
) then begin die(); break
; end;
1100 VelY
:= VelY
+ AccelY
;
1106 // ////////////////////////////////////////////////////////////////////////// //
1107 procedure g_GFX_SparkVel (fX
, fY
: Integer; Count
: Word; VX
, VY
: Integer; DevX
, DevY
: Byte);
1114 if not gpart_dbg_enabled
then Exit
;
1115 l
:= Length(Particles
);
1117 if Count
> l
then Count
:= l
;
1119 DevX1
:= DevX
div 2;
1121 DevY1
:= DevY
div 2;
1124 for a
:= 1 to Count
do
1126 with Particles
[CurrentParticle
] do
1128 X
:= fX
-DevX1
+Random(DevX2
);
1129 Y
:= fY
-DevY1
+Random(DevY2
);
1131 VelX
:= VX
+ (Random
-Random
)*3;
1132 VelY
:= VY
+ (Random
-Random
)*3;
1140 AccelX
:= -Sign(VelX
)*Random
/100;
1144 Green
:= 100+Random(155);
1148 State
:= STATE_NORMAL
;
1150 LiveTime
:= 30+Random(60);
1151 ParticleType
:= PARTICLE_SPARK
;
1152 justSticked
:= false;
1153 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1157 if CurrentParticle
+2 > MaxParticles
then
1158 CurrentParticle
:= 0
1160 CurrentParticle
:= CurrentParticle
+1;
1165 procedure g_GFX_Blood(fX
, fY
: Integer; Count
: Word; vx
, vy
: Integer;
1166 DevX
, DevY
: Word; CR
, CG
, CB
: Byte; Kind
: Byte = BLOOD_NORMAL
);
1175 if not gpart_dbg_enabled
then Exit
;
1176 if Kind
= BLOOD_SPARKS
then
1178 g_GFX_SparkVel(fX
, fY
, 2 + Random(2), -VX
div 2, -VY
div 2, DevX
, DevY
);
1181 l
:= Length(Particles
);
1187 DevX1
:= DevX
div 2;
1189 DevY1
:= DevY
div 2;
1192 for a
:= 1 to Count
do
1194 with Particles
[CurrentParticle
] do
1196 X
:= fX
- DevX1
+ Random(DevX2
);
1197 Y
:= fY
- DevY1
+ Random(DevY2
);
1200 if (X < 0) or (X > gMapInfo.Width-1) or
1201 (Y < 0) or (Y > gMapInfo.Height-1) or
1202 ByteBool(gCollideMap[Y, X] and MARK_WALL) then
1205 if isWallAt(X
, Y
) then continue
;
1207 VelX
:= vx
+ (Random
-Random
)*3;
1208 VelY
:= vy
+ (Random
-Random
)*3;
1216 AccelX
:= -Sign(VelX
)*Random
/100;
1219 CRnd
:= 20*Random(6);
1222 CC
:= CR
+ CRnd
- 50;
1223 if CC
< 0 then CC
:= 0;
1224 if CC
> 255 then CC
:= 255;
1230 CC
:= CG
+ CRnd
- 50;
1231 if CC
< 0 then CC
:= 0;
1232 if CC
> 255 then CC
:= 255;
1238 CC
:= CB
+ CRnd
- 50;
1239 if CC
< 0 then CC
:= 0;
1240 if CC
> 255 then CC
:= 255;
1247 State
:= STATE_NORMAL
;
1249 LiveTime
:= 120+Random(40);
1250 ParticleType
:= PARTICLE_BLOOD
;
1251 justSticked
:= false;
1252 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1257 if CurrentParticle
>= MaxParticles
-1 then
1258 CurrentParticle
:= 0
1260 CurrentParticle
:= CurrentParticle
+1;
1265 procedure g_GFX_Spark(fX
, fY
: Integer; Count
: Word; Angle
: SmallInt; DevX
, DevY
: Byte);
1271 BaseVelX
, BaseVelY
: Single;
1274 if not gpart_dbg_enabled
then Exit
;
1275 l
:= Length(Particles
);
1281 Angle
:= 360 - Angle
;
1283 DevX1
:= DevX
div 2;
1285 DevY1
:= DevY
div 2;
1288 b
:= DegToRad(Angle
);
1290 BaseVelY
:= 1.6*sin(b
);
1291 if Abs(BaseVelX
) < 0.01 then
1293 if Abs(BaseVelY
) < 0.01 then
1295 for a
:= 1 to Count
do
1297 with Particles
[CurrentParticle
] do
1299 X
:= fX
-DevX1
+Random(DevX2
);
1300 Y
:= fY
-DevY1
+Random(DevY2
);
1302 VelX
:= BaseVelX
*Random
;
1303 VelY
:= BaseVelY
-Random
;
1308 Green
:= 100+Random(155);
1312 State
:= STATE_NORMAL
;
1314 LiveTime
:= 30+Random(60);
1315 ParticleType
:= PARTICLE_SPARK
;
1316 justSticked
:= false;
1317 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1321 if CurrentParticle
+2 > MaxParticles
then
1322 CurrentParticle
:= 0
1324 CurrentParticle
:= CurrentParticle
+1;
1328 procedure g_GFX_Water(fX
, fY
: Integer; Count
: Word; fVelX
, fVelY
: Single; DevX
, DevY
, Color
: Byte);
1335 if not gpart_dbg_enabled
then Exit
;
1336 l
:= Length(Particles
);
1342 if Abs(fVelX
) < 3.0 then
1343 fVelX
:= 3.0 - 6.0*Random
;
1345 DevX1
:= DevX
div 2;
1347 DevY1
:= DevY
div 2;
1350 for a
:= 1 to Count
do
1352 with Particles
[CurrentParticle
] do
1354 X
:= fX
-DevX1
+Random(DevX2
);
1355 Y
:= fY
-DevY1
+Random(DevY2
);
1357 if Abs(fVelX
) < 0.5 then
1358 VelX
:= 1.0 - 2.0*Random
1360 VelX
:= fVelX
*Random
;
1361 if Random(10) < 7 then
1363 VelY
:= fVelY
*Random
;
1370 Red
:= 155 + Random(9)*10;
1371 Green
:= Trunc(150*Random
);
1376 Red
:= Trunc(150*Random
);
1377 Green
:= 175 + Random(9)*10;
1382 Red
:= Trunc(200*Random
);
1384 Blue
:= 175 + Random(9)*10;
1388 Red
:= 90 + Random(12)*10;
1396 State
:= STATE_NORMAL
;
1398 LiveTime
:= 60+Random(60);
1399 ParticleType
:= PARTICLE_WATER
;
1400 justSticked
:= false;
1401 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1405 if CurrentParticle
+2 > MaxParticles
then
1406 CurrentParticle
:= 0
1408 CurrentParticle
:= CurrentParticle
+1;
1412 procedure g_GFX_SimpleWater(fX
, fY
: Integer; Count
: Word; fVelX
, fVelY
: Single; DefColor
, CR
, CG
, CB
: Byte);
1417 if not gpart_dbg_enabled
then Exit
;
1418 l
:= Length(Particles
);
1424 for a
:= 1 to Count
do
1426 with Particles
[CurrentParticle
] do
1439 Red
:= 155 + Random(9)*10;
1440 Green
:= Trunc(150*Random
);
1445 Red
:= Trunc(150*Random
);
1446 Green
:= 175 + Random(9)*10;
1451 Red
:= Trunc(200*Random
);
1453 Blue
:= 175 + Random(9)*10;
1455 4: // Ñâîé öâåò, ñâåòëåå
1457 Red
:= 20 + Random(19)*10;
1460 Red
:= Min(Red
+ CR
, 255);
1461 Green
:= Min(Green
+ CG
, 255);
1462 Blue
:= Min(Blue
+ CB
, 255);
1464 5: // Ñâîé öâåò, òåìíåå
1466 Red
:= 20 + Random(19)*10;
1469 Red
:= Max(CR
- Red
, 0);
1470 Green
:= Max(CG
- Green
, 0);
1471 Blue
:= Max(CB
- Blue
, 0);
1475 Red
:= 90 + Random(12)*10;
1483 State
:= STATE_NORMAL
;
1485 LiveTime
:= 60+Random(60);
1486 ParticleType
:= PARTICLE_WATER
;
1487 justSticked
:= false;
1488 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1492 if CurrentParticle
+2 > MaxParticles
then
1493 CurrentParticle
:= 0
1495 CurrentParticle
:= CurrentParticle
+1;
1500 {.$DEFINE D2F_DEBUG_BUBBLES}
1501 procedure g_GFX_Bubbles(fX
, fY
: Integer; Count
: Word; DevX
, DevY
: Byte);
1506 l
, liquidx
: Integer;
1507 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1512 if not gpart_dbg_enabled
then Exit
;
1513 l
:= Length(Particles
);
1519 DevX1
:= DevX
div 2;
1521 DevY1
:= DevY
div 2;
1524 for a
:= 1 to Count
do
1526 with Particles
[CurrentParticle
] do
1528 X
:= fX
-DevX1
+Random(DevX2
);
1529 Y
:= fY
-DevY1
+Random(DevY2
);
1531 if (X
>= gMapInfo
.Width
) or (X
<= 0) or
1532 (Y
>= gMapInfo
.Height
) or (Y
<= 0) then
1536 // don't spawn bubbles outside of the liquid
1537 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1541 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1542 // tracer will return `false` if we started outside of the liquid
1544 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1545 stt
:= curTimeMicro();
1546 ptr
:= mapGrid
.traceOrthoRayWhileIn(liquidx
, liquidTopY
, X
, Y
, X
, 0, GridTagWater
or GridTagAcid1
or GridTagAcid2
);
1547 stt
:= curTimeMicro()-stt
;
1548 e_LogWritefln('traceOrthoRayWhileIn: time=%s (%s); liquidTopY=%s', [Integer(stt
), ptr
, liquidTopY
]);
1550 stt
:= curTimeMicro();
1551 nptr
:= g_Map_TraceLiquidNonPrecise(X
, Y
, 0, -8, liquidx
, liquidTopY
);
1552 stt
:= curTimeMicro()-stt
;
1553 e_LogWritefln('g_Map_TraceLiquidNonPrecise: time=%s (%s); liquidTopY=%s', [Integer(stt
), nptr
, liquidTopY
]);
1554 if not nptr
then continue
;
1556 if not g_Map_TraceLiquidNonPrecise(X
, Y
, 0, -8, liquidx
, liquidTopY
) then continue
;
1569 State
:= STATE_NORMAL
;
1572 ParticleType
:= PARTICLE_BUBBLES
;
1573 justSticked
:= false;
1574 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1578 if CurrentParticle
+2 > MaxParticles
then
1579 CurrentParticle
:= 0
1581 CurrentParticle
:= CurrentParticle
+1;
1585 procedure g_GFX_SetMax(Count
: Integer);
1589 if Count
> 50000 then Count
:= 50000;
1590 if (Count
< 1) then Count
:= 1;
1592 SetLength(Particles
, Count
);
1593 for a
:= 0 to High(Particles
) do Particles
[a
].die();
1594 MaxParticles
:= Count
;
1595 //if CurrentParticle >= Count then
1596 CurrentParticle
:= 0;
1599 function g_GFX_GetMax(): Integer;
1601 Result
:= MaxParticles
;
1604 function FindOnceAnim
: DWORD
;
1608 if OnceAnims
<> nil then
1609 for i
:= 0 to High(OnceAnims
) do
1610 if OnceAnims
[i
].Animation
= nil then
1616 if OnceAnims
= nil then
1618 SetLength(OnceAnims
, 16);
1623 Result
:= High(OnceAnims
) + 1;
1624 SetLength(OnceAnims
, Length(OnceAnims
) + 16);
1628 procedure g_GFX_OnceAnim(X
, Y
: Integer; Anim
: TAnimation
; AnimType
: Byte = 0);
1632 if not gpart_dbg_enabled
then Exit
;
1636 find_id
:= FindOnceAnim();
1638 OnceAnims
[find_id
].AnimType
:= AnimType
;
1639 OnceAnims
[find_id
].Animation
:= TAnimation
.Create(Anim
.FramesID
, Anim
.Loop
, Anim
.Speed
);
1640 OnceAnims
[find_id
].Animation
.Blending
:= Anim
.Blending
;
1641 OnceAnims
[find_id
].Animation
.Alpha
:= Anim
.Alpha
;
1642 OnceAnims
[find_id
].X
:= X
;
1643 OnceAnims
[find_id
].Y
:= Y
;
1646 procedure g_GFX_Update();
1652 if not gpart_dbg_enabled
then exit
;
1654 if (Particles
<> nil) then
1656 w
:= gMapInfo
.Width
;
1657 h
:= gMapInfo
.Height
;
1659 len
:= High(Particles
);
1661 for a
:= 0 to len
do
1663 if Particles
[a
].alive
then
1665 with Particles
[a
] do
1667 if (Time
= LiveTime
) then begin die(); continue
; end;
1668 if (X
+1 >= w
) or (Y
+1 >= h
) or (X
<= 0) or (Y
<= 0) then begin die(); end;
1669 //if not alive then Continue;
1670 //e_WriteLog(Format('particle #%d: %d', [State, ParticleType]), MSG_NOTIFY);
1675 end; // Particles <> nil
1680 if OnceAnims
<> nil then
1682 for a
:= 0 to High(OnceAnims
) do
1683 if OnceAnims
[a
].Animation
<> nil then
1685 case OnceAnims
[a
].AnimType
of
1688 if Random(3) = 0 then
1689 OnceAnims
[a
].X
:= OnceAnims
[a
].X
-1+Random(3);
1690 if Random(2) = 0 then
1691 OnceAnims
[a
].Y
:= OnceAnims
[a
].Y
-Random(2);
1695 if OnceAnims
[a
].Animation
.Played
then
1697 OnceAnims
[a
].Animation
.Free();
1698 OnceAnims
[a
].Animation
:= nil;
1701 OnceAnims
[a
].Animation
.Update();
1706 procedure g_GFX_Draw();
1710 if Particles
<> nil then
1712 glDisable(GL_TEXTURE_2D
);
1716 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1720 len
:= High(Particles
);
1722 for a
:= 0 to len
do
1723 with Particles
[a
] do
1724 if alive
and (X
>= sX
) and (Y
>= sY
) and (X
<= sX
+sWidth
) and (sY
<= sY
+sHeight
) then
1726 glColor4ub(Red
, Green
, Blue
, Alpha
);
1727 glVertex2i(X
+ offsetX
, Y
+ offsetY
);
1732 glDisable(GL_BLEND
);
1735 if OnceAnims
<> nil then
1736 for a
:= 0 to High(OnceAnims
) do
1737 if OnceAnims
[a
].Animation
<> nil then
1738 with OnceAnims
[a
] do
1739 Animation
.Draw(X
, Y
, M_NONE
);