DEADSOFTWARE

no more "correct offsets" in particle engine
[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;
320 procedure CorrectOffsets(id: Integer); inline;
321 var
322 part: PParticle;
323 begin
324 part := @Particles[id];
325 part.offsetX := 0;
326 part.offsetY := 0;
327 // check for upper wall
328 if isBlockedAt(part.X, part.Y-1) then part.offsetY := 1;
329 // check for left wall
330 if isBlockedAt(part.X-1, part.Y) then part.offsetX := 1;
331 end;
335 procedure g_GFX_SparkVel (fX, fY: Integer; Count: Word; VX, VY: Integer; DevX, DevY: Byte);
336 var
337 a: Integer;
338 DevX1, DevX2,
339 DevY1, DevY2: Byte;
340 l: Integer;
341 begin
342 l := Length(Particles);
343 if l = 0 then exit;
344 if Count > l then Count := l;
346 DevX1 := DevX div 2;
347 DevX2 := DevX + 1;
348 DevY1 := DevY div 2;
349 DevY2 := DevY + 1;
351 for a := 1 to Count do
352 begin
353 with Particles[CurrentParticle] do
354 begin
355 X := fX-DevX1+Random(DevX2);
356 Y := fY-DevY1+Random(DevY2);
358 VelX := VX + (Random-Random)*3;
359 VelY := VY + (Random-Random)*3;
361 if VelY > -4 then
362 if VelY-4 < -4 then
363 VelY := -4
364 else
365 VelY := VelY-4;
367 AccelX := -Sign(VelX)*Random/100;
368 AccelY := 0.8;
370 Red := 255;
371 Green := 100+Random(155);
372 Blue := 64;
373 Alpha := 255;
375 State := STATE_NORMAL;
376 Time := 0;
377 LiveTime := 30+Random(60);
378 ParticleType := PARTICLE_SPARK;
380 {CorrectOffsets(CurrentParticle);}
381 end;
383 if CurrentParticle+2 > MaxParticles then
384 CurrentParticle := 0
385 else
386 CurrentParticle := CurrentParticle+1;
387 end;
388 end;
391 procedure g_GFX_Blood(fX, fY: Integer; Count: Word; vx, vy: Integer;
392 DevX, DevY: Word; CR, CG, CB: Byte; Kind: Byte = BLOOD_NORMAL);
393 var
394 a: Integer;
395 DevX1, DevX2,
396 DevY1, DevY2: Word;
397 l: Integer;
398 CRnd: Byte;
399 CC: SmallInt;
400 begin
401 if Kind = BLOOD_SPARKS then
402 begin
403 g_GFX_SparkVel(fX, fY, 2 + Random(2), -VX div 2, -VY div 2, DevX, DevY);
404 Exit;
405 end;
406 l := Length(Particles);
407 if l = 0 then
408 Exit;
409 if Count > l then
410 Count := l;
412 DevX1 := DevX div 2;
413 DevX2 := DevX + 1;
414 DevY1 := DevY div 2;
415 DevY2 := DevY + 1;
417 for a := 1 to Count do
418 begin
419 with Particles[CurrentParticle] do
420 begin
421 X := fX - DevX1 + Random(DevX2);
422 Y := fY - DevY1 + Random(DevY2);
425 if (X < 0) or (X > gMapInfo.Width-1) or
426 (Y < 0) or (Y > gMapInfo.Height-1) or
427 ByteBool(gCollideMap[Y, X] and MARK_WALL) then
428 Continue;
430 if isWallAt(X, Y) then continue;
432 VelX := vx + (Random-Random)*3;
433 VelY := vy + (Random-Random)*3;
435 if VelY > -4 then
436 if VelY-4 < -4 then
437 VelY := -4
438 else
439 VelY := VelY-4;
441 AccelX := -Sign(VelX)*Random/100;
442 AccelY := 0.8;
444 CRnd := 20*Random(6);
445 if CR > 0 then
446 begin
447 CC := CR + CRnd - 50;
448 if CC < 0 then CC := 0;
449 if CC > 255 then CC := 255;
450 Red := CC;
451 end else
452 Red := 0;
453 if CG > 0 then
454 begin
455 CC := CG + CRnd - 50;
456 if CC < 0 then CC := 0;
457 if CC > 255 then CC := 255;
458 Green := CC;
459 end else
460 Green := 0;
461 if CB > 0 then
462 begin
463 CC := CB + CRnd - 50;
464 if CC < 0 then CC := 0;
465 if CC > 255 then CC := 255;
466 Blue := CC;
467 end else
468 Blue := 0;
470 Alpha := 255;
472 State := STATE_NORMAL;
473 Time := 0;
474 LiveTime := 120+Random(40);
475 ParticleType := PARTICLE_BLOOD;
477 {CorrectOffsets(CurrentParticle);}
478 end;
480 if CurrentParticle >= MaxParticles-1 then
481 CurrentParticle := 0
482 else
483 CurrentParticle := CurrentParticle+1;
484 end;
485 end;
488 procedure g_GFX_Spark(fX, fY: Integer; Count: Word; Angle: SmallInt; DevX, DevY: Byte);
489 var
490 a: Integer;
491 b: Single;
492 DevX1, DevX2,
493 DevY1, DevY2: Byte;
494 BaseVelX, BaseVelY: Single;
495 l: Integer;
496 begin
497 l := Length(Particles);
498 if l = 0 then
499 Exit;
500 if Count > l then
501 Count := l;
503 Angle := 360 - Angle;
505 DevX1 := DevX div 2;
506 DevX2 := DevX + 1;
507 DevY1 := DevY div 2;
508 DevY2 := DevY + 1;
510 b := DegToRad(Angle);
511 BaseVelX := cos(b);
512 BaseVelY := 1.6*sin(b);
513 if Abs(BaseVelX) < 0.01 then
514 BaseVelX := 0.0;
515 if Abs(BaseVelY) < 0.01 then
516 BaseVelY := 0.0;
517 for a := 1 to Count do
518 begin
519 with Particles[CurrentParticle] do
520 begin
521 X := fX-DevX1+Random(DevX2);
522 Y := fY-DevY1+Random(DevY2);
524 VelX := BaseVelX*Random;
525 VelY := BaseVelY-Random;
526 AccelX := VelX/3.0;
527 AccelY := VelY/5.0;
529 Red := 255;
530 Green := 100+Random(155);
531 Blue := 64;
532 Alpha := 255;
534 State := STATE_NORMAL;
535 Time := 0;
536 LiveTime := 30+Random(60);
537 ParticleType := PARTICLE_SPARK;
539 {CorrectOffsets(CurrentParticle);}
540 end;
542 if CurrentParticle+2 > MaxParticles then
543 CurrentParticle := 0
544 else
545 CurrentParticle := CurrentParticle+1;
546 end;
547 end;
549 procedure g_GFX_Water(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DevX, DevY, Color: Byte);
550 var
551 a: Integer;
552 DevX1, DevX2,
553 DevY1, DevY2: Byte;
554 l: Integer;
555 begin
556 l := Length(Particles);
557 if l = 0 then
558 Exit;
559 if Count > l then
560 Count := l;
562 if Abs(fVelX) < 3.0 then
563 fVelX := 3.0 - 6.0*Random;
565 DevX1 := DevX div 2;
566 DevX2 := DevX + 1;
567 DevY1 := DevY div 2;
568 DevY2 := DevY + 1;
570 for a := 1 to Count do
571 begin
572 with Particles[CurrentParticle] do
573 begin
574 X := fX-DevX1+Random(DevX2);
575 Y := fY-DevY1+Random(DevY2);
577 if Abs(fVelX) < 0.5 then
578 VelX := 1.0 - 2.0*Random
579 else
580 VelX := fVelX*Random;
581 if Random(10) < 7 then
582 VelX := -VelX;
583 VelY := fVelY*Random;
584 AccelX := 0.0;
585 AccelY := 0.8;
587 case Color of
588 1: // Êðàñíûé
589 begin
590 Red := 155 + Random(9)*10;
591 Green := Trunc(150*Random);
592 Blue := Green;
593 end;
594 2: // Çåëåíûé
595 begin
596 Red := Trunc(150*Random);
597 Green := 175 + Random(9)*10;
598 Blue := Red;
599 end;
600 3: // Ñèíèé
601 begin
602 Red := Trunc(200*Random);
603 Green := Red;
604 Blue := 175 + Random(9)*10;
605 end;
606 else // Ñåðûé
607 begin
608 Red := 90 + Random(12)*10;
609 Green := Red;
610 Blue := Red;
611 end;
612 end;
614 Alpha := 255;
616 State := STATE_NORMAL;
617 Time := 0;
618 LiveTime := 60+Random(60);
619 ParticleType := PARTICLE_WATER;
621 {CorrectOffsets(CurrentParticle);}
622 end;
624 if CurrentParticle+2 > MaxParticles then
625 CurrentParticle := 0
626 else
627 CurrentParticle := CurrentParticle+1;
628 end;
629 end;
631 procedure g_GFX_SimpleWater(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DefColor, CR, CG, CB: Byte);
632 var
633 a: Integer;
634 l: Integer;
635 begin
636 l := Length(Particles);
637 if l = 0 then
638 Exit;
639 if Count > l then
640 Count := l;
642 for a := 1 to Count do
643 begin
644 with Particles[CurrentParticle] do
645 begin
646 X := fX;
647 Y := fY;
649 VelX := fVelX;
650 VelY := fVelY;
651 AccelX := 0.0;
652 AccelY := 0.8;
654 case DefColor of
655 1: // Êðàñíûé
656 begin
657 Red := 155 + Random(9)*10;
658 Green := Trunc(150*Random);
659 Blue := Green;
660 end;
661 2: // Çåëåíûé
662 begin
663 Red := Trunc(150*Random);
664 Green := 175 + Random(9)*10;
665 Blue := Red;
666 end;
667 3: // Ñèíèé
668 begin
669 Red := Trunc(200*Random);
670 Green := Red;
671 Blue := 175 + Random(9)*10;
672 end;
673 4: // Ñâîé öâåò, ñâåòëåå
674 begin
675 Red := 20 + Random(19)*10;
676 Green := Red;
677 Blue := Red;
678 Red := Min(Red + CR, 255);
679 Green := Min(Green + CG, 255);
680 Blue := Min(Blue + CB, 255);
681 end;
682 5: // Ñâîé öâåò, òåìíåå
683 begin
684 Red := 20 + Random(19)*10;
685 Green := Red;
686 Blue := Red;
687 Red := Max(CR - Red, 0);
688 Green := Max(CG - Green, 0);
689 Blue := Max(CB - Blue, 0);
690 end;
691 else // Ñåðûé
692 begin
693 Red := 90 + Random(12)*10;
694 Green := Red;
695 Blue := Red;
696 end;
697 end;
699 Alpha := 255;
701 State := STATE_NORMAL;
702 Time := 0;
703 LiveTime := 60+Random(60);
704 ParticleType := PARTICLE_WATER;
706 {CorrectOffsets(CurrentParticle);}
707 end;
709 if CurrentParticle+2 > MaxParticles then
710 CurrentParticle := 0
711 else
712 CurrentParticle := CurrentParticle+1;
713 end;
714 end;
716 procedure g_GFX_Bubbles(fX, fY: Integer; Count: Word; DevX, DevY: Byte);
717 var
718 a: Integer;
719 DevX1, DevX2,
720 DevY1, DevY2: Byte;
721 l: Integer;
722 begin
723 l := Length(Particles);
724 if l = 0 then
725 Exit;
726 if Count > l then
727 Count := l;
729 DevX1 := DevX div 2;
730 DevX2 := DevX + 1;
731 DevY1 := DevY div 2;
732 DevY2 := DevY + 1;
734 for a := 1 to Count do
735 begin
736 with Particles[CurrentParticle] do
737 begin
738 X := fX-DevX1+Random(DevX2);
739 Y := fY-DevY1+Random(DevY2);
741 if (X >= gMapInfo.Width) or (X <= 0) or
742 (Y >= gMapInfo.Height) or (Y <= 0) then
743 Continue;
745 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
746 Continue;
748 VelX := 0;
749 VelY := -1-Random;
750 AccelX := 0;
751 AccelY := VelY/10;
753 Red := 255;
754 Green := 255;
755 Blue := 255;
756 Alpha := 255;
758 State := STATE_NORMAL;
759 Time := 0;
760 LiveTime := 65535;
761 ParticleType := PARTICLE_BUBBLES;
763 {CorrectOffsets(CurrentParticle);}
764 end;
766 if CurrentParticle+2 > MaxParticles then
767 CurrentParticle := 0
768 else
769 CurrentParticle := CurrentParticle+1;
770 end;
771 end;
773 procedure g_GFX_SetMax(Count: Integer);
774 var
775 a: Integer;
776 begin
777 if Count > 50000 then Count := 50000;
778 if (Count < 1) then Count := 1;
780 SetLength(Particles, Count);
781 for a := 0 to High(Particles) do Particles[a].State := STATE_FREE;
782 MaxParticles := Count;
783 //if CurrentParticle >= Count then
784 CurrentParticle := 0;
785 end;
787 function g_GFX_GetMax(): Integer;
788 begin
789 Result := MaxParticles;
790 end;
792 function FindOnceAnim: DWORD;
793 var
794 i: Integer;
795 begin
796 if OnceAnims <> nil then
797 for i := 0 to High(OnceAnims) do
798 if OnceAnims[i].Animation = nil then
799 begin
800 Result := i;
801 Exit;
802 end;
804 if OnceAnims = nil then
805 begin
806 SetLength(OnceAnims, 16);
807 Result := 0;
808 end
809 else
810 begin
811 Result := High(OnceAnims) + 1;
812 SetLength(OnceAnims, Length(OnceAnims) + 16);
813 end;
814 end;
816 procedure g_GFX_OnceAnim(X, Y: Integer; Anim: TAnimation; AnimType: Byte = 0);
817 var
818 find_id: DWORD;
819 begin
820 if Anim = nil then
821 Exit;
823 find_id := FindOnceAnim();
825 OnceAnims[find_id].AnimType := AnimType;
826 OnceAnims[find_id].Animation := TAnimation.Create(Anim.FramesID, Anim.Loop, Anim.Speed);
827 OnceAnims[find_id].Animation.Blending := Anim.Blending;
828 OnceAnims[find_id].Animation.Alpha := Anim.Alpha;
829 OnceAnims[find_id].X := X;
830 OnceAnims[find_id].Y := Y;
831 end;
833 procedure g_GFX_Update();
834 var
835 a: Integer;
836 w, h: Integer;
837 dX, dY: SmallInt;
838 b, len: Integer;
839 s: ShortInt;
840 //c: Byte;
841 begin
842 if not gpart_dbg_enabled then exit;
843 if Particles <> nil then
844 begin
845 w := gMapInfo.Width;
846 h := gMapInfo.Height;
848 len := High(Particles);
850 for a := 0 to len do
851 begin
852 if Particles[a].State <> STATE_FREE then
853 begin
854 with Particles[a] do
855 begin
856 if Time = LiveTime then State := STATE_FREE;
857 if (X+1 >= w) or (Y+1 >= h) or (X <= 0) or (Y <= 0) then State := STATE_FREE;
858 if State = STATE_FREE then Continue;
859 //e_WriteLog(Format('particle #%d: %d', [State, ParticleType]), MSG_NOTIFY);
861 case ParticleType of
862 PARTICLE_BLOOD:
863 begin
864 if gAdvBlood then
865 begin
866 if (State = STATE_STICK) then
868 if (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
869 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) and
870 (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
871 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED))
872 then
874 if (not isBlockedAt(X, Y-1)) and
875 (not isBlockedAt(X, Y+1)) and
876 (not isBlockedAt(X-1, Y)) and
877 (not isBlockedAt(X+1, Y))
878 then
879 begin // Îòëèïëà - êàïàåò
880 VelY := 0.5;
881 AccelY := 0.15;
882 State := STATE_NORMAL;
883 end
884 else
885 if Random(200) = 100 then
886 begin // Ïðèëåïëåíà - íî âîçìîæíî ñòåêàåò
887 VelY := 0.5;
888 AccelY := 0.15;
889 Continue;
890 end;
892 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
893 begin
894 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
895 begin // Ëèôò ââåðõ
896 if VelY > -4-Random(3) then
897 VelY := VelY - 0.8;
898 if Abs(VelX) > 0.1 then
899 VelX := VelX - VelX/10.0;
900 VelX := VelX + (Random-Random)*0.2;
901 AccelY := 0.15;
902 end;
903 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
904 begin // Ïîòîê âëåâî
905 if VelX > -8-Random(3) then
906 VelX := VelX - 0.8;
907 AccelY := 0.15;
908 end;
909 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
910 begin // Ïîòîê âïðàâî
911 if VelX < 8+Random(3) then
912 VelX := VelX + 0.8;
913 AccelY := 0.15;
914 end;
915 end;
917 dX := Round(VelX);
918 dY := Round(VelY);
920 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
921 if (State <> STATE_STICK) and
922 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
923 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
924 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
925 begin // Âèñèò â âîçäóõå - êàïàåò
926 VelY := 0.8;
927 AccelY := 0.5;
928 State := STATE_NORMAL;
929 end;
931 if dX <> 0 then
932 begin
933 if dX > 0 then
934 s := 1
935 else
936 s := -1;
938 dX := Abs(dX);
940 for b := 1 to dX do
941 begin
942 if (X+s >= w) or (X+s <= 0) then
943 begin
944 State := STATE_FREE;
945 Break;
946 end;
948 //c := gCollideMap[Y, X+s];
950 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
951 begin // Ñòåíà/äâåðü
952 VelX := 0;
953 VelY := 0;
954 AccelX := 0;
955 AccelY := 0;
956 State := STATE_STICK;
957 Break;
958 end;
960 X := X+s;
961 end;
962 end;
964 if dY <> 0 then
965 begin
966 if dY > 0 then
967 s := 1
968 else
969 s := -1;
971 dY := Abs(dY);
973 for b := 1 to dY do
974 begin
975 if (Y+s >= h) or (Y+s <= 0) then
976 begin
977 State := STATE_FREE;
978 Break;
979 end;
981 //c := gCollideMap[Y+s, X];
983 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
984 begin // Ñòåíà/äâåðü
985 VelX := 0;
986 VelY := 0;
987 AccelX := 0;
988 AccelY := 0;
989 if (s > 0) and (State <> STATE_STICK) then
990 State := STATE_NORMAL
991 else
992 State := STATE_STICK;
993 Break;
994 end;
996 Y := Y+s;
997 end;
998 end;
999 end // if gAdvBlood
1000 else
1001 begin
1002 dX := Round(VelX);
1003 dY := Round(VelY);
1005 if (X+dX >= w) or (Y+dY >= h) or
1006 (X+dX <= 0) or (Y+dY <= 0) or
1007 isBlockedAt(X+dX, Y+dY) {ByteBool(gCollideMap[Y+dY, X+dX] and MARK_BLOCKED)} then
1008 begin // Ñòåíà/äâåðü/ãðàíèöà
1009 State := STATE_FREE;
1010 VelX := 0;
1011 VelY := 0;
1012 end
1013 else
1014 begin
1015 Y := Y + dY;
1016 X := X + dX;
1017 end;
1018 end;
1020 VelX := VelX + AccelX;
1021 VelY := VelY + AccelY;
1023 // Êðîâü ðàñòâîðÿåòñÿ â æèäêîñòè:
1024 if isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1025 begin
1026 Inc(Time);
1028 Alpha := 255 - Trunc((255.0 * Time) / LiveTime);
1029 end;
1030 end;
1032 PARTICLE_SPARK:
1033 begin
1034 dX := Round(VelX);
1035 dY := Round(VelY);
1037 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) and
1038 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
1039 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
1040 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
1041 begin // Âèñèò â âîçäóõå
1042 VelY := 0.8;
1043 AccelY := 0.5;
1044 end;
1046 if dX <> 0 then
1047 begin
1048 if dX > 0 then
1049 s := 1
1050 else
1051 s := -1;
1053 dX := Abs(dX);
1055 for b := 1 to dX do
1056 begin
1057 if (X+s >= w) or (X+s <= 0) then
1058 begin
1059 State := STATE_FREE;
1060 Break;
1061 end;
1063 //c := gCollideMap[Y, X+s];
1065 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
1066 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1067 VelX := 0;
1068 AccelX := 0;
1069 Break;
1070 end
1071 else // Ïóñòî:
1072 if not isAnythingAt(X+s, Y) {c = MARK_FREE} then
1073 X := X + s
1074 else // Îñòàëüíîå:
1075 begin
1076 State := STATE_FREE;
1077 Break;
1078 end;
1079 end;
1080 end;
1082 if dY <> 0 then
1083 begin
1084 if dY > 0 then
1085 s := 1
1086 else
1087 s := -1;
1089 dY := Abs(dY);
1091 for b := 1 to dY do
1092 begin
1093 if (Y+s >= h) or (Y+s <= 0) then
1094 begin
1095 State := STATE_FREE;
1096 Break;
1097 end;
1099 //c := gCollideMap[Y+s, X];
1101 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
1102 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1103 if s < 0 then
1104 begin
1105 VelY := -VelY;
1106 AccelY := Abs(AccelY);
1107 end
1108 else // Èëè íå ïàäàåò
1109 begin
1110 VelX := 0;
1111 AccelX := 0;
1112 VelY := 0;
1113 AccelY := 0.8;
1114 end;
1116 Break;
1117 end
1118 else // Ïóñòî:
1119 if not isAnythingAt(X, Y+s) {c = MARK_FREE} then
1120 Y := Y + s
1121 else // Îñàëüíîå:
1122 begin
1123 State := STATE_FREE;
1124 Break;
1125 end;
1126 end;
1127 end;
1129 if VelX <> 0.0 then
1130 VelX := VelX + AccelX;
1131 if VelY <> 0.0 then
1132 begin
1133 if AccelY < 10 then
1134 AccelY := AccelY + 0.08;
1135 VelY := VelY + AccelY;
1136 end;
1138 Time := Time + 1;
1139 end;
1141 PARTICLE_WATER:
1142 begin
1143 if (State = STATE_STICK) and (Random(30) = 15) then
1144 begin // Ñòåêàåò/îòëèïàåò
1145 VelY := 0.5;
1146 AccelY := 0.15;
1147 if (not isBlockedAt(X-1, Y) {ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)}) and
1148 (not isBlockedAt(X+1, Y) {ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)}) then
1149 State := STATE_NORMAL;
1150 Continue;
1151 end;
1153 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
1154 begin
1155 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
1156 begin // Ëèôò ââåðõ
1157 if VelY > -4-Random(3) then
1158 VelY := VelY - 0.8;
1159 if Abs(VelX) > 0.1 then
1160 VelX := VelX - VelX/10.0;
1161 VelX := VelX + (Random-Random)*0.2;
1162 AccelY := 0.15;
1163 end;
1164 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
1165 begin // Ïîòîê âëåâî
1166 if VelX > -8-Random(3) then
1167 VelX := VelX - 0.8;
1168 AccelY := 0.15;
1169 end;
1170 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
1171 begin // Ïîòîê âïðàâî
1172 if VelX < 8+Random(3) then
1173 VelX := VelX + 0.8;
1174 AccelY := 0.15;
1175 end;
1176 end;
1178 dX := Round(VelX);
1179 dY := Round(VelY);
1181 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
1182 if (State <> STATE_STICK) and
1183 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
1184 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
1185 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
1186 begin // Âèñèò â âîçäóõå - êàïàåò
1187 VelY := 0.8;
1188 AccelY := 0.5;
1189 State := STATE_NORMAL;
1190 end;
1192 if dX <> 0 then
1193 begin
1194 if dX > 0 then
1195 s := 1
1196 else
1197 s := -1;
1199 for b := 1 to Abs(dX) do
1200 begin
1201 if (X+s >= w) or (X+s <= 0) then
1202 begin // Ñáîêó ãðàíèöà
1203 State := STATE_FREE;
1204 Break;
1205 end;
1207 //c := gCollideMap[Y, X+s];
1209 if isLiquidAt(X+s, Y) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then
1210 begin // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1211 State := STATE_FREE;
1212 Break;
1213 end;
1215 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
1216 begin // Ñòåíà/äâåðü
1217 VelX := 0;
1218 VelY := 0;
1219 AccelX := 0;
1220 AccelY := 0;
1221 State := STATE_STICK;
1222 Break;
1223 end;
1225 X := X+s;
1226 end;
1227 end;
1229 if dY <> 0 then
1230 begin
1231 if dY > 0 then
1232 s := 1
1233 else
1234 s := -1;
1236 for b := 1 to Abs(dY) do
1237 begin
1238 if (Y+s >= h) or (Y+s <= 0) then
1239 begin // Ñíèçó/ñâåðõó ãðàíèöà
1240 State := STATE_FREE;
1241 Break;
1242 end;
1244 //c := gCollideMap[Y+s, X];
1246 if isLiquidAt(X, Y+s) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then
1247 begin // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1248 State := STATE_FREE;
1249 Break;
1250 end;
1252 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
1253 begin // Ñòåíà/äâåðü
1254 VelX := 0;
1255 VelY := 0;
1256 AccelX := 0;
1257 AccelY := 0;
1258 if (s > 0) and (State <> STATE_STICK) then
1259 State := STATE_NORMAL
1260 else
1261 State := STATE_STICK;
1262 Break;
1263 end;
1265 Y := Y+s;
1266 end;
1267 end;
1269 VelX := VelX + AccelX;
1270 VelY := VelY + AccelY;
1272 Time := Time + 1;
1273 end;
1275 PARTICLE_BUBBLES:
1276 begin
1277 dY := Round(VelY);
1279 if dY <> 0 then
1280 begin
1281 if dY > 0 then
1282 s := 1
1283 else
1284 s := -1;
1286 for b := 1 to Abs(dY) do
1287 begin
1288 if (Y+s >= h) or (Y+s <= 0) then
1289 begin
1290 State := STATE_FREE;
1291 Break;
1292 end;
1294 if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
1295 begin // Óæå íå æèäêîñòü
1296 State := STATE_FREE;
1297 Break;
1298 end;
1300 Y := Y+s;
1301 end;
1302 end;
1304 if VelY > -4 then
1305 VelY := VelY + AccelY;
1307 Time := Time + 1;
1308 end;
1309 end; // case
1311 {CorrectOffsets(a);}
1312 end; // with
1313 end; // if
1314 end; // for
1315 end; // Particles <> nil
1317 if OnceAnims <> nil then
1318 begin
1319 for a := 0 to High(OnceAnims) do
1320 if OnceAnims[a].Animation <> nil then
1321 begin
1322 case OnceAnims[a].AnimType of
1323 ONCEANIM_SMOKE:
1324 begin
1325 if Random(3) = 0 then
1326 OnceAnims[a].X := OnceAnims[a].X-1+Random(3);
1327 if Random(2) = 0 then
1328 OnceAnims[a].Y := OnceAnims[a].Y-Random(2);
1329 end;
1330 end;
1332 if OnceAnims[a].Animation.Played then
1333 begin
1334 OnceAnims[a].Animation.Free();
1335 OnceAnims[a].Animation := nil;
1336 end
1337 else
1338 OnceAnims[a].Animation.Update();
1339 end;
1340 end;
1341 end;
1343 procedure g_GFX_Draw();
1344 var
1345 a, len: Integer;
1346 begin
1347 if Particles <> nil then
1348 begin
1349 glDisable(GL_TEXTURE_2D);
1350 glPointSize(2);
1352 glEnable(GL_BLEND);
1353 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1355 glBegin(GL_POINTS);
1357 len := High(Particles);
1359 for a := 0 to len do
1360 with Particles[a] do
1361 if (State <> STATE_FREE) and (X >= sX) and (Y >= sY) and
1362 (X <= sX+sWidth) and (sY <= sY+sHeight) then
1363 begin
1364 glColor4ub(Red, Green, Blue, Alpha);
1365 glVertex2i(X + offsetX, Y + offsetY);
1366 end;
1368 glEnd();
1370 glDisable(GL_BLEND);
1371 end;
1373 if OnceAnims <> nil then
1374 for a := 0 to High(OnceAnims) do
1375 if OnceAnims[a].Animation <> nil then
1376 with OnceAnims[a] do
1377 Animation.Draw(X, Y, M_NONE);
1378 end;
1380 end.