DEADSOFTWARE

more grid code uglification -- should be a little faster now
[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 // for bubbles
87 liquidTopY: Integer; // don't float higher than this
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): Boolean; inline;
113 begin
114 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
115 result := g_Map_HasAnyPanelAtPoint(x, y, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
116 end;
118 // ???
119 function isWallAt (x, y: Integer): Boolean; inline;
120 begin
121 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
122 result := g_Map_HasAnyPanelAtPoint(x, y, (PANEL_WALL or PANEL_STEP));
123 end;
125 function isLiftUpAt (x, y: Integer): Boolean; inline;
126 begin
127 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
128 result := g_Map_HasAnyPanelAtPoint(x, y, PANEL_LIFTUP);
129 end;
131 function isLiftDownAt (x, y: Integer): Boolean; inline;
132 begin
133 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
134 result := g_Map_HasAnyPanelAtPoint(x, y, PANEL_LIFTDOWN);
135 end;
137 function isLiftLeftAt (x, y: Integer): Boolean; inline;
138 begin
139 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
140 result := g_Map_HasAnyPanelAtPoint(x, y, PANEL_LIFTLEFT);
141 end;
143 function isLiftRightAt (x, y: Integer): Boolean; inline;
144 begin
145 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
146 result := g_Map_HasAnyPanelAtPoint(x, y, PANEL_LIFTRIGHT);
147 end;
149 function isLiquidAt (x, y: Integer): Boolean; inline;
150 begin
151 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
152 result := g_Map_HasAnyPanelAtPoint(x, y, (PANEL_WATER or PANEL_ACID1 or PANEL_ACID2));
153 end;
155 function isAnythingAt (x, y: Integer): Boolean; inline;
156 begin
157 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
158 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));
159 end;
162 procedure g_Mark(x, y, Width, Height: Integer; t: Byte; st: Boolean);
163 {$IF not DEFINED(HAS_COLLIDE_BITMAP)}
164 begin
165 end;
166 {$ELSE}
167 var
168 yy, y2, xx, x2: Integer;
169 begin
170 if x < 0 then
171 begin
172 Width := Width + x;
173 x := 0;
174 end;
176 if Width < 0 then
177 Exit;
179 if y < 0 then
180 begin
181 Height := Height + y;
182 y := 0;
183 end;
185 if Height < 0 then
186 Exit;
188 if x > gMapInfo.Width then
189 Exit;
190 if y > gMapInfo.Height then
191 Exit;
193 y2 := y + Height - 1;
194 if y2 > gMapInfo.Height then
195 y2 := gMapInfo.Height;
197 x2 := x + Width - 1;
198 if x2 > gMapInfo.Width then
199 x2 := gMapInfo.Width;
201 if st then
202 begin // Óñòàíîâèòü ïðèçíàê
203 for yy := y to y2 do
204 for xx := x to x2 do
205 gCollideMap[yy][xx] := gCollideMap[yy][xx] or t;
206 end
207 else
208 begin // Óáðàòü ïðèçíàê
209 t := not t;
210 for yy := y to y2 do
211 for xx := x to x2 do
212 gCollideMap[yy][xx] := gCollideMap[yy][xx] and t;
213 end;
214 end;
215 {$ENDIF}
218 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
219 procedure CreateCollideMap();
220 var
221 a: Integer;
222 begin
223 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
224 SetLength(gCollideMap, gMapInfo.Height+1);
225 for a := 0 to High(gCollideMap) do
226 SetLength(gCollideMap[a], gMapInfo.Width+1);
228 if gWater <> nil then
229 begin
230 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 2/6', 0, True);
231 for a := 0 to High(gWater) do
232 with gWater[a] do
233 g_Mark(X, Y, Width, Height, MARK_WATER, True);
234 end;
236 if gAcid1 <> nil then
237 begin
238 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 3/6', 0, True);
239 for a := 0 to High(gAcid1) do
240 with gAcid1[a] do
241 g_Mark(X, Y, Width, Height, MARK_ACID, True);
242 end;
244 if gAcid2 <> nil then
245 begin
246 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 4/6', 0, True);
247 for a := 0 to High(gAcid2) do
248 with gAcid2[a] do
249 g_Mark(X, Y, Width, Height, MARK_ACID, True);
250 end;
252 if gLifts <> nil then
253 begin
254 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 5/6', 0, True);
255 for a := 0 to High(gLifts) do
256 with gLifts[a] do
257 begin
258 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
260 if LiftType = 0 then
261 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
262 else if LiftType = 1 then
263 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
264 else if LiftType = 2 then
265 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
266 else if LiftType = 3 then
267 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True)
268 end;
269 end;
271 if gWalls <> nil then
272 begin
273 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 6/6', 0, True);
274 for a := 0 to High(gWalls) do
275 begin
276 if gWalls[a].Door then
277 begin
278 // Çàêðûòàÿ äâåðü:
279 if gWalls[a].Enabled then
280 with gWalls[a] do
281 g_Mark(X, Y, Width, Height, MARK_DOOR, True)
282 else // Îòêðûòàÿ äâåðü:
283 if gWalls[a].Enabled then
284 with gWalls[a] do
285 g_Mark(X, Y, Width, Height, MARK_DOOR, False);
286 end
287 else // Ñòåíà
288 with gWalls[a] do
289 g_Mark(X, Y, Width, Height, MARK_WALL, True);
290 end;
291 end;
292 end;
293 {$ENDIF}
296 procedure g_GFX_Init();
297 begin
298 //CreateCollideMap();
299 end;
302 procedure g_GFX_Free();
303 var
304 a: Integer;
305 begin
306 Particles := nil;
307 SetLength(Particles, MaxParticles);
308 for a := 0 to High(Particles) do Particles[a].State := STATE_FREE;
309 CurrentParticle := 0;
311 if OnceAnims <> nil then
312 begin
313 for a := 0 to High(OnceAnims) do
314 OnceAnims[a].Animation.Free();
316 OnceAnims := nil;
317 end;
318 end;
322 procedure CorrectOffsets(id: Integer); inline;
323 var
324 part: PParticle;
325 begin
326 part := @Particles[id];
327 part.offsetX := 0;
328 part.offsetY := 0;
329 // check for upper wall
330 if isBlockedAt(part.X, part.Y-1) then part.offsetY := 1;
331 // check for left wall
332 if isBlockedAt(part.X-1, part.Y) then part.offsetX := 1;
333 end;
337 procedure g_GFX_SparkVel (fX, fY: Integer; Count: Word; VX, VY: Integer; DevX, DevY: Byte);
338 var
339 a: Integer;
340 DevX1, DevX2,
341 DevY1, DevY2: Byte;
342 l: Integer;
343 begin
344 l := Length(Particles);
345 if l = 0 then exit;
346 if Count > l then Count := l;
348 DevX1 := DevX div 2;
349 DevX2 := DevX + 1;
350 DevY1 := DevY div 2;
351 DevY2 := DevY + 1;
353 for a := 1 to Count do
354 begin
355 with Particles[CurrentParticle] do
356 begin
357 X := fX-DevX1+Random(DevX2);
358 Y := fY-DevY1+Random(DevY2);
360 VelX := VX + (Random-Random)*3;
361 VelY := VY + (Random-Random)*3;
363 if VelY > -4 then
364 if VelY-4 < -4 then
365 VelY := -4
366 else
367 VelY := VelY-4;
369 AccelX := -Sign(VelX)*Random/100;
370 AccelY := 0.8;
372 Red := 255;
373 Green := 100+Random(155);
374 Blue := 64;
375 Alpha := 255;
377 State := STATE_NORMAL;
378 Time := 0;
379 LiveTime := 30+Random(60);
380 ParticleType := PARTICLE_SPARK;
382 {CorrectOffsets(CurrentParticle);}
383 end;
385 if CurrentParticle+2 > MaxParticles then
386 CurrentParticle := 0
387 else
388 CurrentParticle := CurrentParticle+1;
389 end;
390 end;
393 procedure g_GFX_Blood(fX, fY: Integer; Count: Word; vx, vy: Integer;
394 DevX, DevY: Word; CR, CG, CB: Byte; Kind: Byte = BLOOD_NORMAL);
395 var
396 a: Integer;
397 DevX1, DevX2,
398 DevY1, DevY2: Word;
399 l: Integer;
400 CRnd: Byte;
401 CC: SmallInt;
402 begin
403 if Kind = BLOOD_SPARKS then
404 begin
405 g_GFX_SparkVel(fX, fY, 2 + Random(2), -VX div 2, -VY div 2, DevX, DevY);
406 Exit;
407 end;
408 l := Length(Particles);
409 if l = 0 then
410 Exit;
411 if Count > l then
412 Count := l;
414 DevX1 := DevX div 2;
415 DevX2 := DevX + 1;
416 DevY1 := DevY div 2;
417 DevY2 := DevY + 1;
419 for a := 1 to Count do
420 begin
421 with Particles[CurrentParticle] do
422 begin
423 X := fX - DevX1 + Random(DevX2);
424 Y := fY - DevY1 + Random(DevY2);
427 if (X < 0) or (X > gMapInfo.Width-1) or
428 (Y < 0) or (Y > gMapInfo.Height-1) or
429 ByteBool(gCollideMap[Y, X] and MARK_WALL) then
430 Continue;
432 if isWallAt(X, Y) then continue;
434 VelX := vx + (Random-Random)*3;
435 VelY := vy + (Random-Random)*3;
437 if VelY > -4 then
438 if VelY-4 < -4 then
439 VelY := -4
440 else
441 VelY := VelY-4;
443 AccelX := -Sign(VelX)*Random/100;
444 AccelY := 0.8;
446 CRnd := 20*Random(6);
447 if CR > 0 then
448 begin
449 CC := CR + CRnd - 50;
450 if CC < 0 then CC := 0;
451 if CC > 255 then CC := 255;
452 Red := CC;
453 end else
454 Red := 0;
455 if CG > 0 then
456 begin
457 CC := CG + CRnd - 50;
458 if CC < 0 then CC := 0;
459 if CC > 255 then CC := 255;
460 Green := CC;
461 end else
462 Green := 0;
463 if CB > 0 then
464 begin
465 CC := CB + CRnd - 50;
466 if CC < 0 then CC := 0;
467 if CC > 255 then CC := 255;
468 Blue := CC;
469 end else
470 Blue := 0;
472 Alpha := 255;
474 State := STATE_NORMAL;
475 Time := 0;
476 LiveTime := 120+Random(40);
477 ParticleType := PARTICLE_BLOOD;
479 {CorrectOffsets(CurrentParticle);}
480 end;
482 if CurrentParticle >= MaxParticles-1 then
483 CurrentParticle := 0
484 else
485 CurrentParticle := CurrentParticle+1;
486 end;
487 end;
490 procedure g_GFX_Spark(fX, fY: Integer; Count: Word; Angle: SmallInt; DevX, DevY: Byte);
491 var
492 a: Integer;
493 b: Single;
494 DevX1, DevX2,
495 DevY1, DevY2: Byte;
496 BaseVelX, BaseVelY: Single;
497 l: Integer;
498 begin
499 l := Length(Particles);
500 if l = 0 then
501 Exit;
502 if Count > l then
503 Count := l;
505 Angle := 360 - Angle;
507 DevX1 := DevX div 2;
508 DevX2 := DevX + 1;
509 DevY1 := DevY div 2;
510 DevY2 := DevY + 1;
512 b := DegToRad(Angle);
513 BaseVelX := cos(b);
514 BaseVelY := 1.6*sin(b);
515 if Abs(BaseVelX) < 0.01 then
516 BaseVelX := 0.0;
517 if Abs(BaseVelY) < 0.01 then
518 BaseVelY := 0.0;
519 for a := 1 to Count do
520 begin
521 with Particles[CurrentParticle] do
522 begin
523 X := fX-DevX1+Random(DevX2);
524 Y := fY-DevY1+Random(DevY2);
526 VelX := BaseVelX*Random;
527 VelY := BaseVelY-Random;
528 AccelX := VelX/3.0;
529 AccelY := VelY/5.0;
531 Red := 255;
532 Green := 100+Random(155);
533 Blue := 64;
534 Alpha := 255;
536 State := STATE_NORMAL;
537 Time := 0;
538 LiveTime := 30+Random(60);
539 ParticleType := PARTICLE_SPARK;
541 {CorrectOffsets(CurrentParticle);}
542 end;
544 if CurrentParticle+2 > MaxParticles then
545 CurrentParticle := 0
546 else
547 CurrentParticle := CurrentParticle+1;
548 end;
549 end;
551 procedure g_GFX_Water(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DevX, DevY, Color: Byte);
552 var
553 a: Integer;
554 DevX1, DevX2,
555 DevY1, DevY2: Byte;
556 l: Integer;
557 begin
558 l := Length(Particles);
559 if l = 0 then
560 Exit;
561 if Count > l then
562 Count := l;
564 if Abs(fVelX) < 3.0 then
565 fVelX := 3.0 - 6.0*Random;
567 DevX1 := DevX div 2;
568 DevX2 := DevX + 1;
569 DevY1 := DevY div 2;
570 DevY2 := DevY + 1;
572 for a := 1 to Count do
573 begin
574 with Particles[CurrentParticle] do
575 begin
576 X := fX-DevX1+Random(DevX2);
577 Y := fY-DevY1+Random(DevY2);
579 if Abs(fVelX) < 0.5 then
580 VelX := 1.0 - 2.0*Random
581 else
582 VelX := fVelX*Random;
583 if Random(10) < 7 then
584 VelX := -VelX;
585 VelY := fVelY*Random;
586 AccelX := 0.0;
587 AccelY := 0.8;
589 case Color of
590 1: // Êðàñíûé
591 begin
592 Red := 155 + Random(9)*10;
593 Green := Trunc(150*Random);
594 Blue := Green;
595 end;
596 2: // Çåëåíûé
597 begin
598 Red := Trunc(150*Random);
599 Green := 175 + Random(9)*10;
600 Blue := Red;
601 end;
602 3: // Ñèíèé
603 begin
604 Red := Trunc(200*Random);
605 Green := Red;
606 Blue := 175 + Random(9)*10;
607 end;
608 else // Ñåðûé
609 begin
610 Red := 90 + Random(12)*10;
611 Green := Red;
612 Blue := Red;
613 end;
614 end;
616 Alpha := 255;
618 State := STATE_NORMAL;
619 Time := 0;
620 LiveTime := 60+Random(60);
621 ParticleType := PARTICLE_WATER;
623 {CorrectOffsets(CurrentParticle);}
624 end;
626 if CurrentParticle+2 > MaxParticles then
627 CurrentParticle := 0
628 else
629 CurrentParticle := CurrentParticle+1;
630 end;
631 end;
633 procedure g_GFX_SimpleWater(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DefColor, CR, CG, CB: Byte);
634 var
635 a: Integer;
636 l: Integer;
637 begin
638 l := Length(Particles);
639 if l = 0 then
640 Exit;
641 if Count > l then
642 Count := l;
644 for a := 1 to Count do
645 begin
646 with Particles[CurrentParticle] do
647 begin
648 X := fX;
649 Y := fY;
651 VelX := fVelX;
652 VelY := fVelY;
653 AccelX := 0.0;
654 AccelY := 0.8;
656 case DefColor of
657 1: // Êðàñíûé
658 begin
659 Red := 155 + Random(9)*10;
660 Green := Trunc(150*Random);
661 Blue := Green;
662 end;
663 2: // Çåëåíûé
664 begin
665 Red := Trunc(150*Random);
666 Green := 175 + Random(9)*10;
667 Blue := Red;
668 end;
669 3: // Ñèíèé
670 begin
671 Red := Trunc(200*Random);
672 Green := Red;
673 Blue := 175 + Random(9)*10;
674 end;
675 4: // Ñâîé öâåò, ñâåòëåå
676 begin
677 Red := 20 + Random(19)*10;
678 Green := Red;
679 Blue := Red;
680 Red := Min(Red + CR, 255);
681 Green := Min(Green + CG, 255);
682 Blue := Min(Blue + CB, 255);
683 end;
684 5: // Ñâîé öâåò, òåìíåå
685 begin
686 Red := 20 + Random(19)*10;
687 Green := Red;
688 Blue := Red;
689 Red := Max(CR - Red, 0);
690 Green := Max(CG - Green, 0);
691 Blue := Max(CB - Blue, 0);
692 end;
693 else // Ñåðûé
694 begin
695 Red := 90 + Random(12)*10;
696 Green := Red;
697 Blue := Red;
698 end;
699 end;
701 Alpha := 255;
703 State := STATE_NORMAL;
704 Time := 0;
705 LiveTime := 60+Random(60);
706 ParticleType := PARTICLE_WATER;
708 {CorrectOffsets(CurrentParticle);}
709 end;
711 if CurrentParticle+2 > MaxParticles then
712 CurrentParticle := 0
713 else
714 CurrentParticle := CurrentParticle+1;
715 end;
716 end;
719 procedure g_GFX_Bubbles(fX, fY: Integer; Count: Word; DevX, DevY: Byte);
720 var
721 a: Integer;
722 DevX1, DevX2,
723 DevY1, DevY2: Byte;
724 l, liquidx: Integer;
725 begin
726 l := Length(Particles);
727 if l = 0 then
728 Exit;
729 if Count > l then
730 Count := l;
732 DevX1 := DevX div 2;
733 DevX2 := DevX + 1;
734 DevY1 := DevY div 2;
735 DevY2 := DevY + 1;
737 for a := 1 to Count do
738 begin
739 with Particles[CurrentParticle] do
740 begin
741 X := fX-DevX1+Random(DevX2);
742 Y := fY-DevY1+Random(DevY2);
744 if (X >= gMapInfo.Width) or (X <= 0) or
745 (Y >= gMapInfo.Height) or (Y <= 0) then
746 Continue;
748 (*
749 // don't spawn bubbles outside of the liquid
750 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
751 Continue;
752 *)
754 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
755 // tracer will return `false` if we started outside of the liquid
756 if not g_Map_TraceLiquid(X, Y, 0, -8, liquidx, liquidTopY) then continue;
758 VelX := 0;
759 VelY := -1-Random;
760 AccelX := 0;
761 AccelY := VelY/10;
763 Red := 255;
764 Green := 255;
765 Blue := 255;
766 Alpha := 255;
768 State := STATE_NORMAL;
769 Time := 0;
770 LiveTime := 65535;
771 ParticleType := PARTICLE_BUBBLES;
773 {CorrectOffsets(CurrentParticle);}
774 end;
776 if CurrentParticle+2 > MaxParticles then
777 CurrentParticle := 0
778 else
779 CurrentParticle := CurrentParticle+1;
780 end;
781 end;
783 procedure g_GFX_SetMax(Count: Integer);
784 var
785 a: Integer;
786 begin
787 if Count > 50000 then Count := 50000;
788 if (Count < 1) then Count := 1;
790 SetLength(Particles, Count);
791 for a := 0 to High(Particles) do Particles[a].State := STATE_FREE;
792 MaxParticles := Count;
793 //if CurrentParticle >= Count then
794 CurrentParticle := 0;
795 end;
797 function g_GFX_GetMax(): Integer;
798 begin
799 Result := MaxParticles;
800 end;
802 function FindOnceAnim: DWORD;
803 var
804 i: Integer;
805 begin
806 if OnceAnims <> nil then
807 for i := 0 to High(OnceAnims) do
808 if OnceAnims[i].Animation = nil then
809 begin
810 Result := i;
811 Exit;
812 end;
814 if OnceAnims = nil then
815 begin
816 SetLength(OnceAnims, 16);
817 Result := 0;
818 end
819 else
820 begin
821 Result := High(OnceAnims) + 1;
822 SetLength(OnceAnims, Length(OnceAnims) + 16);
823 end;
824 end;
826 procedure g_GFX_OnceAnim(X, Y: Integer; Anim: TAnimation; AnimType: Byte = 0);
827 var
828 find_id: DWORD;
829 begin
830 if Anim = nil then
831 Exit;
833 find_id := FindOnceAnim();
835 OnceAnims[find_id].AnimType := AnimType;
836 OnceAnims[find_id].Animation := TAnimation.Create(Anim.FramesID, Anim.Loop, Anim.Speed);
837 OnceAnims[find_id].Animation.Blending := Anim.Blending;
838 OnceAnims[find_id].Animation.Alpha := Anim.Alpha;
839 OnceAnims[find_id].X := X;
840 OnceAnims[find_id].Y := Y;
841 end;
843 procedure g_GFX_Update();
844 var
845 a: Integer;
846 w, h: Integer;
847 dX, dY: SmallInt;
848 b, len: Integer;
849 s: ShortInt;
850 //c: Byte;
851 begin
852 if not gpart_dbg_enabled then exit;
853 if Particles <> nil then
854 begin
855 w := gMapInfo.Width;
856 h := gMapInfo.Height;
858 len := High(Particles);
860 for a := 0 to len do
861 begin
862 if Particles[a].State <> STATE_FREE then
863 begin
864 with Particles[a] do
865 begin
866 if Time = LiveTime then State := STATE_FREE;
867 if (X+1 >= w) or (Y+1 >= h) or (X <= 0) or (Y <= 0) then State := STATE_FREE;
868 if State = STATE_FREE then Continue;
869 //e_WriteLog(Format('particle #%d: %d', [State, ParticleType]), MSG_NOTIFY);
871 case ParticleType of
872 PARTICLE_BLOOD:
873 begin
874 if gAdvBlood then
875 begin
876 if (State = STATE_STICK) then
878 if (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
879 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) and
880 (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
881 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED))
882 then
884 if (not isBlockedAt(X, Y-1)) and
885 (not isBlockedAt(X, Y+1)) and
886 (not isBlockedAt(X-1, Y)) and
887 (not isBlockedAt(X+1, Y))
888 then
889 begin // Îòëèïëà - êàïàåò
890 VelY := 0.5;
891 AccelY := 0.15;
892 State := STATE_NORMAL;
893 end
894 else
895 if Random(200) = 100 then
896 begin // Ïðèëåïëåíà - íî âîçìîæíî ñòåêàåò
897 VelY := 0.5;
898 AccelY := 0.15;
899 Continue;
900 end;
902 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
903 begin
904 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
905 begin // Ëèôò ââåðõ
906 if VelY > -4-Random(3) then
907 VelY := VelY - 0.8;
908 if Abs(VelX) > 0.1 then
909 VelX := VelX - VelX/10.0;
910 VelX := VelX + (Random-Random)*0.2;
911 AccelY := 0.15;
912 end;
913 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
914 begin // Ïîòîê âëåâî
915 if VelX > -8-Random(3) then
916 VelX := VelX - 0.8;
917 AccelY := 0.15;
918 end;
919 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
920 begin // Ïîòîê âïðàâî
921 if VelX < 8+Random(3) then
922 VelX := VelX + 0.8;
923 AccelY := 0.15;
924 end;
925 end;
927 dX := Round(VelX);
928 dY := Round(VelY);
930 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
931 if (State <> STATE_STICK) and
932 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
933 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
934 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
935 begin // Âèñèò â âîçäóõå - êàïàåò
936 VelY := 0.8;
937 AccelY := 0.5;
938 State := STATE_NORMAL;
939 end;
941 if dX <> 0 then
942 begin
943 if dX > 0 then
944 s := 1
945 else
946 s := -1;
948 dX := Abs(dX);
950 for b := 1 to dX do
951 begin
952 if (X+s >= w) or (X+s <= 0) then
953 begin
954 State := STATE_FREE;
955 Break;
956 end;
958 //c := gCollideMap[Y, X+s];
960 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
961 begin // Ñòåíà/äâåðü
962 VelX := 0;
963 VelY := 0;
964 AccelX := 0;
965 AccelY := 0;
966 State := STATE_STICK;
967 Break;
968 end;
970 X := X+s;
971 end;
972 end;
974 if dY <> 0 then
975 begin
976 if dY > 0 then
977 s := 1
978 else
979 s := -1;
981 dY := Abs(dY);
983 for b := 1 to dY do
984 begin
985 if (Y+s >= h) or (Y+s <= 0) then
986 begin
987 State := STATE_FREE;
988 Break;
989 end;
991 //c := gCollideMap[Y+s, X];
993 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
994 begin // Ñòåíà/äâåðü
995 VelX := 0;
996 VelY := 0;
997 AccelX := 0;
998 AccelY := 0;
999 if (s > 0) and (State <> STATE_STICK) then
1000 State := STATE_NORMAL
1001 else
1002 State := STATE_STICK;
1003 Break;
1004 end;
1006 Y := Y+s;
1007 end;
1008 end;
1009 end // if gAdvBlood
1010 else
1011 begin
1012 dX := Round(VelX);
1013 dY := Round(VelY);
1015 if (X+dX >= w) or (Y+dY >= h) or
1016 (X+dX <= 0) or (Y+dY <= 0) or
1017 isBlockedAt(X+dX, Y+dY) {ByteBool(gCollideMap[Y+dY, X+dX] and MARK_BLOCKED)} then
1018 begin // Ñòåíà/äâåðü/ãðàíèöà
1019 State := STATE_FREE;
1020 VelX := 0;
1021 VelY := 0;
1022 end
1023 else
1024 begin
1025 Y := Y + dY;
1026 X := X + dX;
1027 end;
1028 end;
1030 VelX := VelX + AccelX;
1031 VelY := VelY + AccelY;
1033 // Êðîâü ðàñòâîðÿåòñÿ â æèäêîñòè:
1034 if isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1035 begin
1036 Inc(Time);
1038 Alpha := 255 - Trunc((255.0 * Time) / LiveTime);
1039 end;
1040 end;
1042 PARTICLE_SPARK:
1043 begin
1044 dX := Round(VelX);
1045 dY := Round(VelY);
1047 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) and
1048 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
1049 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
1050 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
1051 begin // Âèñèò â âîçäóõå
1052 VelY := 0.8;
1053 AccelY := 0.5;
1054 end;
1056 if dX <> 0 then
1057 begin
1058 if dX > 0 then
1059 s := 1
1060 else
1061 s := -1;
1063 dX := Abs(dX);
1065 for b := 1 to dX do
1066 begin
1067 if (X+s >= w) or (X+s <= 0) then
1068 begin
1069 State := STATE_FREE;
1070 Break;
1071 end;
1073 //c := gCollideMap[Y, X+s];
1075 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
1076 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1077 VelX := 0;
1078 AccelX := 0;
1079 Break;
1080 end
1081 else // Ïóñòî:
1082 if not isAnythingAt(X+s, Y) {c = MARK_FREE} then
1083 X := X + s
1084 else // Îñòàëüíîå:
1085 begin
1086 State := STATE_FREE;
1087 Break;
1088 end;
1089 end;
1090 end;
1092 if dY <> 0 then
1093 begin
1094 if dY > 0 then
1095 s := 1
1096 else
1097 s := -1;
1099 dY := Abs(dY);
1101 for b := 1 to dY do
1102 begin
1103 if (Y+s >= h) or (Y+s <= 0) then
1104 begin
1105 State := STATE_FREE;
1106 Break;
1107 end;
1109 //c := gCollideMap[Y+s, X];
1111 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
1112 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1113 if s < 0 then
1114 begin
1115 VelY := -VelY;
1116 AccelY := Abs(AccelY);
1117 end
1118 else // Èëè íå ïàäàåò
1119 begin
1120 VelX := 0;
1121 AccelX := 0;
1122 VelY := 0;
1123 AccelY := 0.8;
1124 end;
1126 Break;
1127 end
1128 else // Ïóñòî:
1129 if not isAnythingAt(X, Y+s) {c = MARK_FREE} then
1130 Y := Y + s
1131 else // Îñàëüíîå:
1132 begin
1133 State := STATE_FREE;
1134 Break;
1135 end;
1136 end;
1137 end;
1139 if VelX <> 0.0 then
1140 VelX := VelX + AccelX;
1141 if VelY <> 0.0 then
1142 begin
1143 if AccelY < 10 then
1144 AccelY := AccelY + 0.08;
1145 VelY := VelY + AccelY;
1146 end;
1148 Time := Time + 1;
1149 end;
1151 PARTICLE_WATER:
1152 begin
1153 if (State = STATE_STICK) and (Random(30) = 15) then
1154 begin // Ñòåêàåò/îòëèïàåò
1155 VelY := 0.5;
1156 AccelY := 0.15;
1157 if (not isBlockedAt(X-1, Y) {ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)}) and
1158 (not isBlockedAt(X+1, Y) {ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)}) then
1159 State := STATE_NORMAL;
1160 Continue;
1161 end;
1163 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
1164 begin
1165 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
1166 begin // Ëèôò ââåðõ
1167 if VelY > -4-Random(3) then
1168 VelY := VelY - 0.8;
1169 if Abs(VelX) > 0.1 then
1170 VelX := VelX - VelX/10.0;
1171 VelX := VelX + (Random-Random)*0.2;
1172 AccelY := 0.15;
1173 end;
1174 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
1175 begin // Ïîòîê âëåâî
1176 if VelX > -8-Random(3) then
1177 VelX := VelX - 0.8;
1178 AccelY := 0.15;
1179 end;
1180 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
1181 begin // Ïîòîê âïðàâî
1182 if VelX < 8+Random(3) then
1183 VelX := VelX + 0.8;
1184 AccelY := 0.15;
1185 end;
1186 end;
1188 dX := Round(VelX);
1189 dY := Round(VelY);
1191 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
1192 if (State <> STATE_STICK) and
1193 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
1194 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
1195 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
1196 begin // Âèñèò â âîçäóõå - êàïàåò
1197 VelY := 0.8;
1198 AccelY := 0.5;
1199 State := STATE_NORMAL;
1200 end;
1202 if dX <> 0 then
1203 begin
1204 if dX > 0 then
1205 s := 1
1206 else
1207 s := -1;
1209 for b := 1 to Abs(dX) do
1210 begin
1211 if (X+s >= w) or (X+s <= 0) then
1212 begin // Ñáîêó ãðàíèöà
1213 State := STATE_FREE;
1214 Break;
1215 end;
1217 //c := gCollideMap[Y, X+s];
1219 if isLiquidAt(X+s, Y) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then
1220 begin // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1221 State := STATE_FREE;
1222 Break;
1223 end;
1225 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
1226 begin // Ñòåíà/äâåðü
1227 VelX := 0;
1228 VelY := 0;
1229 AccelX := 0;
1230 AccelY := 0;
1231 State := STATE_STICK;
1232 Break;
1233 end;
1235 X := X+s;
1236 end;
1237 end;
1239 if dY <> 0 then
1240 begin
1241 if dY > 0 then
1242 s := 1
1243 else
1244 s := -1;
1246 for b := 1 to Abs(dY) do
1247 begin
1248 if (Y+s >= h) or (Y+s <= 0) then
1249 begin // Ñíèçó/ñâåðõó ãðàíèöà
1250 State := STATE_FREE;
1251 Break;
1252 end;
1254 //c := gCollideMap[Y+s, X];
1256 if isLiquidAt(X, Y+s) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then
1257 begin // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1258 State := STATE_FREE;
1259 Break;
1260 end;
1262 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
1263 begin // Ñòåíà/äâåðü
1264 VelX := 0;
1265 VelY := 0;
1266 AccelX := 0;
1267 AccelY := 0;
1268 if (s > 0) and (State <> STATE_STICK) then
1269 State := STATE_NORMAL
1270 else
1271 State := STATE_STICK;
1272 Break;
1273 end;
1275 Y := Y+s;
1276 end;
1277 end;
1279 VelX := VelX + AccelX;
1280 VelY := VelY + AccelY;
1282 Time := Time + 1;
1283 end;
1285 PARTICLE_BUBBLES:
1286 begin
1287 dY := Round(VelY);
1289 if dY <> 0 then
1290 begin
1291 if dY > 0 then
1292 s := 1
1293 else
1294 s := -1;
1296 for b := 1 to Abs(dY) do
1297 begin
1298 if (Y+s >= h) or (Y+s <= 0) then
1299 begin
1300 State := STATE_FREE;
1301 Break;
1302 end;
1304 if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
1305 begin // Óæå íå æèäêîñòü
1306 State := STATE_FREE;
1307 Break;
1308 end;
1310 Y := Y+s;
1311 end;
1312 end;
1314 if VelY > -4 then
1315 VelY := VelY + AccelY;
1317 Time := Time + 1;
1318 end;
1319 end; // case
1321 {CorrectOffsets(a);}
1322 end; // with
1323 end; // if
1324 end; // for
1325 end; // Particles <> nil
1327 if OnceAnims <> nil then
1328 begin
1329 for a := 0 to High(OnceAnims) do
1330 if OnceAnims[a].Animation <> nil then
1331 begin
1332 case OnceAnims[a].AnimType of
1333 ONCEANIM_SMOKE:
1334 begin
1335 if Random(3) = 0 then
1336 OnceAnims[a].X := OnceAnims[a].X-1+Random(3);
1337 if Random(2) = 0 then
1338 OnceAnims[a].Y := OnceAnims[a].Y-Random(2);
1339 end;
1340 end;
1342 if OnceAnims[a].Animation.Played then
1343 begin
1344 OnceAnims[a].Animation.Free();
1345 OnceAnims[a].Animation := nil;
1346 end
1347 else
1348 OnceAnims[a].Animation.Update();
1349 end;
1350 end;
1351 end;
1353 procedure g_GFX_Draw();
1354 var
1355 a, len: Integer;
1356 begin
1357 if Particles <> nil then
1358 begin
1359 glDisable(GL_TEXTURE_2D);
1360 glPointSize(2);
1362 glEnable(GL_BLEND);
1363 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1365 glBegin(GL_POINTS);
1367 len := High(Particles);
1369 for a := 0 to len do
1370 with Particles[a] do
1371 if (State <> STATE_FREE) and (X >= sX) and (Y >= sY) and
1372 (X <= sX+sWidth) and (sY <= sY+sHeight) then
1373 begin
1374 glColor4ub(Red, Green, Blue, Alpha);
1375 glVertex2i(X + offsetX, Y + offsetY);
1376 end;
1378 glEnd();
1380 glDisable(GL_BLEND);
1381 end;
1383 if OnceAnims <> nil then
1384 for a := 0 to High(OnceAnims) do
1385 if OnceAnims[a].Animation <> nil then
1386 with OnceAnims[a] do
1387 Animation.Draw(X, Y, M_NONE);
1388 end;
1390 end.