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 alive (): Boolean; inline;
107 procedure die (); inline;
108 procedure think (); inline;
114 Animation
: TAnimation
;
120 PARTICLE_BUBBLES
= 2;
127 Particles
: array of TParticle
;
128 OnceAnims
: array of TOnceAnim
;
129 MaxParticles
: Integer;
130 CurrentParticle
: Integer;
133 // ////////////////////////////////////////////////////////////////////////// //
134 function TParticle
.alive (): Boolean; inline; begin result
:= (State
<> STATE_FREE
); end;
135 procedure TParticle
.die (); inline; begin State
:= STATE_FREE
; end;
137 procedure TParticle
.think (); inline;
140 PARTICLE_BLOOD
: thinkerBlood();
141 PARTICLE_SPARK
: thinkerSpark();
142 PARTICLE_BUBBLES
: thinkerBubble();
143 PARTICLE_WATER
: thinkerWater();
148 // ////////////////////////////////////////////////////////////////////////// //
149 function isBlockedAt (x
, y
: Integer): Boolean; inline;
151 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
152 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
156 function isWallAt (x
, y
: Integer): Boolean; inline;
158 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
159 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, (PANEL_WALL
or PANEL_STEP
));
162 function isLiftUpAt (x
, y
: Integer): Boolean; inline;
164 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
165 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, PANEL_LIFTUP
);
168 function isLiftDownAt (x
, y
: Integer): Boolean; inline;
170 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
171 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, PANEL_LIFTDOWN
);
174 function isLiftLeftAt (x
, y
: Integer): Boolean; inline;
176 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
177 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, PANEL_LIFTLEFT
);
180 function isLiftRightAt (x
, y
: Integer): Boolean; inline;
182 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
183 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, PANEL_LIFTRIGHT
);
186 function isLiquidAt (x
, y
: Integer): Boolean; inline;
188 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
189 result
:= g_Map_HasAnyPanelAtPoint(x
, y
, (PANEL_WATER
or PANEL_ACID1
or PANEL_ACID2
));
192 function isAnythingAt (x
, y
: Integer): Boolean; inline;
194 if not gpart_dbg_phys_enabled
then begin result
:= false; exit
; end;
195 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
));
199 procedure g_Mark(x
, y
, Width
, Height
: Integer; t
: Byte; st
: Boolean=true);
200 {$IF not DEFINED(HAS_COLLIDE_BITMAP)}
205 for f
:= 0 to High(Particles
) do
207 part
:= @Particles
[f
];
208 if part
.alive
and (part
.onGround
or (not part
.justSticked
and (part
.State
= STATE_STICK
))) and
209 (part
.X
>= x
-2) and (part
.Y
>= y
-2) and (part
.X
< x
+Width
+4) and (part
.Y
< y
+Height
+4) then
211 // wakup this particle
213 if (part.ParticleType = PARTICLE_SPARK) then
215 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]);
218 part
.justSticked
:= true; // so sticked state will be re-evaluated
219 if part
.onGround
then
221 if (part
.VelY
= 0) then part
.VelY
:= 0.1;
222 if (part
.AccelY
= 0) then part
.AccelY
:= 0.5;
224 part
.onGround
:= false; // so onground state will be re-evaluated
231 yy
, y2
, xx
, x2
: Integer;
244 Height
:= Height
+ y
;
251 if x
> gMapInfo
.Width
then
253 if y
> gMapInfo
.Height
then
256 y2
:= y
+ Height
- 1;
257 if y2
> gMapInfo
.Height
then
258 y2
:= gMapInfo
.Height
;
261 if x2
> gMapInfo
.Width
then
262 x2
:= gMapInfo
.Width
;
265 begin // Óñòàíîâèòü ïðèçíàê
268 gCollideMap
[yy
][xx
] := gCollideMap
[yy
][xx
] or t
;
271 begin // Óáðàòü ïðèçíàê
275 gCollideMap
[yy
][xx
] := gCollideMap
[yy
][xx
] and t
;
281 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
282 procedure CreateCollideMap();
286 g_Game_SetLoadingText(_lc
[I_LOAD_COLLIDE_MAP
]+' 1/6', 0, False);
287 SetLength(gCollideMap
, gMapInfo
.Height
+1);
288 for a
:= 0 to High(gCollideMap
) do
289 SetLength(gCollideMap
[a
], gMapInfo
.Width
+1);
291 if gWater
<> nil then
293 g_Game_SetLoadingText(_lc
[I_LOAD_COLLIDE_MAP
]+' 2/6', 0, True);
294 for a
:= 0 to High(gWater
) do
296 g_Mark(X
, Y
, Width
, Height
, MARK_WATER
, True);
299 if gAcid1
<> nil then
301 g_Game_SetLoadingText(_lc
[I_LOAD_COLLIDE_MAP
]+' 3/6', 0, True);
302 for a
:= 0 to High(gAcid1
) do
304 g_Mark(X
, Y
, Width
, Height
, MARK_ACID
, True);
307 if gAcid2
<> nil then
309 g_Game_SetLoadingText(_lc
[I_LOAD_COLLIDE_MAP
]+' 4/6', 0, True);
310 for a
:= 0 to High(gAcid2
) do
312 g_Mark(X
, Y
, Width
, Height
, MARK_ACID
, True);
315 if gLifts
<> nil then
317 g_Game_SetLoadingText(_lc
[I_LOAD_COLLIDE_MAP
]+' 5/6', 0, True);
318 for a
:= 0 to High(gLifts
) do
321 g_Mark(X
, Y
, Width
, Height
, MARK_LIFT
, False);
324 g_Mark(X
, Y
, Width
, Height
, MARK_LIFTUP
, True)
325 else if LiftType
= 1 then
326 g_Mark(X
, Y
, Width
, Height
, MARK_LIFTDOWN
, True)
327 else if LiftType
= 2 then
328 g_Mark(X
, Y
, Width
, Height
, MARK_LIFTLEFT
, True)
329 else if LiftType
= 3 then
330 g_Mark(X
, Y
, Width
, Height
, MARK_LIFTRIGHT
, True)
334 if gWalls
<> nil then
336 g_Game_SetLoadingText(_lc
[I_LOAD_COLLIDE_MAP
]+' 6/6', 0, True);
337 for a
:= 0 to High(gWalls
) do
339 if gWalls
[a
].Door
then
342 if gWalls
[a
].Enabled
then
344 g_Mark(X
, Y
, Width
, Height
, MARK_DOOR
, True)
345 else // Îòêðûòàÿ äâåðü:
346 if gWalls
[a
].Enabled
then
348 g_Mark(X
, Y
, Width
, Height
, MARK_DOOR
, False);
352 g_Mark(X
, Y
, Width
, Height
, MARK_WALL
, True);
359 procedure g_GFX_Init();
361 //CreateCollideMap();
363 gpart_dbg_enabled
:= False;
368 procedure g_GFX_Free();
373 SetLength(Particles
, MaxParticles
);
374 for a
:= 0 to High(Particles
) do Particles
[a
].die();
375 CurrentParticle
:= 0;
377 if OnceAnims
<> nil then
379 for a
:= 0 to High(OnceAnims
) do
380 OnceAnims
[a
].Animation
.Free();
387 // ////////////////////////////////////////////////////////////////////////// //
388 procedure TParticle
.thinkerBlood ();
392 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
401 h
:= gMapInfo
.Height
;
405 if (State
= STATE_STICK
) then
407 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
409 if (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
410 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) and
411 (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
412 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED))
415 if (not isBlockedAt(X
, Y
-1)) and
416 (not isBlockedAt(X
, Y
+1)) and
417 (not isBlockedAt(X
-1, Y
)) and
418 (not isBlockedAt(X
+1, Y
))
422 if not mapGrid
.traceOrthoRayWhileIn(ex
, ey
, X
+stickDX
, Y
, X
+stickDX
, mapGrid
.gridY0
+mapGrid
.gridHeight
, GridTagWall
or GridTagDoor
or GridTagStep
) then
425 State
:= STATE_NORMAL
;
426 //e_LogWritefln('juststicked unsticked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s', [X, X+stickDX, stickDX, Y]);
431 if (nil <> g_Map_traceToNearest(X
, Y
, X
, stickEY
, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
)) then
433 if (ey
> stickEY
) then stickEY
:= ey
-1;
435 justSticked
:= false;
436 //e_LogWritefln('juststicked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s; stickEY=%s', [X, X+stickDX, stickDX, Y, stickEY]);
439 if (State
<> STATE_STICK
) or (Y
>= stickEY
)
440 //if not g_Map_CollidePanel(X-1, Y-1, 3, 3, (PANEL_STEP or PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR))
443 begin // Îòëèïëà - êàïàåò
446 State
:= STATE_NORMAL
;
448 else if (Random(200) = 100) then
449 begin // Ïðèëåïëåíà - íî âîçìîæíî ñòåêàåò
456 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
457 if not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
459 if isLiftUpAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
461 if (VelY
> -4-Random(3)) then VelY
-= 0.8;
462 if (abs(VelX
) > 0.1) then VelX
-= VelX
/10.0;
463 VelX
+= (Random
-Random
)*0.2;
466 if isLiftLeftAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
468 if (VelX
> -8-Random(3)) then VelX
-= 0.8;
471 if isLiftRightAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
472 begin // Ïîòîê âïðàâî
473 if (VelX
< 8+Random(3)) then VelX
+= 0.8;
478 pan
:= g_Map_PanelAtPoint(X
, Y
, GridTagLift
);
481 if ((pan
.PanelType
and PANEL_LIFTUP
) <> 0) then
483 if (VelY
> -4-Random(3)) then VelY
-= 0.8;
484 if (abs(VelX
) > 0.1) then VelX
-= VelX
/10.0;
485 VelX
+= (Random
-Random
)*0.2;
488 if ((pan
.PanelType
and PANEL_LIFTLEFT
) <> 0) then
490 if (VelX
> -8-Random(3)) then VelX
-= 0.8;
493 if ((pan
.PanelType
and PANEL_LIFTRIGHT
) <> 0) then
495 if (VelX
< 8+Random(3)) then VelX
+= 0.8;
504 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
505 if (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) then
507 if (State
<> STATE_STICK
) and
508 (not isBlockedAt(X
, Y
-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
509 (not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
510 (not isBlockedAt(X
, Y
+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
511 begin // Âèñèò â âîçäóõå - êàïàåò
514 State
:= STATE_NORMAL
;
518 if (State
<> STATE_STICK
) and (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) then
520 // Âèñèò â âîçäóõå - êàïàåò
521 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
525 State
:= STATE_NORMAL
;
530 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
534 pan
:= g_Map_traceToNearest(X
, Y
, X
+dX
, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
), @ex
, @ey
);
544 State
:= STATE_STICK
;
546 if (dX
> 0) then stickDX
:= 1 else stickDX
:= -1;
548 if (X
< 0) or (X
>= w
) then begin die(); exit
; end;
553 if (dY
< 0) or not onGround
then
555 pan
:= g_Map_traceToNearest(X
, Y
, X
, Y
+dY
, (GridTagWall
or GridTagDoor
or GridTagStep
), @ex
, @ey
);
565 if (dY
> 0) and (State
<> STATE_STICK
) then
567 State
:= STATE_NORMAL
;
571 State
:= STATE_STICK
;
572 if (g_Map_PanelAtPoint(X
-1, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
)) <> nil) then stickDX
:= -1
573 else if (g_Map_PanelAtPoint(X
+1, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
)) <> nil) then stickDX
:= 1
578 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
580 if (Y
< 0) or (Y
>= h
) then begin die(); exit
; end;
586 if (dX
> 0) then s
:= 1 else s
:= -1;
590 if (X
+s
>= w
) or (X
+s
<= 0) then begin die(); break
; end;
591 //c := gCollideMap[Y, X+s];
592 if isBlockedAt(X
+s
, Y
) {ByteBool(c and MARK_BLOCKED)} then
598 State
:= STATE_STICK
;
608 if (dY
> 0) then s
:= 1 else s
:= -1;
612 if (Y
+s
>= h
) or (Y
+s
<= 0) then begin die(); break
; end;
613 //c := gCollideMap[Y+s, X];
614 if isBlockedAt(X
, Y
+s
) {ByteBool(c and MARK_BLOCKED)} then
620 if (s
> 0) and (State
<> STATE_STICK
) then State
:= STATE_NORMAL
else State
:= STATE_STICK
;
621 justSticked
:= (State
= STATE_STICK
);
633 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
634 begin // Ñòåíà/äâåðü/ãðàíèöà
650 // Êðîâü ðàñòâîðÿåòñÿ â æèäêîñòè:
651 if isLiquidAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
654 Alpha
:= 255-trunc((255.0*Time
)/LiveTime
);
660 // ////////////////////////////////////////////////////////////////////////// //
661 procedure TParticle
.thinkerWater ();
664 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
673 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
675 h
:= gMapInfo
.Height
;
678 //TODO: trace wall end when water becomes stick
679 if (State
= STATE_STICK
) and (Random(30) = 15) then
680 begin // Ñòåêàåò/îòëèïàåò
683 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
684 if (not isBlockedAt(X
-1, Y
) {ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)}) and
685 (not isBlockedAt(X
+1, Y
) {ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)}) then
686 State
:= STATE_NORMAL
;
688 if (stickDX
= 0) then
690 // no walls around, drop
691 State
:= STATE_NORMAL
;
697 if not mapGrid
.traceOrthoRayWhileIn(ex
, ey
, X
+stickDX
, Y
, X
+stickDX
, mapGrid
.gridY0
+mapGrid
.gridHeight
, GridTagWall
or GridTagDoor
or GridTagStep
) then
700 State
:= STATE_NORMAL
;
701 //e_LogWritefln('juststicked unsticked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s', [X, X+stickDX, stickDX, Y]);
706 justSticked
:= false;
707 if (nil <> g_Map_traceToNearest(X
, Y
, X
, stickEY
, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
)) then
709 if (ey
> stickEY
) then stickEY
:= ey
-1;
711 //e_LogWritefln('juststicked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s; stickEY=%s', [X, X+stickDX, stickDX, Y, stickEY]);
716 if (Y
>= stickEY
) then State
:= STATE_NORMAL
;
718 //if not g_Map_CollidePanel(X-1, Y-1, 3, 3, (PANEL_STEP or PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR))
724 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
725 if not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
727 if isLiftUpAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
729 if VelY
> -4-Random(3) then
731 if Abs(VelX
) > 0.1 then
732 VelX
:= VelX
- VelX
/10.0;
733 VelX
:= VelX
+ (Random
-Random
)*0.2;
736 if isLiftLeftAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
738 if VelX
> -8-Random(3) then
742 if isLiftRightAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
743 begin // Ïîòîê âïðàâî
744 if VelX
< 8+Random(3) then
750 pan
:= g_Map_PanelAtPoint(X
, Y
, (GridTagAcid1
or GridTagAcid2
or GridTagWater
or GridTagLift
));
753 if ((pan
.tag
and (GridTagAcid1
or GridTagAcid2
or GridTagWater
)) <> 0) then begin die(); exit
; end;
754 if ((pan
.PanelType
and PANEL_LIFTUP
) <> 0) then
756 if (VelY
> -4-Random(3)) then VelY
-= 0.8;
757 if (Abs(VelX
) > 0.1) then VelX
-= VelX
/10.0;
758 VelX
+= (Random
-Random
)*0.2;
761 if ((pan
.PanelType
and PANEL_LIFTLEFT
) <> 0) then
763 if (VelX
> -8-Random(3)) then VelX
-= 0.8;
766 if ((pan
.PanelType
and PANEL_LIFTRIGHT
) <> 0) then
768 if (VelX
< 8+Random(3)) then VelX
+= 0.8;
777 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
778 if (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) then
780 if (State
<> STATE_STICK
) and
781 (not isBlockedAt(X
, Y
-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
782 (not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
783 (not isBlockedAt(X
, Y
+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
784 begin // Âèñèò â âîçäóõå - êàïàåò
787 State
:= STATE_NORMAL
;
791 if (State
<> STATE_STICK
) and (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) then
793 // Âèñèò â âîçäóõå - êàïàåò
794 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
798 State
:= STATE_NORMAL
;
803 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
807 pan
:= g_Map_traceToNearest(X
, Y
, X
+dX
, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
);
813 if (dY
> 0) and ((pan
.tag
and (GridTagAcid1
or GridTagAcid2
or GridTagWater
)) <> 0) then begin die(); exit
; end;
815 if ((pan
.tag
and (GridTagWall
or GridTagDoor
or GridTagStep
)) <> 0) then
821 State
:= STATE_STICK
;
823 if (dX
> 0) then stickDX
:= 1 else stickDX
:= -1;
826 if (X
< 0) or (X
>= gMapInfo
.Width
) then begin die(); exit
; end;
831 if (dY
< 0) or not onGround
then
833 pan
:= g_Map_traceToNearest(X
, Y
, X
, Y
+dY
, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
);
839 if (dY
> 0) and ((pan
.tag
and (GridTagAcid1
or GridTagAcid2
or GridTagWater
)) <> 0) then begin die(); exit
; end;
841 if ((pan
.tag
and (GridTagWall
or GridTagDoor
or GridTagStep
)) <> 0) then
847 if (dY
> 0) and (State
<> STATE_STICK
) then
849 State
:= STATE_NORMAL
;
853 State
:= STATE_STICK
;
854 if (g_Map_PanelAtPoint(X
-1, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
)) <> nil) then stickDX
:= -1
855 else if (g_Map_PanelAtPoint(X
+1, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
)) <> nil) then stickDX
:= 1
861 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
863 if (Y
< 0) or (Y
>= gMapInfo
.Height
) then begin die(); exit
; end;
869 if (dX
> 0) then s
:= 1 else s
:= -1;
870 for b
:= 1 to Abs(dX
) do
873 if (X
+s
>= w
) or (X
+s
<= 0) then begin die(); break
;end;
874 //c := gCollideMap[Y, X+s];
875 // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò?
876 if isLiquidAt(X
+s
, Y
) {ByteBool(c and MARK_LIQUID)} and (dY
> 0) then begin die(); break
; end;
877 if isBlockedAt(X
+s
, Y
) {ByteBool(c and MARK_BLOCKED)} then
883 State
:= STATE_STICK
;
893 if (dY
> 0) then s
:= 1 else s
:= -1;
894 for b
:= 1 to Abs(dY
) do
896 // Ñíèçó/ñâåðõó ãðàíèöà
897 if (Y
+s
>= h
) or (Y
+s
<= 0) then begin die(); break
; end;
898 //c := gCollideMap[Y+s, X];
899 // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
900 if isLiquidAt(X
, Y
+s
) {ByteBool(c and MARK_LIQUID)} and (dY
> 0) then begin die(); break
; end;
901 if isBlockedAt(X
, Y
+s
) {ByteBool(c and MARK_BLOCKED)} then
907 if (s
> 0) and (State
<> STATE_STICK
) then State
:= STATE_NORMAL
else State
:= STATE_STICK
;
908 justSticked
:= (State
= STATE_STICK
);
923 // ////////////////////////////////////////////////////////////////////////// //
924 procedure TParticle
.thinkerSpark ();
927 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
938 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
939 if (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) then
941 pan
:= g_Map_traceToNearest(X
, Y
-1, X
, Y
+1, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
);
944 if (Abs(VelX
) < 0.1) and (Abs(VelY
) < 0.1) and
945 (not isBlockedAt(X
, Y
-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
946 (not isBlockedAt(X
, Y
) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
947 (not isBlockedAt(X
, Y
+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
948 begin // Âèñèò â âîçäóõå
956 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
957 pan
:= g_Map_traceToNearest(X
, Y
, X
+dX
, Y
, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
);
958 //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);
964 if ((pan
.tag
and (GridTagAcid1
or GridTagAcid2
or GridTagWater
)) <> 0) then begin die(); exit
; end;
968 if (X
< 0) or (X
>= gMapInfo
.Width
) then begin die(); exit
; end;
970 if (dX
> 0) then s
:= 1 else s
:= -1;
974 if (X
+s
>= gMapInfo
.Width
) or (X
+s
<= 0) then begin die(); break
; end;
975 //c := gCollideMap[Y, X+s];
976 if isBlockedAt(X
+s
, Y
) {ByteBool(c and MARK_BLOCKED)} then
977 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
983 if not isAnythingAt(X
+s
, Y
) {c = MARK_FREE} then
996 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
997 if (dY
< 0) or not onGround
then
999 pan
:= g_Map_traceToNearest(X
, Y
, X
, Y
+dY
, (GridTagWall
or GridTagDoor
or GridTagStep
or GridTagAcid1
or GridTagAcid2
or GridTagWater
), @ex
, @ey
);
1005 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]);
1009 if (pan
<> nil) then
1012 if ((pan
.tag
and (GridTagAcid1
or GridTagAcid2
or GridTagWater
)) <> 0) then begin die(); exit
; end;
1016 AccelY
:= abs(AccelY
);
1026 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1028 if (Y
< 0) or (Y
>= gMapInfo
.Height
) then begin die(); exit
; end;
1030 if (dY
> 0) then s
:= 1 else s
:= -1;
1034 if (Y
+s
>= gMapInfo
.Height
) or (Y
+s
<= 0) then begin die(); break
; end;
1035 //c := gCollideMap[Y+s, X];
1036 if isBlockedAt(X
, Y
+s
) {ByteBool(c and MARK_BLOCKED)} then
1037 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1041 AccelY
:= Abs(AccelY
);
1043 else // Èëè íå ïàäàåò
1054 if not isAnythingAt(X
, Y
+s
) {c = MARK_FREE} then
1065 if (VelX
<> 0.0) then VelX
+= AccelX
;
1067 if (VelY
<> 0.0) then
1069 if (AccelY
< 10) then AccelY
+= 0.08;
1076 // ////////////////////////////////////////////////////////////////////////// //
1077 procedure TParticle
.thinkerBubble ();
1084 h
:= gMapInfo
.Height
;
1095 for b
:= 1 to Abs(dY
) do
1097 if (Y
+s
>= h
) or (Y
+s
<= 0) then begin die(); break
; end;
1100 if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
1101 begin // Óæå íå æèäêîñòü
1102 State := STATE_FREE;
1106 // we traced liquid before, so don't bother checking
1107 if (Y
+s
<= liquidTopY
) then begin die(); break
; end;
1114 VelY
:= VelY
+ AccelY
;
1120 // ////////////////////////////////////////////////////////////////////////// //
1121 procedure g_GFX_SparkVel (fX
, fY
: Integer; Count
: Word; VX
, VY
: Integer; DevX
, DevY
: Byte);
1128 if not gpart_dbg_enabled
then Exit
;
1129 l
:= Length(Particles
);
1131 if Count
> l
then Count
:= l
;
1133 DevX1
:= DevX
div 2;
1135 DevY1
:= DevY
div 2;
1138 for a
:= 1 to Count
do
1140 with Particles
[CurrentParticle
] do
1142 X
:= fX
-DevX1
+Random(DevX2
);
1143 Y
:= fY
-DevY1
+Random(DevY2
);
1145 VelX
:= VX
+ (Random
-Random
)*3;
1146 VelY
:= VY
+ (Random
-Random
)*3;
1154 AccelX
:= -Sign(VelX
)*Random
/100;
1158 Green
:= 100+Random(155);
1162 State
:= STATE_NORMAL
;
1164 LiveTime
:= 30+Random(60);
1165 ParticleType
:= PARTICLE_SPARK
;
1166 justSticked
:= false;
1167 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1171 if CurrentParticle
+2 > MaxParticles
then
1172 CurrentParticle
:= 0
1174 CurrentParticle
:= CurrentParticle
+1;
1179 procedure g_GFX_Blood(fX
, fY
: Integer; Count
: Word; vx
, vy
: Integer;
1180 DevX
, DevY
: Word; CR
, CG
, CB
: Byte; Kind
: Byte = BLOOD_NORMAL
);
1189 if not gpart_dbg_enabled
then Exit
;
1190 if Kind
= BLOOD_SPARKS
then
1192 g_GFX_SparkVel(fX
, fY
, 2 + Random(2), -VX
div 2, -VY
div 2, DevX
, DevY
);
1195 l
:= Length(Particles
);
1201 DevX1
:= DevX
div 2;
1203 DevY1
:= DevY
div 2;
1206 for a
:= 1 to Count
do
1208 with Particles
[CurrentParticle
] do
1210 X
:= fX
- DevX1
+ Random(DevX2
);
1211 Y
:= fY
- DevY1
+ Random(DevY2
);
1214 if (X < 0) or (X > gMapInfo.Width-1) or
1215 (Y < 0) or (Y > gMapInfo.Height-1) or
1216 ByteBool(gCollideMap[Y, X] and MARK_WALL) then
1219 if isWallAt(X
, Y
) then continue
;
1221 VelX
:= vx
+ (Random
-Random
)*3;
1222 VelY
:= vy
+ (Random
-Random
)*3;
1230 AccelX
:= -Sign(VelX
)*Random
/100;
1233 CRnd
:= 20*Random(6);
1236 CC
:= CR
+ CRnd
- 50;
1237 if CC
< 0 then CC
:= 0;
1238 if CC
> 255 then CC
:= 255;
1244 CC
:= CG
+ CRnd
- 50;
1245 if CC
< 0 then CC
:= 0;
1246 if CC
> 255 then CC
:= 255;
1252 CC
:= CB
+ CRnd
- 50;
1253 if CC
< 0 then CC
:= 0;
1254 if CC
> 255 then CC
:= 255;
1261 State
:= STATE_NORMAL
;
1263 LiveTime
:= 120+Random(40);
1264 ParticleType
:= PARTICLE_BLOOD
;
1265 justSticked
:= false;
1266 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1271 if CurrentParticle
>= MaxParticles
-1 then
1272 CurrentParticle
:= 0
1274 CurrentParticle
:= CurrentParticle
+1;
1279 procedure g_GFX_Spark(fX
, fY
: Integer; Count
: Word; Angle
: SmallInt; DevX
, DevY
: Byte);
1285 BaseVelX
, BaseVelY
: Single;
1288 if not gpart_dbg_enabled
then Exit
;
1289 l
:= Length(Particles
);
1295 Angle
:= 360 - Angle
;
1297 DevX1
:= DevX
div 2;
1299 DevY1
:= DevY
div 2;
1302 b
:= DegToRad(Angle
);
1304 BaseVelY
:= 1.6*sin(b
);
1305 if Abs(BaseVelX
) < 0.01 then
1307 if Abs(BaseVelY
) < 0.01 then
1309 for a
:= 1 to Count
do
1311 with Particles
[CurrentParticle
] do
1313 X
:= fX
-DevX1
+Random(DevX2
);
1314 Y
:= fY
-DevY1
+Random(DevY2
);
1316 VelX
:= BaseVelX
*Random
;
1317 VelY
:= BaseVelY
-Random
;
1322 Green
:= 100+Random(155);
1326 State
:= STATE_NORMAL
;
1328 LiveTime
:= 30+Random(60);
1329 ParticleType
:= PARTICLE_SPARK
;
1330 justSticked
:= false;
1331 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1335 if CurrentParticle
+2 > MaxParticles
then
1336 CurrentParticle
:= 0
1338 CurrentParticle
:= CurrentParticle
+1;
1342 procedure g_GFX_Water(fX
, fY
: Integer; Count
: Word; fVelX
, fVelY
: Single; DevX
, DevY
, Color
: Byte);
1349 if not gpart_dbg_enabled
then Exit
;
1350 l
:= Length(Particles
);
1356 if Abs(fVelX
) < 3.0 then
1357 fVelX
:= 3.0 - 6.0*Random
;
1359 DevX1
:= DevX
div 2;
1361 DevY1
:= DevY
div 2;
1364 for a
:= 1 to Count
do
1366 with Particles
[CurrentParticle
] do
1368 X
:= fX
-DevX1
+Random(DevX2
);
1369 Y
:= fY
-DevY1
+Random(DevY2
);
1371 if Abs(fVelX
) < 0.5 then
1372 VelX
:= 1.0 - 2.0*Random
1374 VelX
:= fVelX
*Random
;
1375 if Random(10) < 7 then
1377 VelY
:= fVelY
*Random
;
1384 Red
:= 155 + Random(9)*10;
1385 Green
:= Trunc(150*Random
);
1390 Red
:= Trunc(150*Random
);
1391 Green
:= 175 + Random(9)*10;
1396 Red
:= Trunc(200*Random
);
1398 Blue
:= 175 + Random(9)*10;
1402 Red
:= 90 + Random(12)*10;
1410 State
:= STATE_NORMAL
;
1412 LiveTime
:= 60+Random(60);
1413 ParticleType
:= PARTICLE_WATER
;
1414 justSticked
:= false;
1415 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1419 if CurrentParticle
+2 > MaxParticles
then
1420 CurrentParticle
:= 0
1422 CurrentParticle
:= CurrentParticle
+1;
1426 procedure g_GFX_SimpleWater(fX
, fY
: Integer; Count
: Word; fVelX
, fVelY
: Single; DefColor
, CR
, CG
, CB
: Byte);
1431 if not gpart_dbg_enabled
then Exit
;
1432 l
:= Length(Particles
);
1438 for a
:= 1 to Count
do
1440 with Particles
[CurrentParticle
] do
1453 Red
:= 155 + Random(9)*10;
1454 Green
:= Trunc(150*Random
);
1459 Red
:= Trunc(150*Random
);
1460 Green
:= 175 + Random(9)*10;
1465 Red
:= Trunc(200*Random
);
1467 Blue
:= 175 + Random(9)*10;
1469 4: // Ñâîé öâåò, ñâåòëåå
1471 Red
:= 20 + Random(19)*10;
1474 Red
:= Min(Red
+ CR
, 255);
1475 Green
:= Min(Green
+ CG
, 255);
1476 Blue
:= Min(Blue
+ CB
, 255);
1478 5: // Ñâîé öâåò, òåìíåå
1480 Red
:= 20 + Random(19)*10;
1483 Red
:= Max(CR
- Red
, 0);
1484 Green
:= Max(CG
- Green
, 0);
1485 Blue
:= Max(CB
- Blue
, 0);
1489 Red
:= 90 + Random(12)*10;
1497 State
:= STATE_NORMAL
;
1499 LiveTime
:= 60+Random(60);
1500 ParticleType
:= PARTICLE_WATER
;
1501 justSticked
:= false;
1502 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1506 if CurrentParticle
+2 > MaxParticles
then
1507 CurrentParticle
:= 0
1509 CurrentParticle
:= CurrentParticle
+1;
1514 {.$DEFINE D2F_DEBUG_BUBBLES}
1515 procedure g_GFX_Bubbles(fX
, fY
: Integer; Count
: Word; DevX
, DevY
: Byte);
1520 l
, liquidx
: Integer;
1521 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1526 if not gpart_dbg_enabled
then Exit
;
1527 l
:= Length(Particles
);
1533 DevX1
:= DevX
div 2;
1535 DevY1
:= DevY
div 2;
1538 for a
:= 1 to Count
do
1540 with Particles
[CurrentParticle
] do
1542 X
:= fX
-DevX1
+Random(DevX2
);
1543 Y
:= fY
-DevY1
+Random(DevY2
);
1545 if (X
>= gMapInfo
.Width
) or (X
<= 0) or
1546 (Y
>= gMapInfo
.Height
) or (Y
<= 0) then
1550 // don't spawn bubbles outside of the liquid
1551 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1555 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1556 // tracer will return `false` if we started outside of the liquid
1558 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1559 stt
:= curTimeMicro();
1560 ptr
:= mapGrid
.traceOrthoRayWhileIn(liquidx
, liquidTopY
, X
, Y
, X
, 0, GridTagWater
or GridTagAcid1
or GridTagAcid2
);
1561 stt
:= curTimeMicro()-stt
;
1562 e_LogWritefln('traceOrthoRayWhileIn: time=%s (%s); liquidTopY=%s', [Integer(stt
), ptr
, liquidTopY
]);
1564 stt
:= curTimeMicro();
1565 nptr
:= g_Map_TraceLiquidNonPrecise(X
, Y
, 0, -8, liquidx
, liquidTopY
);
1566 stt
:= curTimeMicro()-stt
;
1567 e_LogWritefln('g_Map_TraceLiquidNonPrecise: time=%s (%s); liquidTopY=%s', [Integer(stt
), nptr
, liquidTopY
]);
1568 if not nptr
then continue
;
1570 if not g_Map_TraceLiquidNonPrecise(X
, Y
, 0, -8, liquidx
, liquidTopY
) then continue
;
1583 State
:= STATE_NORMAL
;
1586 ParticleType
:= PARTICLE_BUBBLES
;
1587 justSticked
:= false;
1588 onGround
:= (VelY
>= 0) and g_Map_HasAnyPanelAtPoint(X
, Y
+1, (PANEL_WALL
or PANEL_CLOSEDOOR
or PANEL_STEP
));
1592 if CurrentParticle
+2 > MaxParticles
then
1593 CurrentParticle
:= 0
1595 CurrentParticle
:= CurrentParticle
+1;
1599 procedure g_GFX_SetMax(Count
: Integer);
1603 if Count
> 50000 then Count
:= 50000;
1604 if (Count
< 1) then Count
:= 1;
1606 SetLength(Particles
, Count
);
1607 for a
:= 0 to High(Particles
) do Particles
[a
].die();
1608 MaxParticles
:= Count
;
1609 //if CurrentParticle >= Count then
1610 CurrentParticle
:= 0;
1613 function g_GFX_GetMax(): Integer;
1615 Result
:= MaxParticles
;
1618 function FindOnceAnim
: DWORD
;
1622 if OnceAnims
<> nil then
1623 for i
:= 0 to High(OnceAnims
) do
1624 if OnceAnims
[i
].Animation
= nil then
1630 if OnceAnims
= nil then
1632 SetLength(OnceAnims
, 16);
1637 Result
:= High(OnceAnims
) + 1;
1638 SetLength(OnceAnims
, Length(OnceAnims
) + 16);
1642 procedure g_GFX_OnceAnim(X
, Y
: Integer; Anim
: TAnimation
; AnimType
: Byte = 0);
1646 if not gpart_dbg_enabled
then Exit
;
1650 find_id
:= FindOnceAnim();
1652 OnceAnims
[find_id
].AnimType
:= AnimType
;
1653 OnceAnims
[find_id
].Animation
:= TAnimation
.Create(Anim
.FramesID
, Anim
.Loop
, Anim
.Speed
);
1654 OnceAnims
[find_id
].Animation
.Blending
:= Anim
.Blending
;
1655 OnceAnims
[find_id
].Animation
.Alpha
:= Anim
.Alpha
;
1656 OnceAnims
[find_id
].X
:= X
;
1657 OnceAnims
[find_id
].Y
:= Y
;
1660 procedure g_GFX_Update();
1666 if not gpart_dbg_enabled
then exit
;
1667 if Particles
<> nil then
1669 w
:= gMapInfo
.Width
;
1670 h
:= gMapInfo
.Height
;
1672 len
:= High(Particles
);
1674 for a
:= 0 to len
do
1676 if Particles
[a
].alive
then
1678 with Particles
[a
] do
1680 if (Time
= LiveTime
) then begin die(); continue
; end;
1681 if (X
+1 >= w
) or (Y
+1 >= h
) or (X
<= 0) or (Y
<= 0) then begin die(); end;
1682 //if not alive then Continue;
1683 //e_WriteLog(Format('particle #%d: %d', [State, ParticleType]), MSG_NOTIFY);
1688 end; // Particles <> nil
1690 if OnceAnims
<> nil then
1692 for a
:= 0 to High(OnceAnims
) do
1693 if OnceAnims
[a
].Animation
<> nil then
1695 case OnceAnims
[a
].AnimType
of
1698 if Random(3) = 0 then
1699 OnceAnims
[a
].X
:= OnceAnims
[a
].X
-1+Random(3);
1700 if Random(2) = 0 then
1701 OnceAnims
[a
].Y
:= OnceAnims
[a
].Y
-Random(2);
1705 if OnceAnims
[a
].Animation
.Played
then
1707 OnceAnims
[a
].Animation
.Free();
1708 OnceAnims
[a
].Animation
:= nil;
1711 OnceAnims
[a
].Animation
.Update();
1716 procedure g_GFX_Draw();
1720 if Particles
<> nil then
1722 glDisable(GL_TEXTURE_2D
);
1726 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1730 len
:= High(Particles
);
1732 for a
:= 0 to len
do
1733 with Particles
[a
] do
1734 if alive
and (X
>= sX
) and (Y
>= sY
) and (X
<= sX
+sWidth
) and (sY
<= sY
+sHeight
) then
1736 glColor4ub(Red
, Green
, Blue
, Alpha
);
1737 glVertex2i(X
+ offsetX
, Y
+ offsetY
);
1742 glDisable(GL_BLEND
);
1745 if OnceAnims
<> nil then
1746 for a
:= 0 to High(OnceAnims
) do
1747 if OnceAnims
[a
].Animation
<> nil then
1748 with OnceAnims
[a
] do
1749 Animation
.Draw(X
, Y
, M_NONE
);