DEADSOFTWARE

more particle control options
[d2df-sdl.git] / src / game / g_gfx.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
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.
7 *
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.
12 *
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/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit g_gfx;
19 interface
21 uses
22 e_log, g_textures;
24 const
25 BLOOD_NORMAL = 0;
26 BLOOD_SPARKS = 1;
27 ONCEANIM_NONE = 0;
28 ONCEANIM_SMOKE = 1;
29 MARK_FREE = 0;
30 MARK_WALL = 1;
31 MARK_WATER = 2;
32 MARK_ACID = 4;
33 MARK_LIFTDOWN = 8;
34 MARK_LIFTUP = 16;
35 MARK_DOOR = 32;
36 MARK_LIFTLEFT = 64;
37 MARK_LIFTRIGHT = 128;
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();
62 var
63 gpart_dbg_enabled: Boolean = true;
64 gpart_dbg_phys_enabled: Boolean = true;
67 implementation
69 uses
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;
74 type
75 PParticle = ^TParticle;
76 TParticle = record
77 X, Y: Integer;
78 VelX, VelY: Single;
79 AccelX, AccelY: Single;
80 Red, Green, Blue: Byte;
81 Alpha: Byte;
82 Time, LiveTime: Word;
83 State: Byte;
84 ParticleType: Byte;
85 offsetX, offsetY: ShortInt;
86 end;
88 TOnceAnim = record
89 AnimType: Byte;
90 X, Y: Integer;
91 Animation: TAnimation;
92 end;
94 const
95 PARTICLE_BLOOD = 0;
96 PARTICLE_SPARK = 1;
97 PARTICLE_BUBBLES = 2;
98 PARTICLE_WATER = 3;
99 STATE_FREE = 0;
100 STATE_NORMAL = 1;
101 STATE_STICK = 2;
103 var
104 Particles: array of TParticle;
105 OnceAnims: array of TOnceAnim;
106 MaxParticles: Integer;
107 CurrentParticle: Integer;
110 function isBlockedAt (x, y: Integer): Boolean; inline;
111 begin
112 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
113 result := g_Map_HasAnyPanelAtPoint(x, y, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
114 end;
116 // ???
117 function isWallAt (x, y: Integer): Boolean; inline;
118 begin
119 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
120 result := g_Map_HasAnyPanelAtPoint(x, y, (PANEL_WALL or PANEL_STEP));
121 end;
123 function isLiftUpAt (x, y: Integer): Boolean; inline;
124 begin
125 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
126 result := g_Map_HasAnyPanelAtPoint(x, y, PANEL_LIFTUP);
127 end;
129 function isLiftDownAt (x, y: Integer): Boolean; inline;
130 begin
131 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
132 result := g_Map_HasAnyPanelAtPoint(x, y, PANEL_LIFTDOWN);
133 end;
135 function isLiftLeftAt (x, y: Integer): Boolean; inline;
136 begin
137 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
138 result := g_Map_HasAnyPanelAtPoint(x, y, PANEL_LIFTLEFT);
139 end;
141 function isLiftRightAt (x, y: Integer): Boolean; inline;
142 begin
143 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
144 result := g_Map_HasAnyPanelAtPoint(x, y, PANEL_LIFTRIGHT);
145 end;
147 function isLiquidAt (x, y: Integer): Boolean; inline;
148 begin
149 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
150 result := g_Map_HasAnyPanelAtPoint(x, y, (PANEL_WATER or PANEL_ACID1 or PANEL_ACID2));
151 end;
153 function isAnythingAt (x, y: Integer): Boolean; inline;
154 begin
155 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
156 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));
157 end;
160 procedure g_Mark(x, y, Width, Height: Integer; t: Byte; st: Boolean);
161 {$IF not DEFINED(HAS_COLLIDE_BITMAP)}
162 begin
163 end;
164 {$ELSE}
165 var
166 yy, y2, xx, x2: Integer;
167 begin
168 if x < 0 then
169 begin
170 Width := Width + x;
171 x := 0;
172 end;
174 if Width < 0 then
175 Exit;
177 if y < 0 then
178 begin
179 Height := Height + y;
180 y := 0;
181 end;
183 if Height < 0 then
184 Exit;
186 if x > gMapInfo.Width then
187 Exit;
188 if y > gMapInfo.Height then
189 Exit;
191 y2 := y + Height - 1;
192 if y2 > gMapInfo.Height then
193 y2 := gMapInfo.Height;
195 x2 := x + Width - 1;
196 if x2 > gMapInfo.Width then
197 x2 := gMapInfo.Width;
199 if st then
200 begin // Óñòàíîâèòü ïðèçíàê
201 for yy := y to y2 do
202 for xx := x to x2 do
203 gCollideMap[yy][xx] := gCollideMap[yy][xx] or t;
204 end
205 else
206 begin // Óáðàòü ïðèçíàê
207 t := not t;
208 for yy := y to y2 do
209 for xx := x to x2 do
210 gCollideMap[yy][xx] := gCollideMap[yy][xx] and t;
211 end;
212 end;
213 {$ENDIF}
216 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
217 procedure CreateCollideMap();
218 var
219 a: Integer;
220 begin
221 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
222 SetLength(gCollideMap, gMapInfo.Height+1);
223 for a := 0 to High(gCollideMap) do
224 SetLength(gCollideMap[a], gMapInfo.Width+1);
226 if gWater <> nil then
227 begin
228 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 2/6', 0, True);
229 for a := 0 to High(gWater) do
230 with gWater[a] do
231 g_Mark(X, Y, Width, Height, MARK_WATER, True);
232 end;
234 if gAcid1 <> nil then
235 begin
236 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 3/6', 0, True);
237 for a := 0 to High(gAcid1) do
238 with gAcid1[a] do
239 g_Mark(X, Y, Width, Height, MARK_ACID, True);
240 end;
242 if gAcid2 <> nil then
243 begin
244 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 4/6', 0, True);
245 for a := 0 to High(gAcid2) do
246 with gAcid2[a] do
247 g_Mark(X, Y, Width, Height, MARK_ACID, True);
248 end;
250 if gLifts <> nil then
251 begin
252 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 5/6', 0, True);
253 for a := 0 to High(gLifts) do
254 with gLifts[a] do
255 begin
256 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
258 if LiftType = 0 then
259 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
260 else if LiftType = 1 then
261 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
262 else if LiftType = 2 then
263 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
264 else if LiftType = 3 then
265 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True)
266 end;
267 end;
269 if gWalls <> nil then
270 begin
271 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 6/6', 0, True);
272 for a := 0 to High(gWalls) do
273 begin
274 if gWalls[a].Door then
275 begin
276 // Çàêðûòàÿ äâåðü:
277 if gWalls[a].Enabled then
278 with gWalls[a] do
279 g_Mark(X, Y, Width, Height, MARK_DOOR, True)
280 else // Îòêðûòàÿ äâåðü:
281 if gWalls[a].Enabled then
282 with gWalls[a] do
283 g_Mark(X, Y, Width, Height, MARK_DOOR, False);
284 end
285 else // Ñòåíà
286 with gWalls[a] do
287 g_Mark(X, Y, Width, Height, MARK_WALL, True);
288 end;
289 end;
290 end;
291 {$ENDIF}
294 procedure g_GFX_Init();
295 begin
296 //CreateCollideMap();
297 end;
300 procedure g_GFX_Free();
301 var
302 a: Integer;
303 begin
304 Particles := nil;
305 SetLength(Particles, MaxParticles);
306 for a := 0 to High(Particles) do Particles[a].State := STATE_FREE;
307 CurrentParticle := 0;
309 if OnceAnims <> nil then
310 begin
311 for a := 0 to High(OnceAnims) do
312 OnceAnims[a].Animation.Free();
314 OnceAnims := nil;
315 end;
316 end;
319 procedure CorrectOffsets(id: Integer);
320 var
321 part: PParticle;
322 begin
323 part := @Particles[id];
324 part.offsetX := 0;
325 part.offsetY := 0;
326 // check for upper wall
327 if isBlockedAt(part.X, part.Y-1) then part.offsetY := 1;
328 // check for left wall
329 if isBlockedAt(part.X-1, part.Y) then part.offsetX := 1;
330 end;
333 procedure g_GFX_SparkVel (fX, fY: Integer; Count: Word; VX, VY: Integer; DevX, DevY: Byte);
334 var
335 a: Integer;
336 DevX1, DevX2,
337 DevY1, DevY2: Byte;
338 l: Integer;
339 begin
340 l := Length(Particles);
341 if l = 0 then exit;
342 if Count > l then Count := l;
344 DevX1 := DevX div 2;
345 DevX2 := DevX + 1;
346 DevY1 := DevY div 2;
347 DevY2 := DevY + 1;
349 for a := 1 to Count do
350 begin
351 with Particles[CurrentParticle] do
352 begin
353 X := fX-DevX1+Random(DevX2);
354 Y := fY-DevY1+Random(DevY2);
356 VelX := VX + (Random-Random)*3;
357 VelY := VY + (Random-Random)*3;
359 if VelY > -4 then
360 if VelY-4 < -4 then
361 VelY := -4
362 else
363 VelY := VelY-4;
365 AccelX := -Sign(VelX)*Random/100;
366 AccelY := 0.8;
368 Red := 255;
369 Green := 100+Random(155);
370 Blue := 64;
371 Alpha := 255;
373 State := STATE_NORMAL;
374 Time := 0;
375 LiveTime := 30+Random(60);
376 ParticleType := PARTICLE_SPARK;
378 CorrectOffsets(CurrentParticle);
379 end;
381 if CurrentParticle+2 > MaxParticles then
382 CurrentParticle := 0
383 else
384 CurrentParticle := CurrentParticle+1;
385 end;
386 end;
389 procedure g_GFX_Blood(fX, fY: Integer; Count: Word; vx, vy: Integer;
390 DevX, DevY: Word; CR, CG, CB: Byte; Kind: Byte = BLOOD_NORMAL);
391 var
392 a: Integer;
393 DevX1, DevX2,
394 DevY1, DevY2: Word;
395 l: Integer;
396 CRnd: Byte;
397 CC: SmallInt;
398 begin
399 if Kind = BLOOD_SPARKS then
400 begin
401 g_GFX_SparkVel(fX, fY, 2 + Random(2), -VX div 2, -VY div 2, DevX, DevY);
402 Exit;
403 end;
404 l := Length(Particles);
405 if l = 0 then
406 Exit;
407 if Count > l then
408 Count := l;
410 DevX1 := DevX div 2;
411 DevX2 := DevX + 1;
412 DevY1 := DevY div 2;
413 DevY2 := DevY + 1;
415 for a := 1 to Count do
416 begin
417 with Particles[CurrentParticle] do
418 begin
419 X := fX - DevX1 + Random(DevX2);
420 Y := fY - DevY1 + Random(DevY2);
423 if (X < 0) or (X > gMapInfo.Width-1) or
424 (Y < 0) or (Y > gMapInfo.Height-1) or
425 ByteBool(gCollideMap[Y, X] and MARK_WALL) then
426 Continue;
428 if isWallAt(X, Y) then continue;
430 VelX := vx + (Random-Random)*3;
431 VelY := vy + (Random-Random)*3;
433 if VelY > -4 then
434 if VelY-4 < -4 then
435 VelY := -4
436 else
437 VelY := VelY-4;
439 AccelX := -Sign(VelX)*Random/100;
440 AccelY := 0.8;
442 CRnd := 20*Random(6);
443 if CR > 0 then
444 begin
445 CC := CR + CRnd - 50;
446 if CC < 0 then CC := 0;
447 if CC > 255 then CC := 255;
448 Red := CC;
449 end else
450 Red := 0;
451 if CG > 0 then
452 begin
453 CC := CG + CRnd - 50;
454 if CC < 0 then CC := 0;
455 if CC > 255 then CC := 255;
456 Green := CC;
457 end else
458 Green := 0;
459 if CB > 0 then
460 begin
461 CC := CB + CRnd - 50;
462 if CC < 0 then CC := 0;
463 if CC > 255 then CC := 255;
464 Blue := CC;
465 end else
466 Blue := 0;
468 Alpha := 255;
470 State := STATE_NORMAL;
471 Time := 0;
472 LiveTime := 120+Random(40);
473 ParticleType := PARTICLE_BLOOD;
475 CorrectOffsets(CurrentParticle);
476 end;
478 if CurrentParticle >= MaxParticles-1 then
479 CurrentParticle := 0
480 else
481 CurrentParticle := CurrentParticle+1;
482 end;
483 end;
486 procedure g_GFX_Spark(fX, fY: Integer; Count: Word; Angle: SmallInt; DevX, DevY: Byte);
487 var
488 a: Integer;
489 b: Single;
490 DevX1, DevX2,
491 DevY1, DevY2: Byte;
492 BaseVelX, BaseVelY: Single;
493 l: Integer;
494 begin
495 l := Length(Particles);
496 if l = 0 then
497 Exit;
498 if Count > l then
499 Count := l;
501 Angle := 360 - Angle;
503 DevX1 := DevX div 2;
504 DevX2 := DevX + 1;
505 DevY1 := DevY div 2;
506 DevY2 := DevY + 1;
508 b := DegToRad(Angle);
509 BaseVelX := cos(b);
510 BaseVelY := 1.6*sin(b);
511 if Abs(BaseVelX) < 0.01 then
512 BaseVelX := 0.0;
513 if Abs(BaseVelY) < 0.01 then
514 BaseVelY := 0.0;
515 for a := 1 to Count do
516 begin
517 with Particles[CurrentParticle] do
518 begin
519 X := fX-DevX1+Random(DevX2);
520 Y := fY-DevY1+Random(DevY2);
522 VelX := BaseVelX*Random;
523 VelY := BaseVelY-Random;
524 AccelX := VelX/3.0;
525 AccelY := VelY/5.0;
527 Red := 255;
528 Green := 100+Random(155);
529 Blue := 64;
530 Alpha := 255;
532 State := STATE_NORMAL;
533 Time := 0;
534 LiveTime := 30+Random(60);
535 ParticleType := PARTICLE_SPARK;
537 CorrectOffsets(CurrentParticle);
538 end;
540 if CurrentParticle+2 > MaxParticles then
541 CurrentParticle := 0
542 else
543 CurrentParticle := CurrentParticle+1;
544 end;
545 end;
547 procedure g_GFX_Water(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DevX, DevY, Color: Byte);
548 var
549 a: Integer;
550 DevX1, DevX2,
551 DevY1, DevY2: Byte;
552 l: Integer;
553 begin
554 l := Length(Particles);
555 if l = 0 then
556 Exit;
557 if Count > l then
558 Count := l;
560 if Abs(fVelX) < 3.0 then
561 fVelX := 3.0 - 6.0*Random;
563 DevX1 := DevX div 2;
564 DevX2 := DevX + 1;
565 DevY1 := DevY div 2;
566 DevY2 := DevY + 1;
568 for a := 1 to Count do
569 begin
570 with Particles[CurrentParticle] do
571 begin
572 X := fX-DevX1+Random(DevX2);
573 Y := fY-DevY1+Random(DevY2);
575 if Abs(fVelX) < 0.5 then
576 VelX := 1.0 - 2.0*Random
577 else
578 VelX := fVelX*Random;
579 if Random(10) < 7 then
580 VelX := -VelX;
581 VelY := fVelY*Random;
582 AccelX := 0.0;
583 AccelY := 0.8;
585 case Color of
586 1: // Êðàñíûé
587 begin
588 Red := 155 + Random(9)*10;
589 Green := Trunc(150*Random);
590 Blue := Green;
591 end;
592 2: // Çåëåíûé
593 begin
594 Red := Trunc(150*Random);
595 Green := 175 + Random(9)*10;
596 Blue := Red;
597 end;
598 3: // Ñèíèé
599 begin
600 Red := Trunc(200*Random);
601 Green := Red;
602 Blue := 175 + Random(9)*10;
603 end;
604 else // Ñåðûé
605 begin
606 Red := 90 + Random(12)*10;
607 Green := Red;
608 Blue := Red;
609 end;
610 end;
612 Alpha := 255;
614 State := STATE_NORMAL;
615 Time := 0;
616 LiveTime := 60+Random(60);
617 ParticleType := PARTICLE_WATER;
619 CorrectOffsets(CurrentParticle);
620 end;
622 if CurrentParticle+2 > MaxParticles then
623 CurrentParticle := 0
624 else
625 CurrentParticle := CurrentParticle+1;
626 end;
627 end;
629 procedure g_GFX_SimpleWater(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DefColor, CR, CG, CB: Byte);
630 var
631 a: Integer;
632 l: Integer;
633 begin
634 l := Length(Particles);
635 if l = 0 then
636 Exit;
637 if Count > l then
638 Count := l;
640 for a := 1 to Count do
641 begin
642 with Particles[CurrentParticle] do
643 begin
644 X := fX;
645 Y := fY;
647 VelX := fVelX;
648 VelY := fVelY;
649 AccelX := 0.0;
650 AccelY := 0.8;
652 case DefColor of
653 1: // Êðàñíûé
654 begin
655 Red := 155 + Random(9)*10;
656 Green := Trunc(150*Random);
657 Blue := Green;
658 end;
659 2: // Çåëåíûé
660 begin
661 Red := Trunc(150*Random);
662 Green := 175 + Random(9)*10;
663 Blue := Red;
664 end;
665 3: // Ñèíèé
666 begin
667 Red := Trunc(200*Random);
668 Green := Red;
669 Blue := 175 + Random(9)*10;
670 end;
671 4: // Ñâîé öâåò, ñâåòëåå
672 begin
673 Red := 20 + Random(19)*10;
674 Green := Red;
675 Blue := Red;
676 Red := Min(Red + CR, 255);
677 Green := Min(Green + CG, 255);
678 Blue := Min(Blue + CB, 255);
679 end;
680 5: // Ñâîé öâåò, òåìíåå
681 begin
682 Red := 20 + Random(19)*10;
683 Green := Red;
684 Blue := Red;
685 Red := Max(CR - Red, 0);
686 Green := Max(CG - Green, 0);
687 Blue := Max(CB - Blue, 0);
688 end;
689 else // Ñåðûé
690 begin
691 Red := 90 + Random(12)*10;
692 Green := Red;
693 Blue := Red;
694 end;
695 end;
697 Alpha := 255;
699 State := STATE_NORMAL;
700 Time := 0;
701 LiveTime := 60+Random(60);
702 ParticleType := PARTICLE_WATER;
704 CorrectOffsets(CurrentParticle);
705 end;
707 if CurrentParticle+2 > MaxParticles then
708 CurrentParticle := 0
709 else
710 CurrentParticle := CurrentParticle+1;
711 end;
712 end;
714 procedure g_GFX_Bubbles(fX, fY: Integer; Count: Word; DevX, DevY: Byte);
715 var
716 a: Integer;
717 DevX1, DevX2,
718 DevY1, DevY2: Byte;
719 l: Integer;
720 begin
721 l := Length(Particles);
722 if l = 0 then
723 Exit;
724 if Count > l then
725 Count := l;
727 DevX1 := DevX div 2;
728 DevX2 := DevX + 1;
729 DevY1 := DevY div 2;
730 DevY2 := DevY + 1;
732 for a := 1 to Count do
733 begin
734 with Particles[CurrentParticle] do
735 begin
736 X := fX-DevX1+Random(DevX2);
737 Y := fY-DevY1+Random(DevY2);
739 if (X >= gMapInfo.Width) or (X <= 0) or
740 (Y >= gMapInfo.Height) or (Y <= 0) then
741 Continue;
743 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
744 Continue;
746 VelX := 0;
747 VelY := -1-Random;
748 AccelX := 0;
749 AccelY := VelY/10;
751 Red := 255;
752 Green := 255;
753 Blue := 255;
754 Alpha := 255;
756 State := STATE_NORMAL;
757 Time := 0;
758 LiveTime := 65535;
759 ParticleType := PARTICLE_BUBBLES;
761 CorrectOffsets(CurrentParticle);
762 end;
764 if CurrentParticle+2 > MaxParticles then
765 CurrentParticle := 0
766 else
767 CurrentParticle := CurrentParticle+1;
768 end;
769 end;
771 procedure g_GFX_SetMax(Count: Integer);
772 var
773 a: Integer;
774 begin
775 if Count > 50000 then Count := 50000;
776 if (Count < 1) then Count := 1;
778 SetLength(Particles, Count);
779 for a := 0 to High(Particles) do Particles[a].State := STATE_FREE;
780 MaxParticles := Count;
781 //if CurrentParticle >= Count then
782 CurrentParticle := 0;
783 end;
785 function g_GFX_GetMax(): Integer;
786 begin
787 Result := MaxParticles;
788 end;
790 function FindOnceAnim: DWORD;
791 var
792 i: Integer;
793 begin
794 if OnceAnims <> nil then
795 for i := 0 to High(OnceAnims) do
796 if OnceAnims[i].Animation = nil then
797 begin
798 Result := i;
799 Exit;
800 end;
802 if OnceAnims = nil then
803 begin
804 SetLength(OnceAnims, 16);
805 Result := 0;
806 end
807 else
808 begin
809 Result := High(OnceAnims) + 1;
810 SetLength(OnceAnims, Length(OnceAnims) + 16);
811 end;
812 end;
814 procedure g_GFX_OnceAnim(X, Y: Integer; Anim: TAnimation; AnimType: Byte = 0);
815 var
816 find_id: DWORD;
817 begin
818 if Anim = nil then
819 Exit;
821 find_id := FindOnceAnim();
823 OnceAnims[find_id].AnimType := AnimType;
824 OnceAnims[find_id].Animation := TAnimation.Create(Anim.FramesID, Anim.Loop, Anim.Speed);
825 OnceAnims[find_id].Animation.Blending := Anim.Blending;
826 OnceAnims[find_id].Animation.Alpha := Anim.Alpha;
827 OnceAnims[find_id].X := X;
828 OnceAnims[find_id].Y := Y;
829 end;
831 procedure g_GFX_Update();
832 var
833 a: Integer;
834 w, h: Integer;
835 dX, dY: SmallInt;
836 b, len: Integer;
837 s: ShortInt;
838 //c: Byte;
839 begin
840 if not gpart_dbg_enabled then exit;
841 if Particles <> nil then
842 begin
843 w := gMapInfo.Width;
844 h := gMapInfo.Height;
846 len := High(Particles);
848 for a := 0 to len do
849 begin
850 if Particles[a].State <> STATE_FREE then
851 begin
852 with Particles[a] do
853 begin
854 if Time = LiveTime then State := STATE_FREE;
855 if (X+1 >= w) or (Y+1 >= h) or (X <= 0) or (Y <= 0) then State := STATE_FREE;
856 if State = STATE_FREE then Continue;
857 //e_WriteLog(Format('particle #%d: %d', [State, ParticleType]), MSG_NOTIFY);
859 case ParticleType of
860 PARTICLE_BLOOD:
861 begin
862 if gAdvBlood then
863 begin
864 if (State = STATE_STICK) then
866 if (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
867 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) and
868 (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
869 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED))
870 then
872 if (not isBlockedAt(X, Y-1)) and
873 (not isBlockedAt(X, Y+1)) and
874 (not isBlockedAt(X-1, Y)) and
875 (not isBlockedAt(X+1, Y))
876 then
877 begin // Îòëèïëà - êàïàåò
878 VelY := 0.5;
879 AccelY := 0.15;
880 State := STATE_NORMAL;
881 end
882 else
883 if Random(200) = 100 then
884 begin // Ïðèëåïëåíà - íî âîçìîæíî ñòåêàåò
885 VelY := 0.5;
886 AccelY := 0.15;
887 Continue;
888 end;
890 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
891 begin
892 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
893 begin // Ëèôò ââåðõ
894 if VelY > -4-Random(3) then
895 VelY := VelY - 0.8;
896 if Abs(VelX) > 0.1 then
897 VelX := VelX - VelX/10.0;
898 VelX := VelX + (Random-Random)*0.2;
899 AccelY := 0.15;
900 end;
901 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
902 begin // Ïîòîê âëåâî
903 if VelX > -8-Random(3) then
904 VelX := VelX - 0.8;
905 AccelY := 0.15;
906 end;
907 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
908 begin // Ïîòîê âïðàâî
909 if VelX < 8+Random(3) then
910 VelX := VelX + 0.8;
911 AccelY := 0.15;
912 end;
913 end;
915 dX := Round(VelX);
916 dY := Round(VelY);
918 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
919 if (State <> STATE_STICK) and
920 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
921 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
922 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
923 begin // Âèñèò â âîçäóõå - êàïàåò
924 VelY := 0.8;
925 AccelY := 0.5;
926 State := STATE_NORMAL;
927 end;
929 if dX <> 0 then
930 begin
931 if dX > 0 then
932 s := 1
933 else
934 s := -1;
936 dX := Abs(dX);
938 for b := 1 to dX do
939 begin
940 if (X+s >= w) or (X+s <= 0) then
941 begin
942 State := STATE_FREE;
943 Break;
944 end;
946 //c := gCollideMap[Y, X+s];
948 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
949 begin // Ñòåíà/äâåðü
950 VelX := 0;
951 VelY := 0;
952 AccelX := 0;
953 AccelY := 0;
954 State := STATE_STICK;
955 Break;
956 end;
958 X := X+s;
959 end;
960 end;
962 if dY <> 0 then
963 begin
964 if dY > 0 then
965 s := 1
966 else
967 s := -1;
969 dY := Abs(dY);
971 for b := 1 to dY do
972 begin
973 if (Y+s >= h) or (Y+s <= 0) then
974 begin
975 State := STATE_FREE;
976 Break;
977 end;
979 //c := gCollideMap[Y+s, X];
981 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
982 begin // Ñòåíà/äâåðü
983 VelX := 0;
984 VelY := 0;
985 AccelX := 0;
986 AccelY := 0;
987 if (s > 0) and (State <> STATE_STICK) then
988 State := STATE_NORMAL
989 else
990 State := STATE_STICK;
991 Break;
992 end;
994 Y := Y+s;
995 end;
996 end;
997 end // if gAdvBlood
998 else
999 begin
1000 dX := Round(VelX);
1001 dY := Round(VelY);
1003 if (X+dX >= w) or (Y+dY >= h) or
1004 (X+dX <= 0) or (Y+dY <= 0) or
1005 isBlockedAt(X+dX, Y+dY) {ByteBool(gCollideMap[Y+dY, X+dX] and MARK_BLOCKED)} then
1006 begin // Ñòåíà/äâåðü/ãðàíèöà
1007 State := STATE_FREE;
1008 VelX := 0;
1009 VelY := 0;
1010 end
1011 else
1012 begin
1013 Y := Y + dY;
1014 X := X + dX;
1015 end;
1016 end;
1018 VelX := VelX + AccelX;
1019 VelY := VelY + AccelY;
1021 // Êðîâü ðàñòâîðÿåòñÿ â æèäêîñòè:
1022 if isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1023 begin
1024 Inc(Time);
1026 Alpha := 255 - Trunc((255.0 * Time) / LiveTime);
1027 end;
1028 end;
1030 PARTICLE_SPARK:
1031 begin
1032 dX := Round(VelX);
1033 dY := Round(VelY);
1035 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) and
1036 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
1037 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
1038 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
1039 begin // Âèñèò â âîçäóõå
1040 VelY := 0.8;
1041 AccelY := 0.5;
1042 end;
1044 if dX <> 0 then
1045 begin
1046 if dX > 0 then
1047 s := 1
1048 else
1049 s := -1;
1051 dX := Abs(dX);
1053 for b := 1 to dX do
1054 begin
1055 if (X+s >= w) or (X+s <= 0) then
1056 begin
1057 State := STATE_FREE;
1058 Break;
1059 end;
1061 //c := gCollideMap[Y, X+s];
1063 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
1064 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1065 VelX := 0;
1066 AccelX := 0;
1067 Break;
1068 end
1069 else // Ïóñòî:
1070 if not isAnythingAt(X+s, Y) {c = MARK_FREE} then
1071 X := X + s
1072 else // Îñòàëüíîå:
1073 begin
1074 State := STATE_FREE;
1075 Break;
1076 end;
1077 end;
1078 end;
1080 if dY <> 0 then
1081 begin
1082 if dY > 0 then
1083 s := 1
1084 else
1085 s := -1;
1087 dY := Abs(dY);
1089 for b := 1 to dY do
1090 begin
1091 if (Y+s >= h) or (Y+s <= 0) then
1092 begin
1093 State := STATE_FREE;
1094 Break;
1095 end;
1097 //c := gCollideMap[Y+s, X];
1099 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
1100 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1101 if s < 0 then
1102 begin
1103 VelY := -VelY;
1104 AccelY := Abs(AccelY);
1105 end
1106 else // Èëè íå ïàäàåò
1107 begin
1108 VelX := 0;
1109 AccelX := 0;
1110 VelY := 0;
1111 AccelY := 0.8;
1112 end;
1114 Break;
1115 end
1116 else // Ïóñòî:
1117 if not isAnythingAt(X, Y+s) {c = MARK_FREE} then
1118 Y := Y + s
1119 else // Îñàëüíîå:
1120 begin
1121 State := STATE_FREE;
1122 Break;
1123 end;
1124 end;
1125 end;
1127 if VelX <> 0.0 then
1128 VelX := VelX + AccelX;
1129 if VelY <> 0.0 then
1130 begin
1131 if AccelY < 10 then
1132 AccelY := AccelY + 0.08;
1133 VelY := VelY + AccelY;
1134 end;
1136 Time := Time + 1;
1137 end;
1139 PARTICLE_WATER:
1140 begin
1141 if (State = STATE_STICK) and (Random(30) = 15) then
1142 begin // Ñòåêàåò/îòëèïàåò
1143 VelY := 0.5;
1144 AccelY := 0.15;
1145 if (not isBlockedAt(X-1, Y) {ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)}) and
1146 (not isBlockedAt(X+1, Y) {ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)}) then
1147 State := STATE_NORMAL;
1148 Continue;
1149 end;
1151 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
1152 begin
1153 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
1154 begin // Ëèôò ââåðõ
1155 if VelY > -4-Random(3) then
1156 VelY := VelY - 0.8;
1157 if Abs(VelX) > 0.1 then
1158 VelX := VelX - VelX/10.0;
1159 VelX := VelX + (Random-Random)*0.2;
1160 AccelY := 0.15;
1161 end;
1162 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
1163 begin // Ïîòîê âëåâî
1164 if VelX > -8-Random(3) then
1165 VelX := VelX - 0.8;
1166 AccelY := 0.15;
1167 end;
1168 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
1169 begin // Ïîòîê âïðàâî
1170 if VelX < 8+Random(3) then
1171 VelX := VelX + 0.8;
1172 AccelY := 0.15;
1173 end;
1174 end;
1176 dX := Round(VelX);
1177 dY := Round(VelY);
1179 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
1180 if (State <> STATE_STICK) and
1181 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
1182 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
1183 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
1184 begin // Âèñèò â âîçäóõå - êàïàåò
1185 VelY := 0.8;
1186 AccelY := 0.5;
1187 State := STATE_NORMAL;
1188 end;
1190 if dX <> 0 then
1191 begin
1192 if dX > 0 then
1193 s := 1
1194 else
1195 s := -1;
1197 for b := 1 to Abs(dX) do
1198 begin
1199 if (X+s >= w) or (X+s <= 0) then
1200 begin // Ñáîêó ãðàíèöà
1201 State := STATE_FREE;
1202 Break;
1203 end;
1205 //c := gCollideMap[Y, X+s];
1207 if isLiquidAt(X+s, Y) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then
1208 begin // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1209 State := STATE_FREE;
1210 Break;
1211 end;
1213 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
1214 begin // Ñòåíà/äâåðü
1215 VelX := 0;
1216 VelY := 0;
1217 AccelX := 0;
1218 AccelY := 0;
1219 State := STATE_STICK;
1220 Break;
1221 end;
1223 X := X+s;
1224 end;
1225 end;
1227 if dY <> 0 then
1228 begin
1229 if dY > 0 then
1230 s := 1
1231 else
1232 s := -1;
1234 for b := 1 to Abs(dY) do
1235 begin
1236 if (Y+s >= h) or (Y+s <= 0) then
1237 begin // Ñíèçó/ñâåðõó ãðàíèöà
1238 State := STATE_FREE;
1239 Break;
1240 end;
1242 //c := gCollideMap[Y+s, X];
1244 if isLiquidAt(X, Y+s) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then
1245 begin // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1246 State := STATE_FREE;
1247 Break;
1248 end;
1250 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
1251 begin // Ñòåíà/äâåðü
1252 VelX := 0;
1253 VelY := 0;
1254 AccelX := 0;
1255 AccelY := 0;
1256 if (s > 0) and (State <> STATE_STICK) then
1257 State := STATE_NORMAL
1258 else
1259 State := STATE_STICK;
1260 Break;
1261 end;
1263 Y := Y+s;
1264 end;
1265 end;
1267 VelX := VelX + AccelX;
1268 VelY := VelY + AccelY;
1270 Time := Time + 1;
1271 end;
1273 PARTICLE_BUBBLES:
1274 begin
1275 dY := Round(VelY);
1277 if dY <> 0 then
1278 begin
1279 if dY > 0 then
1280 s := 1
1281 else
1282 s := -1;
1284 for b := 1 to Abs(dY) do
1285 begin
1286 if (Y+s >= h) or (Y+s <= 0) then
1287 begin
1288 State := STATE_FREE;
1289 Break;
1290 end;
1292 if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
1293 begin // Óæå íå æèäêîñòü
1294 State := STATE_FREE;
1295 Break;
1296 end;
1298 Y := Y+s;
1299 end;
1300 end;
1302 if VelY > -4 then
1303 VelY := VelY + AccelY;
1305 Time := Time + 1;
1306 end;
1307 end; // case
1309 CorrectOffsets(a);
1310 end; // with
1311 end; // if
1312 end; // for
1313 end; // Particles <> nil
1315 if OnceAnims <> nil then
1316 begin
1317 for a := 0 to High(OnceAnims) do
1318 if OnceAnims[a].Animation <> nil then
1319 begin
1320 case OnceAnims[a].AnimType of
1321 ONCEANIM_SMOKE:
1322 begin
1323 if Random(3) = 0 then
1324 OnceAnims[a].X := OnceAnims[a].X-1+Random(3);
1325 if Random(2) = 0 then
1326 OnceAnims[a].Y := OnceAnims[a].Y-Random(2);
1327 end;
1328 end;
1330 if OnceAnims[a].Animation.Played then
1331 begin
1332 OnceAnims[a].Animation.Free();
1333 OnceAnims[a].Animation := nil;
1334 end
1335 else
1336 OnceAnims[a].Animation.Update();
1337 end;
1338 end;
1339 end;
1341 procedure g_GFX_Draw();
1342 var
1343 a, len: Integer;
1344 begin
1345 if Particles <> nil then
1346 begin
1347 glDisable(GL_TEXTURE_2D);
1348 glPointSize(2);
1350 glEnable(GL_BLEND);
1351 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1353 glBegin(GL_POINTS);
1355 len := High(Particles);
1357 for a := 0 to len do
1358 with Particles[a] do
1359 if (State <> STATE_FREE) and (X >= sX) and (Y >= sY) and
1360 (X <= sX+sWidth) and (sY <= sY+sHeight) then
1361 begin
1362 glColor4ub(Red, Green, Blue, Alpha);
1363 glVertex2i(X + offsetX, Y + offsetY);
1364 end;
1366 glEnd();
1368 glDisable(GL_BLEND);
1369 end;
1371 if OnceAnims <> nil then
1372 for a := 0 to High(OnceAnims) do
1373 if OnceAnims[a].Animation <> nil then
1374 with OnceAnims[a] do
1375 Animation.Draw(X, Y, M_NONE);
1376 end;
1378 end.