DEADSOFTWARE

restored particles
[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 {.$DEFINE HAS_COLLIDE_BITMAP}
18 unit g_gfx;
20 interface
22 uses
23 g_textures;
25 const
26 BLOOD_NORMAL = 0;
27 BLOOD_SPARKS = 1;
28 ONCEANIM_NONE = 0;
29 ONCEANIM_SMOKE = 1;
30 MARK_FREE = 0;
31 MARK_WALL = 1;
32 MARK_WATER = 2;
33 MARK_ACID = 4;
34 MARK_LIFTDOWN = 8;
35 MARK_LIFTUP = 16;
36 MARK_DOOR = 32;
37 MARK_LIFTLEFT = 64;
38 MARK_LIFTRIGHT = 128;
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);
59 procedure g_GFX_Update();
60 procedure g_GFX_Draw();
63 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
64 var
65 gCollideMap: Array of Array of Byte;
66 {$ENDIF}
69 implementation
71 uses
72 g_map, g_basic, Math, e_graphics, GL, GLExt,
73 g_options, g_console, SysUtils, g_triggers, MAPDEF,
74 g_game, g_language, g_net;
76 type
77 PParticle = ^TParticle;
78 TParticle = record
79 X, Y: Integer;
80 VelX, VelY: Single;
81 AccelX, AccelY: Single;
82 Red, Green, Blue: Byte;
83 Alpha: Byte;
84 Time, LiveTime: Word;
85 State: Byte;
86 ParticleType: Byte;
87 offsetX, offsetY: ShortInt;
88 end;
90 TOnceAnim = record
91 AnimType: Byte;
92 X, Y: Integer;
93 Animation: TAnimation;
94 end;
96 const
97 PARTICLE_BLOOD = 0;
98 PARTICLE_SPARK = 1;
99 PARTICLE_BUBBLES = 2;
100 PARTICLE_WATER = 3;
101 STATE_FREE = 0;
102 STATE_NORMAL = 1;
103 STATE_STICK = 2;
105 var
106 Particles: Array of TParticle;
107 OnceAnims: Array of TOnceAnim;
108 MaxParticles: Integer;
109 CurrentParticle: Integer;
112 function isBlockedAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
113 begin
114 result := g_Map_CollidePanel(x, y, w, h, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
115 end;
118 // ???
119 function isWallAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
120 begin
121 result := g_Map_CollidePanel(x, y, w, h, (PANEL_WALL or PANEL_STEP));
122 end;
125 function isLiftUpAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
126 begin
127 result := g_Map_CollidePanel(x, y, w, h, PANEL_LIFTUP);
128 end;
130 function isLiftDownAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
131 begin
132 result := g_Map_CollidePanel(x, y, w, h, PANEL_LIFTDOWN);
133 end;
135 function isLiftLeftAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
136 begin
137 result := g_Map_CollidePanel(x, y, w, h, PANEL_LIFTLEFT);
138 end;
140 function isLiftRightAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
141 begin
142 result := g_Map_CollidePanel(x, y, w, h, PANEL_LIFTRIGHT);
143 end;
146 function isLiquidAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
147 begin
148 result := g_Map_CollidePanel(x, y, w, h, (PANEL_WATER or PANEL_ACID1 or PANEL_ACID2));
149 end;
152 function isAnythingAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
153 begin
154 result := g_Map_CollidePanel(x, y, w, h, (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));
155 end;
158 procedure g_Mark(x, y, Width, Height: Integer; t: Byte; st: Boolean);
159 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
160 var
161 yy, y2, xx, x2: Integer;
162 {$ENDIF}
163 begin
164 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
165 if x < 0 then
166 begin
167 Width := Width + x;
168 x := 0;
169 end;
171 if Width < 0 then
172 Exit;
174 if y < 0 then
175 begin
176 Height := Height + y;
177 y := 0;
178 end;
180 if Height < 0 then
181 Exit;
183 if x > gMapInfo.Width then
184 Exit;
185 if y > gMapInfo.Height then
186 Exit;
188 y2 := y + Height - 1;
189 if y2 > gMapInfo.Height then
190 y2 := gMapInfo.Height;
192 x2 := x + Width - 1;
193 if x2 > gMapInfo.Width then
194 x2 := gMapInfo.Width;
196 if st then
197 begin // Óñòàíîâèòü ïðèçíàê
198 for yy := y to y2 do
199 for xx := x to x2 do
200 gCollideMap[yy][xx] := gCollideMap[yy][xx] or t;
201 end
202 else
203 begin // Óáðàòü ïðèçíàê
204 t := not t;
205 for yy := y to y2 do
206 for xx := x to x2 do
207 gCollideMap[yy][xx] := gCollideMap[yy][xx] and t;
208 end;
209 {$ENDIF}
210 end;
212 procedure CreateCollideMap();
213 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
214 var
215 a: Integer;
216 {$ENDIF}
217 begin
218 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
219 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
220 SetLength(gCollideMap, gMapInfo.Height+1);
221 for a := 0 to High(gCollideMap) do
222 SetLength(gCollideMap[a], gMapInfo.Width+1);
224 if gWater <> nil then
225 begin
226 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 2/6', 0, True);
227 for a := 0 to High(gWater) do
228 with gWater[a] do
229 g_Mark(X, Y, Width, Height, MARK_WATER, True);
230 end;
232 if gAcid1 <> nil then
233 begin
234 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 3/6', 0, True);
235 for a := 0 to High(gAcid1) do
236 with gAcid1[a] do
237 g_Mark(X, Y, Width, Height, MARK_ACID, True);
238 end;
240 if gAcid2 <> nil then
241 begin
242 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 4/6', 0, True);
243 for a := 0 to High(gAcid2) do
244 with gAcid2[a] do
245 g_Mark(X, Y, Width, Height, MARK_ACID, True);
246 end;
248 if gLifts <> nil then
249 begin
250 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 5/6', 0, True);
251 for a := 0 to High(gLifts) do
252 with gLifts[a] do
253 begin
254 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
256 if LiftType = 0 then
257 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
258 else if LiftType = 1 then
259 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
260 else if LiftType = 2 then
261 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
262 else if LiftType = 3 then
263 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True)
264 end;
265 end;
267 if gWalls <> nil then
268 begin
269 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 6/6', 0, True);
270 for a := 0 to High(gWalls) do
271 begin
272 if gWalls[a].Door then
273 begin
274 // Çàêðûòàÿ äâåðü:
275 if gWalls[a].Enabled then
276 with gWalls[a] do
277 g_Mark(X, Y, Width, Height, MARK_DOOR, True)
278 else // Îòêðûòàÿ äâåðü:
279 if gWalls[a].Enabled then
280 with gWalls[a] do
281 g_Mark(X, Y, Width, Height, MARK_DOOR, False);
282 end
283 else // Ñòåíà
284 with gWalls[a] do
285 g_Mark(X, Y, Width, Height, MARK_WALL, True);
286 end;
287 end;
288 {$ENDIF}
289 end;
291 procedure g_GFX_Init();
292 begin
293 CreateCollideMap();
294 end;
296 procedure g_GFX_Free();
297 var
298 a: Integer;
299 begin
300 Particles := nil;
301 SetLength(Particles, MaxParticles);
302 CurrentParticle := 0;
304 if OnceAnims <> nil then
305 begin
306 for a := 0 to High(OnceAnims) do
307 OnceAnims[a].Animation.Free();
309 OnceAnims := nil;
310 end;
312 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
313 gCollideMap := nil;
314 {$ENDIF}
315 end;
318 procedure CorrectOffsets(id: Integer);
319 {$IF not DEFINED(HAS_COLLIDE_BITMAP)}
320 var
321 part: PParticle;
322 {$ENDIF}
323 begin
324 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
325 with Particles[id] do
326 begin
327 if (X >= 0) and (Y > 0) and
328 (Y < Length(gCollideMap)) and (X < Length(gCollideMap[0])) and
329 (ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) then
330 offsetY := 1 // Ñòåíà ñâåðõó
331 else
332 offsetY := 0;
334 if (X > 0) and (Y >= 0) and
335 (Y < Length(gCollideMap)) and (X < Length(gCollideMap[0])) and
336 (ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) then
337 offsetX := 1 // Ñòåíà ñëåâà
338 else
339 offsetX := 0;
340 end;
341 {$ELSE}
342 part := @Particles[id];
343 part.offsetX := 0;
344 part.offsetY := 0;
345 // check for upper wall
346 if isBlockedAt(part.X, part.Y-1) then part.offsetY := 1;
347 // check for left wall
348 if isBlockedAt(part.X-1, part.Y) then part.offsetX := 1;
349 {$ENDIF}
350 end;
353 procedure g_GFX_SparkVel (fX, fY: Integer; Count: Word; VX, VY: Integer; DevX, DevY: Byte);
354 var
355 a: Integer;
356 DevX1, DevX2,
357 DevY1, DevY2: Byte;
358 l: Integer;
359 begin
360 l := Length(Particles);
361 if l = 0 then exit;
362 if Count > l then Count := l;
364 DevX1 := DevX div 2;
365 DevX2 := DevX + 1;
366 DevY1 := DevY div 2;
367 DevY2 := DevY + 1;
369 for a := 1 to Count do
370 begin
371 with Particles[CurrentParticle] do
372 begin
373 X := fX-DevX1+Random(DevX2);
374 Y := fY-DevY1+Random(DevY2);
376 VelX := VX + (Random-Random)*3;
377 VelY := VY + (Random-Random)*3;
379 if VelY > -4 then
380 if VelY-4 < -4 then
381 VelY := -4
382 else
383 VelY := VelY-4;
385 AccelX := -Sign(VelX)*Random/100;
386 AccelY := 0.8;
388 Red := 255;
389 Green := 100+Random(155);
390 Blue := 64;
391 Alpha := 255;
393 State := STATE_NORMAL;
394 Time := 0;
395 LiveTime := 30+Random(60);
396 ParticleType := PARTICLE_SPARK;
398 CorrectOffsets(CurrentParticle);
399 end;
401 if CurrentParticle+2 > MaxParticles then
402 CurrentParticle := 0
403 else
404 CurrentParticle := CurrentParticle+1;
405 end;
406 end;
409 procedure g_GFX_Blood(fX, fY: Integer; Count: Word; vx, vy: Integer;
410 DevX, DevY: Word; CR, CG, CB: Byte; Kind: Byte = BLOOD_NORMAL);
411 var
412 a: Integer;
413 DevX1, DevX2,
414 DevY1, DevY2: Word;
415 l: Integer;
416 CRnd: Byte;
417 CC: SmallInt;
418 begin
419 if Kind = BLOOD_SPARKS then
420 begin
421 g_GFX_SparkVel(fX, fY, 2 + Random(2), -VX div 2, -VY div 2, DevX, DevY);
422 Exit;
423 end;
424 l := Length(Particles);
425 if l = 0 then
426 Exit;
427 if Count > l then
428 Count := l;
430 DevX1 := DevX div 2;
431 DevX2 := DevX + 1;
432 DevY1 := DevY div 2;
433 DevY2 := DevY + 1;
435 for a := 1 to Count do
436 begin
437 with Particles[CurrentParticle] do
438 begin
439 X := fX - DevX1 + Random(DevX2);
440 Y := fY - DevY1 + Random(DevY2);
443 if (X < 0) or (X > gMapInfo.Width-1) or
444 (Y < 0) or (Y > gMapInfo.Height-1) or
445 ByteBool(gCollideMap[Y, X] and MARK_WALL) then
446 Continue;
448 if isWallAt(X, Y) then continue;
450 VelX := vx + (Random-Random)*3;
451 VelY := vy + (Random-Random)*3;
453 if VelY > -4 then
454 if VelY-4 < -4 then
455 VelY := -4
456 else
457 VelY := VelY-4;
459 AccelX := -Sign(VelX)*Random/100;
460 AccelY := 0.8;
462 CRnd := 20*Random(6);
463 if CR > 0 then
464 begin
465 CC := CR + CRnd - 50;
466 if CC < 0 then CC := 0;
467 if CC > 255 then CC := 255;
468 Red := CC;
469 end else
470 Red := 0;
471 if CG > 0 then
472 begin
473 CC := CG + CRnd - 50;
474 if CC < 0 then CC := 0;
475 if CC > 255 then CC := 255;
476 Green := CC;
477 end else
478 Green := 0;
479 if CB > 0 then
480 begin
481 CC := CB + CRnd - 50;
482 if CC < 0 then CC := 0;
483 if CC > 255 then CC := 255;
484 Blue := CC;
485 end else
486 Blue := 0;
488 Alpha := 255;
490 State := STATE_NORMAL;
491 Time := 0;
492 LiveTime := 120+Random(40);
493 ParticleType := PARTICLE_BLOOD;
495 CorrectOffsets(CurrentParticle);
496 end;
498 if CurrentParticle >= MaxParticles-1 then
499 CurrentParticle := 0
500 else
501 CurrentParticle := CurrentParticle+1;
502 end;
503 end;
506 procedure g_GFX_Spark(fX, fY: Integer; Count: Word; Angle: SmallInt; DevX, DevY: Byte);
507 var
508 a: Integer;
509 b: Single;
510 DevX1, DevX2,
511 DevY1, DevY2: Byte;
512 BaseVelX, BaseVelY: Single;
513 l: Integer;
514 begin
515 l := Length(Particles);
516 if l = 0 then
517 Exit;
518 if Count > l then
519 Count := l;
521 Angle := 360 - Angle;
523 DevX1 := DevX div 2;
524 DevX2 := DevX + 1;
525 DevY1 := DevY div 2;
526 DevY2 := DevY + 1;
528 b := DegToRad(Angle);
529 BaseVelX := cos(b);
530 BaseVelY := 1.6*sin(b);
531 if Abs(BaseVelX) < 0.01 then
532 BaseVelX := 0.0;
533 if Abs(BaseVelY) < 0.01 then
534 BaseVelY := 0.0;
535 for a := 1 to Count do
536 begin
537 with Particles[CurrentParticle] do
538 begin
539 X := fX-DevX1+Random(DevX2);
540 Y := fY-DevY1+Random(DevY2);
542 VelX := BaseVelX*Random;
543 VelY := BaseVelY-Random;
544 AccelX := VelX/3.0;
545 AccelY := VelY/5.0;
547 Red := 255;
548 Green := 100+Random(155);
549 Blue := 64;
550 Alpha := 255;
552 State := STATE_NORMAL;
553 Time := 0;
554 LiveTime := 30+Random(60);
555 ParticleType := PARTICLE_SPARK;
557 CorrectOffsets(CurrentParticle);
558 end;
560 if CurrentParticle+2 > MaxParticles then
561 CurrentParticle := 0
562 else
563 CurrentParticle := CurrentParticle+1;
564 end;
565 end;
567 procedure g_GFX_Water(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DevX, DevY, Color: Byte);
568 var
569 a: Integer;
570 DevX1, DevX2,
571 DevY1, DevY2: Byte;
572 l: Integer;
573 begin
574 l := Length(Particles);
575 if l = 0 then
576 Exit;
577 if Count > l then
578 Count := l;
580 if Abs(fVelX) < 3.0 then
581 fVelX := 3.0 - 6.0*Random;
583 DevX1 := DevX div 2;
584 DevX2 := DevX + 1;
585 DevY1 := DevY div 2;
586 DevY2 := DevY + 1;
588 for a := 1 to Count do
589 begin
590 with Particles[CurrentParticle] do
591 begin
592 X := fX-DevX1+Random(DevX2);
593 Y := fY-DevY1+Random(DevY2);
595 if Abs(fVelX) < 0.5 then
596 VelX := 1.0 - 2.0*Random
597 else
598 VelX := fVelX*Random;
599 if Random(10) < 7 then
600 VelX := -VelX;
601 VelY := fVelY*Random;
602 AccelX := 0.0;
603 AccelY := 0.8;
605 case Color of
606 1: // Êðàñíûé
607 begin
608 Red := 155 + Random(9)*10;
609 Green := Trunc(150*Random);
610 Blue := Green;
611 end;
612 2: // Çåëåíûé
613 begin
614 Red := Trunc(150*Random);
615 Green := 175 + Random(9)*10;
616 Blue := Red;
617 end;
618 3: // Ñèíèé
619 begin
620 Red := Trunc(200*Random);
621 Green := Red;
622 Blue := 175 + Random(9)*10;
623 end;
624 else // Ñåðûé
625 begin
626 Red := 90 + Random(12)*10;
627 Green := Red;
628 Blue := Red;
629 end;
630 end;
632 Alpha := 255;
634 State := STATE_NORMAL;
635 Time := 0;
636 LiveTime := 60+Random(60);
637 ParticleType := PARTICLE_WATER;
639 CorrectOffsets(CurrentParticle);
640 end;
642 if CurrentParticle+2 > MaxParticles then
643 CurrentParticle := 0
644 else
645 CurrentParticle := CurrentParticle+1;
646 end;
647 end;
649 procedure g_GFX_SimpleWater(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DefColor, CR, CG, CB: Byte);
650 var
651 a: Integer;
652 l: Integer;
653 begin
654 l := Length(Particles);
655 if l = 0 then
656 Exit;
657 if Count > l then
658 Count := l;
660 for a := 1 to Count do
661 begin
662 with Particles[CurrentParticle] do
663 begin
664 X := fX;
665 Y := fY;
667 VelX := fVelX;
668 VelY := fVelY;
669 AccelX := 0.0;
670 AccelY := 0.8;
672 case DefColor of
673 1: // Êðàñíûé
674 begin
675 Red := 155 + Random(9)*10;
676 Green := Trunc(150*Random);
677 Blue := Green;
678 end;
679 2: // Çåëåíûé
680 begin
681 Red := Trunc(150*Random);
682 Green := 175 + Random(9)*10;
683 Blue := Red;
684 end;
685 3: // Ñèíèé
686 begin
687 Red := Trunc(200*Random);
688 Green := Red;
689 Blue := 175 + Random(9)*10;
690 end;
691 4: // Ñâîé öâåò, ñâåòëåå
692 begin
693 Red := 20 + Random(19)*10;
694 Green := Red;
695 Blue := Red;
696 Red := Min(Red + CR, 255);
697 Green := Min(Green + CG, 255);
698 Blue := Min(Blue + CB, 255);
699 end;
700 5: // Ñâîé öâåò, òåìíåå
701 begin
702 Red := 20 + Random(19)*10;
703 Green := Red;
704 Blue := Red;
705 Red := Max(CR - Red, 0);
706 Green := Max(CG - Green, 0);
707 Blue := Max(CB - Blue, 0);
708 end;
709 else // Ñåðûé
710 begin
711 Red := 90 + Random(12)*10;
712 Green := Red;
713 Blue := Red;
714 end;
715 end;
717 Alpha := 255;
719 State := STATE_NORMAL;
720 Time := 0;
721 LiveTime := 60+Random(60);
722 ParticleType := PARTICLE_WATER;
724 CorrectOffsets(CurrentParticle);
725 end;
727 if CurrentParticle+2 > MaxParticles then
728 CurrentParticle := 0
729 else
730 CurrentParticle := CurrentParticle+1;
731 end;
732 end;
734 procedure g_GFX_Bubbles(fX, fY: Integer; Count: Word; DevX, DevY: Byte);
735 var
736 a: Integer;
737 DevX1, DevX2,
738 DevY1, DevY2: Byte;
739 l: Integer;
740 begin
741 l := Length(Particles);
742 if l = 0 then
743 Exit;
744 if Count > l then
745 Count := l;
747 DevX1 := DevX div 2;
748 DevX2 := DevX + 1;
749 DevY1 := DevY div 2;
750 DevY2 := DevY + 1;
752 for a := 1 to Count do
753 begin
754 with Particles[CurrentParticle] do
755 begin
756 X := fX-DevX1+Random(DevX2);
757 Y := fY-DevY1+Random(DevY2);
759 if (X >= gMapInfo.Width) or (X <= 0) or
760 (Y >= gMapInfo.Height) or (Y <= 0) then
761 Continue;
763 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
764 Continue;
766 VelX := 0;
767 VelY := -1-Random;
768 AccelX := 0;
769 AccelY := VelY/10;
771 Red := 255;
772 Green := 255;
773 Blue := 255;
774 Alpha := 255;
776 State := STATE_NORMAL;
777 Time := 0;
778 LiveTime := 65535;
779 ParticleType := PARTICLE_BUBBLES;
781 CorrectOffsets(CurrentParticle);
782 end;
784 if CurrentParticle+2 > MaxParticles then
785 CurrentParticle := 0
786 else
787 CurrentParticle := CurrentParticle+1;
788 end;
789 end;
791 procedure g_GFX_SetMax(Count: Integer);
792 begin
793 if Count > 50000 then
794 Count := 50000;
796 SetLength(Particles, Count);
797 MaxParticles := Count;
798 if CurrentParticle >= Count then
799 CurrentParticle := 0;
800 end;
802 function g_GFX_GetMax(): Integer;
803 begin
804 Result := MaxParticles;
805 end;
807 function FindOnceAnim: DWORD;
808 var
809 i: Integer;
810 begin
811 if OnceAnims <> nil then
812 for i := 0 to High(OnceAnims) do
813 if OnceAnims[i].Animation = nil then
814 begin
815 Result := i;
816 Exit;
817 end;
819 if OnceAnims = nil then
820 begin
821 SetLength(OnceAnims, 16);
822 Result := 0;
823 end
824 else
825 begin
826 Result := High(OnceAnims) + 1;
827 SetLength(OnceAnims, Length(OnceAnims) + 16);
828 end;
829 end;
831 procedure g_GFX_OnceAnim(X, Y: Integer; Anim: TAnimation; AnimType: Byte = 0);
832 var
833 find_id: DWORD;
834 begin
835 if Anim = nil then
836 Exit;
838 find_id := FindOnceAnim();
840 OnceAnims[find_id].AnimType := AnimType;
841 OnceAnims[find_id].Animation := TAnimation.Create(Anim.FramesID, Anim.Loop, Anim.Speed);
842 OnceAnims[find_id].Animation.Blending := Anim.Blending;
843 OnceAnims[find_id].Animation.Alpha := Anim.Alpha;
844 OnceAnims[find_id].X := X;
845 OnceAnims[find_id].Y := Y;
846 end;
848 procedure g_GFX_Update();
849 var
850 a: Integer;
851 w, h: Integer;
852 dX, dY: SmallInt;
853 b, len: Integer;
854 s: ShortInt;
855 //c: Byte;
856 begin
857 if Particles <> nil then
858 begin
859 w := gMapInfo.Width;
860 h := gMapInfo.Height;
862 len := High(Particles);
864 for a := 0 to len do
865 if Particles[a].State <> 0 then
866 with Particles[a] do
867 begin
868 if Time = LiveTime then
869 State := STATE_FREE;
870 if (X+1 >= w) or (Y+1 >= h) or (X <= 0) or (Y <= 0) then
871 State := STATE_FREE;
872 if State = STATE_FREE then
873 Continue;
875 case ParticleType of
876 PARTICLE_BLOOD:
877 begin
878 if gAdvBlood then
879 begin
880 if (State = STATE_STICK) then
882 if (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
883 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) and
884 (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
885 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED))
886 then
888 if (not isBlockedAt(X, Y-1)) and
889 (not isBlockedAt(X, Y+1)) and
890 (not isBlockedAt(X-1, Y)) and
891 (not isBlockedAt(X+1, Y))
892 then
893 begin // Îòëèïëà - êàïàåò
894 VelY := 0.5;
895 AccelY := 0.15;
896 State := STATE_NORMAL;
897 end
898 else
899 if Random(200) = 100 then
900 begin // Ïðèëåïëåíà - íî âîçìîæíî ñòåêàåò
901 VelY := 0.5;
902 AccelY := 0.15;
903 Continue;
904 end;
906 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
907 begin
908 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
909 begin // Ëèôò ââåðõ
910 if VelY > -4-Random(3) then
911 VelY := VelY - 0.8;
912 if Abs(VelX) > 0.1 then
913 VelX := VelX - VelX/10.0;
914 VelX := VelX + (Random-Random)*0.2;
915 AccelY := 0.15;
916 end;
917 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
918 begin // Ïîòîê âëåâî
919 if VelX > -8-Random(3) then
920 VelX := VelX - 0.8;
921 AccelY := 0.15;
922 end;
923 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
924 begin // Ïîòîê âïðàâî
925 if VelX < 8+Random(3) then
926 VelX := VelX + 0.8;
927 AccelY := 0.15;
928 end;
929 end;
931 dX := Round(VelX);
932 dY := Round(VelY);
934 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
935 if (State <> STATE_STICK) and
936 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
937 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
938 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
939 begin // Âèñèò â âîçäóõå - êàïàåò
940 VelY := 0.8;
941 AccelY := 0.5;
942 State := STATE_NORMAL;
943 end;
945 if dX <> 0 then
946 begin
947 if dX > 0 then
948 s := 1
949 else
950 s := -1;
952 dX := Abs(dX);
954 for b := 1 to dX do
955 begin
956 if (X+s >= w) or (X+s <= 0) then
957 begin
958 State := STATE_FREE;
959 Break;
960 end;
962 //c := gCollideMap[Y, X+s];
964 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
965 begin // Ñòåíà/äâåðü
966 VelX := 0;
967 VelY := 0;
968 AccelX := 0;
969 AccelY := 0;
970 State := STATE_STICK;
971 Break;
972 end;
974 X := X+s;
975 end;
976 end;
978 if dY <> 0 then
979 begin
980 if dY > 0 then
981 s := 1
982 else
983 s := -1;
985 dY := Abs(dY);
987 for b := 1 to dY do
988 begin
989 if (Y+s >= h) or (Y+s <= 0) then
990 begin
991 State := STATE_FREE;
992 Break;
993 end;
995 //c := gCollideMap[Y+s, X];
997 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
998 begin // Ñòåíà/äâåðü
999 VelX := 0;
1000 VelY := 0;
1001 AccelX := 0;
1002 AccelY := 0;
1003 if (s > 0) and (State <> STATE_STICK) then
1004 State := STATE_NORMAL
1005 else
1006 State := STATE_STICK;
1007 Break;
1008 end;
1010 Y := Y+s;
1011 end;
1012 end;
1013 end // if gAdvBlood
1014 else
1015 begin
1016 dX := Round(VelX);
1017 dY := Round(VelY);
1019 if (X+dX >= w) or (Y+dY >= h) or
1020 (X+dX <= 0) or (Y+dY <= 0) or
1021 isBlockedAt(X+dX, Y+dY) {ByteBool(gCollideMap[Y+dY, X+dX] and MARK_BLOCKED)} then
1022 begin // Ñòåíà/äâåðü/ãðàíèöà
1023 State := STATE_FREE;
1024 VelX := 0;
1025 VelY := 0;
1026 end
1027 else
1028 begin
1029 Y := Y + dY;
1030 X := X + dX;
1031 end;
1032 end;
1034 VelX := VelX + AccelX;
1035 VelY := VelY + AccelY;
1037 // Êðîâü ðàñòâîðÿåòñÿ â æèäêîñòè:
1038 if isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1039 begin
1040 Inc(Time);
1042 Alpha := 255 - Trunc((255.0 * Time) / LiveTime);
1043 end;
1044 end;
1046 PARTICLE_SPARK:
1047 begin
1048 dX := Round(VelX);
1049 dY := Round(VelY);
1051 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) and
1052 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
1053 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
1054 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
1055 begin // Âèñèò â âîçäóõå
1056 VelY := 0.8;
1057 AccelY := 0.5;
1058 end;
1060 if dX <> 0 then
1061 begin
1062 if dX > 0 then
1063 s := 1
1064 else
1065 s := -1;
1067 dX := Abs(dX);
1069 for b := 1 to dX do
1070 begin
1071 if (X+s >= w) or (X+s <= 0) then
1072 begin
1073 State := STATE_FREE;
1074 Break;
1075 end;
1077 //c := gCollideMap[Y, X+s];
1079 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
1080 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1081 VelX := 0;
1082 AccelX := 0;
1083 Break;
1084 end
1085 else // Ïóñòî:
1086 if not isAnythingAt(X+s, Y) {c = MARK_FREE} then
1087 X := X + s
1088 else // Îñòàëüíîå:
1089 begin
1090 State := STATE_FREE;
1091 Break;
1092 end;
1093 end;
1094 end;
1096 if dY <> 0 then
1097 begin
1098 if dY > 0 then
1099 s := 1
1100 else
1101 s := -1;
1103 dY := Abs(dY);
1105 for b := 1 to dY do
1106 begin
1107 if (Y+s >= h) or (Y+s <= 0) then
1108 begin
1109 State := STATE_FREE;
1110 Break;
1111 end;
1113 //c := gCollideMap[Y+s, X];
1115 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
1116 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1117 if s < 0 then
1118 begin
1119 VelY := -VelY;
1120 AccelY := Abs(AccelY);
1121 end
1122 else // Èëè íå ïàäàåò
1123 begin
1124 VelX := 0;
1125 AccelX := 0;
1126 VelY := 0;
1127 AccelY := 0.8;
1128 end;
1130 Break;
1131 end
1132 else // Ïóñòî:
1133 if not isAnythingAt(X, Y+s) {c = MARK_FREE} then
1134 Y := Y + s
1135 else // Îñàëüíîå:
1136 begin
1137 State := STATE_FREE;
1138 Break;
1139 end;
1140 end;
1141 end;
1143 if VelX <> 0.0 then
1144 VelX := VelX + AccelX;
1145 if VelY <> 0.0 then
1146 begin
1147 if AccelY < 10 then
1148 AccelY := AccelY + 0.08;
1149 VelY := VelY + AccelY;
1150 end;
1152 Time := Time + 1;
1153 end;
1155 PARTICLE_WATER:
1156 begin
1157 if (State = STATE_STICK) and (Random(30) = 15) then
1158 begin // Ñòåêàåò/îòëèïàåò
1159 VelY := 0.5;
1160 AccelY := 0.15;
1161 if (not isBlockedAt(X-1, Y) {ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)}) and
1162 (not isBlockedAt(X+1, Y) {ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)}) then
1163 State := STATE_NORMAL;
1164 Continue;
1165 end;
1167 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
1168 begin
1169 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
1170 begin // Ëèôò ââåðõ
1171 if VelY > -4-Random(3) then
1172 VelY := VelY - 0.8;
1173 if Abs(VelX) > 0.1 then
1174 VelX := VelX - VelX/10.0;
1175 VelX := VelX + (Random-Random)*0.2;
1176 AccelY := 0.15;
1177 end;
1178 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
1179 begin // Ïîòîê âëåâî
1180 if VelX > -8-Random(3) then
1181 VelX := VelX - 0.8;
1182 AccelY := 0.15;
1183 end;
1184 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
1185 begin // Ïîòîê âïðàâî
1186 if VelX < 8+Random(3) then
1187 VelX := VelX + 0.8;
1188 AccelY := 0.15;
1189 end;
1190 end;
1192 dX := Round(VelX);
1193 dY := Round(VelY);
1195 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
1196 if (State <> STATE_STICK) and
1197 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
1198 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
1199 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
1200 begin // Âèñèò â âîçäóõå - êàïàåò
1201 VelY := 0.8;
1202 AccelY := 0.5;
1203 State := STATE_NORMAL;
1204 end;
1206 if dX <> 0 then
1207 begin
1208 if dX > 0 then
1209 s := 1
1210 else
1211 s := -1;
1213 for b := 1 to Abs(dX) do
1214 begin
1215 if (X+s >= w) or (X+s <= 0) then
1216 begin // Ñáîêó ãðàíèöà
1217 State := STATE_FREE;
1218 Break;
1219 end;
1221 //c := gCollideMap[Y, X+s];
1223 if isLiquidAt(X+s, Y) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then
1224 begin // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1225 State := STATE_FREE;
1226 Break;
1227 end;
1229 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
1230 begin // Ñòåíà/äâåðü
1231 VelX := 0;
1232 VelY := 0;
1233 AccelX := 0;
1234 AccelY := 0;
1235 State := STATE_STICK;
1236 Break;
1237 end;
1239 X := X+s;
1240 end;
1241 end;
1243 if dY <> 0 then
1244 begin
1245 if dY > 0 then
1246 s := 1
1247 else
1248 s := -1;
1250 for b := 1 to Abs(dY) do
1251 begin
1252 if (Y+s >= h) or (Y+s <= 0) then
1253 begin // Ñíèçó/ñâåðõó ãðàíèöà
1254 State := STATE_FREE;
1255 Break;
1256 end;
1258 //c := gCollideMap[Y+s, X];
1260 if isLiquidAt(X, Y+s) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then
1261 begin // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1262 State := STATE_FREE;
1263 Break;
1264 end;
1266 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
1267 begin // Ñòåíà/äâåðü
1268 VelX := 0;
1269 VelY := 0;
1270 AccelX := 0;
1271 AccelY := 0;
1272 if (s > 0) and (State <> STATE_STICK) then
1273 State := STATE_NORMAL
1274 else
1275 State := STATE_STICK;
1276 Break;
1277 end;
1279 Y := Y+s;
1280 end;
1281 end;
1283 VelX := VelX + AccelX;
1284 VelY := VelY + AccelY;
1286 Time := Time + 1;
1287 end;
1289 PARTICLE_BUBBLES:
1290 begin
1291 dY := Round(VelY);
1293 if dY <> 0 then
1294 begin
1295 if dY > 0 then
1296 s := 1
1297 else
1298 s := -1;
1300 for b := 1 to Abs(dY) do
1301 begin
1302 if (Y+s >= h) or (Y+s <= 0) then
1303 begin
1304 State := STATE_FREE;
1305 Break;
1306 end;
1308 if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
1309 begin // Óæå íå æèäêîñòü
1310 State := STATE_FREE;
1311 Break;
1312 end;
1314 Y := Y+s;
1315 end;
1316 end;
1318 if VelY > -4 then
1319 VelY := VelY + AccelY;
1321 Time := Time + 1;
1322 end;
1323 end; // case
1325 CorrectOffsets(a);
1326 end;
1327 end; // Particles <> nil
1329 if OnceAnims <> nil then
1330 begin
1331 for a := 0 to High(OnceAnims) do
1332 if OnceAnims[a].Animation <> nil then
1333 begin
1334 case OnceAnims[a].AnimType of
1335 ONCEANIM_SMOKE:
1336 begin
1337 if Random(3) = 0 then
1338 OnceAnims[a].X := OnceAnims[a].X-1+Random(3);
1339 if Random(2) = 0 then
1340 OnceAnims[a].Y := OnceAnims[a].Y-Random(2);
1341 end;
1342 end;
1344 if OnceAnims[a].Animation.Played then
1345 begin
1346 OnceAnims[a].Animation.Free();
1347 OnceAnims[a].Animation := nil;
1348 end
1349 else
1350 OnceAnims[a].Animation.Update();
1351 end;
1352 end;
1353 end;
1355 procedure g_GFX_Draw();
1356 var
1357 a, len: Integer;
1358 begin
1359 if Particles <> nil then
1360 begin
1361 glDisable(GL_TEXTURE_2D);
1362 glPointSize(2);
1364 glEnable(GL_BLEND);
1365 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1367 glBegin(GL_POINTS);
1369 len := High(Particles);
1371 for a := 0 to len do
1372 with Particles[a] do
1373 if (State <> STATE_FREE) and (X >= sX) and (Y >= sY) and
1374 (X <= sX+sWidth) and (sY <= sY+sHeight) then
1375 begin
1376 glColor4ub(Red, Green, Blue, Alpha);
1377 glVertex2i(X + offsetX, Y + offsetY);
1378 end;
1380 glEnd();
1382 glDisable(GL_BLEND);
1383 end;
1385 if OnceAnims <> nil then
1386 for a := 0 to High(OnceAnims) do
1387 if OnceAnims[a].Animation <> nil then
1388 with OnceAnims[a] do
1389 Animation.Draw(X, Y, M_NONE);
1390 end;
1392 end.