DEADSOFTWARE

better hitscan tracer; no more level trace bitmap (but no more particles too, alas)
[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 TParticle = record
78 X, Y: Integer;
79 VelX, VelY: Single;
80 AccelX, AccelY: Single;
81 Red, Green, Blue: Byte;
82 Alpha: Byte;
83 Time, LiveTime: Word;
84 State: Byte;
85 ParticleType: Byte;
86 offsetX, offsetY: ShortInt;
87 end;
89 TOnceAnim = record
90 AnimType: Byte;
91 X, Y: Integer;
92 Animation: TAnimation;
93 end;
95 const
96 PARTICLE_BLOOD = 0;
97 PARTICLE_SPARK = 1;
98 PARTICLE_BUBBLES = 2;
99 PARTICLE_WATER = 3;
100 STATE_FREE = 0;
101 STATE_NORMAL = 1;
102 STATE_STICK = 2;
104 var
105 Particles: Array of TParticle;
106 OnceAnims: Array of TOnceAnim;
107 MaxParticles: Integer;
108 CurrentParticle: Integer;
110 procedure g_Mark(x, y, Width, Height: Integer; t: Byte; st: Boolean);
111 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
112 var
113 yy, y2, xx, x2: Integer;
114 {$ENDIF}
115 begin
116 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
117 if x < 0 then
118 begin
119 Width := Width + x;
120 x := 0;
121 end;
123 if Width < 0 then
124 Exit;
126 if y < 0 then
127 begin
128 Height := Height + y;
129 y := 0;
130 end;
132 if Height < 0 then
133 Exit;
135 if x > gMapInfo.Width then
136 Exit;
137 if y > gMapInfo.Height then
138 Exit;
140 y2 := y + Height - 1;
141 if y2 > gMapInfo.Height then
142 y2 := gMapInfo.Height;
144 x2 := x + Width - 1;
145 if x2 > gMapInfo.Width then
146 x2 := gMapInfo.Width;
148 if st then
149 begin // Óñòàíîâèòü ïðèçíàê
150 for yy := y to y2 do
151 for xx := x to x2 do
152 gCollideMap[yy][xx] := gCollideMap[yy][xx] or t;
153 end
154 else
155 begin // Óáðàòü ïðèçíàê
156 t := not t;
157 for yy := y to y2 do
158 for xx := x to x2 do
159 gCollideMap[yy][xx] := gCollideMap[yy][xx] and t;
160 end;
161 {$ENDIF}
162 end;
164 procedure CreateCollideMap();
165 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
166 var
167 a: Integer;
168 {$ENDIF}
169 begin
170 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
171 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
172 SetLength(gCollideMap, gMapInfo.Height+1);
173 for a := 0 to High(gCollideMap) do
174 SetLength(gCollideMap[a], gMapInfo.Width+1);
176 if gWater <> nil then
177 begin
178 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 2/6', 0, True);
179 for a := 0 to High(gWater) do
180 with gWater[a] do
181 g_Mark(X, Y, Width, Height, MARK_WATER, True);
182 end;
184 if gAcid1 <> nil then
185 begin
186 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 3/6', 0, True);
187 for a := 0 to High(gAcid1) do
188 with gAcid1[a] do
189 g_Mark(X, Y, Width, Height, MARK_ACID, True);
190 end;
192 if gAcid2 <> nil then
193 begin
194 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 4/6', 0, True);
195 for a := 0 to High(gAcid2) do
196 with gAcid2[a] do
197 g_Mark(X, Y, Width, Height, MARK_ACID, True);
198 end;
200 if gLifts <> nil then
201 begin
202 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 5/6', 0, True);
203 for a := 0 to High(gLifts) do
204 with gLifts[a] do
205 begin
206 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
208 if LiftType = 0 then
209 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
210 else if LiftType = 1 then
211 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
212 else if LiftType = 2 then
213 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
214 else if LiftType = 3 then
215 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True)
216 end;
217 end;
219 if gWalls <> nil then
220 begin
221 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 6/6', 0, True);
222 for a := 0 to High(gWalls) do
223 begin
224 if gWalls[a].Door then
225 begin
226 // Çàêðûòàÿ äâåðü:
227 if gWalls[a].Enabled then
228 with gWalls[a] do
229 g_Mark(X, Y, Width, Height, MARK_DOOR, True)
230 else // Îòêðûòàÿ äâåðü:
231 if gWalls[a].Enabled then
232 with gWalls[a] do
233 g_Mark(X, Y, Width, Height, MARK_DOOR, False);
234 end
235 else // Ñòåíà
236 with gWalls[a] do
237 g_Mark(X, Y, Width, Height, MARK_WALL, True);
238 end;
239 end;
240 {$ENDIF}
241 end;
243 procedure g_GFX_Init();
244 begin
245 CreateCollideMap();
246 end;
248 procedure g_GFX_Free();
249 var
250 a: Integer;
251 begin
252 Particles := nil;
253 SetLength(Particles, MaxParticles);
254 CurrentParticle := 0;
256 if OnceAnims <> nil then
257 begin
258 for a := 0 to High(OnceAnims) do
259 OnceAnims[a].Animation.Free();
261 OnceAnims := nil;
262 end;
264 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
265 gCollideMap := nil;
266 {$ENDIF}
267 end;
270 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
271 procedure CorrectOffsets(id: Integer);
272 begin
273 with Particles[id] do
274 begin
275 if (X >= 0) and (Y > 0) and
276 (Y < Length(gCollideMap)) and (X < Length(gCollideMap[0])) and
277 (ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) then
278 offsetY := 1 // Ñòåíà ñâåðõó
279 else
280 offsetY := 0;
282 if (X > 0) and (Y >= 0) and
283 (Y < Length(gCollideMap)) and (X < Length(gCollideMap[0])) and
284 (ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) then
285 offsetX := 1 // Ñòåíà ñëåâà
286 else
287 offsetX := 0;
288 end;
289 end;
290 {$ENDIF}
293 procedure g_GFX_SparkVel(fX, fY: Integer; Count: Word; VX, VY: Integer; DevX, DevY: Byte);
294 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
295 var
296 a: Integer;
297 DevX1, DevX2,
298 DevY1, DevY2: Byte;
299 l: Integer;
300 {$ENDIF}
301 begin
302 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
303 l := Length(Particles);
304 if l = 0 then
305 Exit;
306 if Count > l then
307 Count := l;
309 DevX1 := DevX div 2;
310 DevX2 := DevX + 1;
311 DevY1 := DevY div 2;
312 DevY2 := DevY + 1;
314 for a := 1 to Count do
315 begin
316 with Particles[CurrentParticle] do
317 begin
318 X := fX-DevX1+Random(DevX2);
319 Y := fY-DevY1+Random(DevY2);
321 VelX := VX + (Random-Random)*3;
322 VelY := VY + (Random-Random)*3;
324 if VelY > -4 then
325 if VelY-4 < -4 then
326 VelY := -4
327 else
328 VelY := VelY-4;
330 AccelX := -Sign(VelX)*Random/100;
331 AccelY := 0.8;
333 Red := 255;
334 Green := 100+Random(155);
335 Blue := 64;
336 Alpha := 255;
338 State := STATE_NORMAL;
339 Time := 0;
340 LiveTime := 30+Random(60);
341 ParticleType := PARTICLE_SPARK;
343 CorrectOffsets(CurrentParticle);
344 end;
346 if CurrentParticle+2 > MaxParticles then
347 CurrentParticle := 0
348 else
349 CurrentParticle := CurrentParticle+1;
350 end;
351 {$ENDIF}
352 end;
354 procedure g_GFX_Blood(fX, fY: Integer; Count: Word; vx, vy: Integer;
355 DevX, DevY: Word; CR, CG, CB: Byte; Kind: Byte = BLOOD_NORMAL);
356 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
357 var
358 a: Integer;
359 DevX1, DevX2,
360 DevY1, DevY2: Word;
361 l: Integer;
362 CRnd: Byte;
363 CC: SmallInt;
364 {$ENDIF}
365 begin
366 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
367 if Kind = BLOOD_SPARKS then
368 begin
369 g_GFX_SparkVel(fX, fY, 2 + Random(2), -VX div 2, -VY div 2, DevX, DevY);
370 Exit;
371 end;
372 l := Length(Particles);
373 if l = 0 then
374 Exit;
375 if Count > l then
376 Count := l;
378 DevX1 := DevX div 2;
379 DevX2 := DevX + 1;
380 DevY1 := DevY div 2;
381 DevY2 := DevY + 1;
383 for a := 1 to Count do
384 begin
385 with Particles[CurrentParticle] do
386 begin
387 X := fX - DevX1 + Random(DevX2);
388 Y := fY - DevY1 + Random(DevY2);
390 if (X < 0) or (X > gMapInfo.Width-1) or
391 (Y < 0) or (Y > gMapInfo.Height-1) or
392 ByteBool(gCollideMap[Y, X] and MARK_WALL) then
393 Continue;
395 VelX := vx + (Random-Random)*3;
396 VelY := vy + (Random-Random)*3;
398 if VelY > -4 then
399 if VelY-4 < -4 then
400 VelY := -4
401 else
402 VelY := VelY-4;
404 AccelX := -Sign(VelX)*Random/100;
405 AccelY := 0.8;
407 CRnd := 20*Random(6);
408 if CR > 0 then
409 begin
410 CC := CR + CRnd - 50;
411 if CC < 0 then CC := 0;
412 if CC > 255 then CC := 255;
413 Red := CC;
414 end else
415 Red := 0;
416 if CG > 0 then
417 begin
418 CC := CG + CRnd - 50;
419 if CC < 0 then CC := 0;
420 if CC > 255 then CC := 255;
421 Green := CC;
422 end else
423 Green := 0;
424 if CB > 0 then
425 begin
426 CC := CB + CRnd - 50;
427 if CC < 0 then CC := 0;
428 if CC > 255 then CC := 255;
429 Blue := CC;
430 end else
431 Blue := 0;
433 Alpha := 255;
435 State := STATE_NORMAL;
436 Time := 0;
437 LiveTime := 120+Random(40);
438 ParticleType := PARTICLE_BLOOD;
440 CorrectOffsets(CurrentParticle);
441 end;
443 if CurrentParticle >= MaxParticles-1 then
444 CurrentParticle := 0
445 else
446 CurrentParticle := CurrentParticle+1;
447 end;
448 {$ENDIF}
449 end;
451 procedure g_GFX_Spark(fX, fY: Integer; Count: Word; Angle: SmallInt; DevX, DevY: Byte);
452 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
453 var
454 a: Integer;
455 b: Single;
456 DevX1, DevX2,
457 DevY1, DevY2: Byte;
458 BaseVelX, BaseVelY: Single;
459 l: Integer;
460 {$ENDIF}
461 begin
462 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
463 l := Length(Particles);
464 if l = 0 then
465 Exit;
466 if Count > l then
467 Count := l;
469 Angle := 360 - Angle;
471 DevX1 := DevX div 2;
472 DevX2 := DevX + 1;
473 DevY1 := DevY div 2;
474 DevY2 := DevY + 1;
476 b := DegToRad(Angle);
477 BaseVelX := cos(b);
478 BaseVelY := 1.6*sin(b);
479 if Abs(BaseVelX) < 0.01 then
480 BaseVelX := 0.0;
481 if Abs(BaseVelY) < 0.01 then
482 BaseVelY := 0.0;
483 for a := 1 to Count do
484 begin
485 with Particles[CurrentParticle] do
486 begin
487 X := fX-DevX1+Random(DevX2);
488 Y := fY-DevY1+Random(DevY2);
490 VelX := BaseVelX*Random;
491 VelY := BaseVelY-Random;
492 AccelX := VelX/3.0;
493 AccelY := VelY/5.0;
495 Red := 255;
496 Green := 100+Random(155);
497 Blue := 64;
498 Alpha := 255;
500 State := STATE_NORMAL;
501 Time := 0;
502 LiveTime := 30+Random(60);
503 ParticleType := PARTICLE_SPARK;
505 CorrectOffsets(CurrentParticle);
506 end;
508 if CurrentParticle+2 > MaxParticles then
509 CurrentParticle := 0
510 else
511 CurrentParticle := CurrentParticle+1;
512 end;
513 {$ENDIF}
514 end;
516 procedure g_GFX_Water(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DevX, DevY, Color: Byte);
517 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
518 var
519 a: Integer;
520 DevX1, DevX2,
521 DevY1, DevY2: Byte;
522 l: Integer;
523 {$ENDIF}
524 begin
525 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
526 l := Length(Particles);
527 if l = 0 then
528 Exit;
529 if Count > l then
530 Count := l;
532 if Abs(fVelX) < 3.0 then
533 fVelX := 3.0 - 6.0*Random;
535 DevX1 := DevX div 2;
536 DevX2 := DevX + 1;
537 DevY1 := DevY div 2;
538 DevY2 := DevY + 1;
540 for a := 1 to Count do
541 begin
542 with Particles[CurrentParticle] do
543 begin
544 X := fX-DevX1+Random(DevX2);
545 Y := fY-DevY1+Random(DevY2);
547 if Abs(fVelX) < 0.5 then
548 VelX := 1.0 - 2.0*Random
549 else
550 VelX := fVelX*Random;
551 if Random(10) < 7 then
552 VelX := -VelX;
553 VelY := fVelY*Random;
554 AccelX := 0.0;
555 AccelY := 0.8;
557 case Color of
558 1: // Êðàñíûé
559 begin
560 Red := 155 + Random(9)*10;
561 Green := Trunc(150*Random);
562 Blue := Green;
563 end;
564 2: // Çåëåíûé
565 begin
566 Red := Trunc(150*Random);
567 Green := 175 + Random(9)*10;
568 Blue := Red;
569 end;
570 3: // Ñèíèé
571 begin
572 Red := Trunc(200*Random);
573 Green := Red;
574 Blue := 175 + Random(9)*10;
575 end;
576 else // Ñåðûé
577 begin
578 Red := 90 + Random(12)*10;
579 Green := Red;
580 Blue := Red;
581 end;
582 end;
584 Alpha := 255;
586 State := STATE_NORMAL;
587 Time := 0;
588 LiveTime := 60+Random(60);
589 ParticleType := PARTICLE_WATER;
591 CorrectOffsets(CurrentParticle);
592 end;
594 if CurrentParticle+2 > MaxParticles then
595 CurrentParticle := 0
596 else
597 CurrentParticle := CurrentParticle+1;
598 end;
599 {$ENDIF}
600 end;
602 procedure g_GFX_SimpleWater(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DefColor, CR, CG, CB: Byte);
603 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
604 var
605 a: Integer;
606 l: Integer;
607 {$ENDIF}
608 begin
609 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
610 l := Length(Particles);
611 if l = 0 then
612 Exit;
613 if Count > l then
614 Count := l;
616 for a := 1 to Count do
617 begin
618 with Particles[CurrentParticle] do
619 begin
620 X := fX;
621 Y := fY;
623 VelX := fVelX;
624 VelY := fVelY;
625 AccelX := 0.0;
626 AccelY := 0.8;
628 case DefColor of
629 1: // Êðàñíûé
630 begin
631 Red := 155 + Random(9)*10;
632 Green := Trunc(150*Random);
633 Blue := Green;
634 end;
635 2: // Çåëåíûé
636 begin
637 Red := Trunc(150*Random);
638 Green := 175 + Random(9)*10;
639 Blue := Red;
640 end;
641 3: // Ñèíèé
642 begin
643 Red := Trunc(200*Random);
644 Green := Red;
645 Blue := 175 + Random(9)*10;
646 end;
647 4: // Ñâîé öâåò, ñâåòëåå
648 begin
649 Red := 20 + Random(19)*10;
650 Green := Red;
651 Blue := Red;
652 Red := Min(Red + CR, 255);
653 Green := Min(Green + CG, 255);
654 Blue := Min(Blue + CB, 255);
655 end;
656 5: // Ñâîé öâåò, òåìíåå
657 begin
658 Red := 20 + Random(19)*10;
659 Green := Red;
660 Blue := Red;
661 Red := Max(CR - Red, 0);
662 Green := Max(CG - Green, 0);
663 Blue := Max(CB - Blue, 0);
664 end;
665 else // Ñåðûé
666 begin
667 Red := 90 + Random(12)*10;
668 Green := Red;
669 Blue := Red;
670 end;
671 end;
673 Alpha := 255;
675 State := STATE_NORMAL;
676 Time := 0;
677 LiveTime := 60+Random(60);
678 ParticleType := PARTICLE_WATER;
680 CorrectOffsets(CurrentParticle);
681 end;
683 if CurrentParticle+2 > MaxParticles then
684 CurrentParticle := 0
685 else
686 CurrentParticle := CurrentParticle+1;
687 end;
688 {$ENDIF}
689 end;
691 procedure g_GFX_Bubbles(fX, fY: Integer; Count: Word; DevX, DevY: Byte);
692 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
693 var
694 a: Integer;
695 DevX1, DevX2,
696 DevY1, DevY2: Byte;
697 l: Integer;
698 {$ENDIF}
699 begin
700 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
701 l := Length(Particles);
702 if l = 0 then
703 Exit;
704 if Count > l then
705 Count := l;
707 DevX1 := DevX div 2;
708 DevX2 := DevX + 1;
709 DevY1 := DevY div 2;
710 DevY2 := DevY + 1;
712 for a := 1 to Count do
713 begin
714 with Particles[CurrentParticle] do
715 begin
716 X := fX-DevX1+Random(DevX2);
717 Y := fY-DevY1+Random(DevY2);
719 if (X >= gMapInfo.Width) or (X <= 0) or
720 (Y >= gMapInfo.Height) or (Y <= 0) then
721 Continue;
723 if not ByteBool(gCollideMap[Y, X] and MARK_LIQUID) then
724 Continue;
726 VelX := 0;
727 VelY := -1-Random;
728 AccelX := 0;
729 AccelY := VelY/10;
731 Red := 255;
732 Green := 255;
733 Blue := 255;
734 Alpha := 255;
736 State := STATE_NORMAL;
737 Time := 0;
738 LiveTime := 65535;
739 ParticleType := PARTICLE_BUBBLES;
741 CorrectOffsets(CurrentParticle);
742 end;
744 if CurrentParticle+2 > MaxParticles then
745 CurrentParticle := 0
746 else
747 CurrentParticle := CurrentParticle+1;
748 end;
749 {$ENDIF}
750 end;
752 procedure g_GFX_SetMax(Count: Integer);
753 begin
754 if Count > 50000 then
755 Count := 50000;
757 SetLength(Particles, Count);
758 MaxParticles := Count;
759 if CurrentParticle >= Count then
760 CurrentParticle := 0;
761 end;
763 function g_GFX_GetMax(): Integer;
764 begin
765 Result := MaxParticles;
766 end;
768 function FindOnceAnim: DWORD;
769 var
770 i: Integer;
771 begin
772 if OnceAnims <> nil then
773 for i := 0 to High(OnceAnims) do
774 if OnceAnims[i].Animation = nil then
775 begin
776 Result := i;
777 Exit;
778 end;
780 if OnceAnims = nil then
781 begin
782 SetLength(OnceAnims, 16);
783 Result := 0;
784 end
785 else
786 begin
787 Result := High(OnceAnims) + 1;
788 SetLength(OnceAnims, Length(OnceAnims) + 16);
789 end;
790 end;
792 procedure g_GFX_OnceAnim(X, Y: Integer; Anim: TAnimation; AnimType: Byte = 0);
793 var
794 find_id: DWORD;
795 begin
796 if Anim = nil then
797 Exit;
799 find_id := FindOnceAnim();
801 OnceAnims[find_id].AnimType := AnimType;
802 OnceAnims[find_id].Animation := TAnimation.Create(Anim.FramesID, Anim.Loop, Anim.Speed);
803 OnceAnims[find_id].Animation.Blending := Anim.Blending;
804 OnceAnims[find_id].Animation.Alpha := Anim.Alpha;
805 OnceAnims[find_id].X := X;
806 OnceAnims[find_id].Y := Y;
807 end;
809 procedure g_GFX_Update();
810 var
811 a: Integer;
812 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
813 w, h: Integer;
814 dX, dY: SmallInt;
815 b, len: Integer;
816 s: ShortInt;
817 c: Byte;
818 {$ENDIF}
819 begin
820 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
821 if Particles <> nil then
822 begin
823 w := gMapInfo.Width;
824 h := gMapInfo.Height;
826 len := High(Particles);
828 for a := 0 to len do
829 if Particles[a].State <> 0 then
830 with Particles[a] do
831 begin
832 if Time = LiveTime then
833 State := STATE_FREE;
834 if (X+1 >= w) or (Y+1 >= h) or (X <= 0) or (Y <= 0) then
835 State := STATE_FREE;
836 if State = STATE_FREE then
837 Continue;
839 case ParticleType of
840 PARTICLE_BLOOD:
841 begin
842 if gAdvBlood then
843 begin
844 if (State = STATE_STICK) then
845 if (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
846 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) and
847 (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
848 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)) then
849 begin // Îòëèïëà - êàïàåò
850 VelY := 0.5;
851 AccelY := 0.15;
852 State := STATE_NORMAL;
853 end
854 else
855 if Random(200) = 100 then
856 begin // Ïðèëåïëåíà - íî âîçìîæíî ñòåêàåò
857 VelY := 0.5;
858 AccelY := 0.15;
859 Continue;
860 end;
862 if not ByteBool(gCollideMap[Y, X] and MARK_BLOCKED) then
863 begin
864 if ByteBool(gCollideMap[Y, X] and MARK_LIFTUP) then
865 begin // Ëèôò ââåðõ
866 if VelY > -4-Random(3) then
867 VelY := VelY - 0.8;
868 if Abs(VelX) > 0.1 then
869 VelX := VelX - VelX/10.0;
870 VelX := VelX + (Random-Random)*0.2;
871 AccelY := 0.15;
872 end;
873 if ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT) then
874 begin // Ïîòîê âëåâî
875 if VelX > -8-Random(3) then
876 VelX := VelX - 0.8;
877 AccelY := 0.15;
878 end;
879 if ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT) then
880 begin // Ïîòîê âïðàâî
881 if VelX < 8+Random(3) then
882 VelX := VelX + 0.8;
883 AccelY := 0.15;
884 end;
885 end;
887 dX := Round(VelX);
888 dY := Round(VelY);
890 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
891 if (State <> STATE_STICK) and
892 (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
893 (not ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)) and
894 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) then
895 begin // Âèñèò â âîçäóõå - êàïàåò
896 VelY := 0.8;
897 AccelY := 0.5;
898 State := STATE_NORMAL;
899 end;
901 if dX <> 0 then
902 begin
903 if dX > 0 then
904 s := 1
905 else
906 s := -1;
908 dX := Abs(dX);
910 for b := 1 to dX do
911 begin
912 if (X+s >= w) or (X+s <= 0) then
913 begin
914 State := STATE_FREE;
915 Break;
916 end;
918 c := gCollideMap[Y, X+s];
920 if ByteBool(c and MARK_BLOCKED) then
921 begin // Ñòåíà/äâåðü
922 VelX := 0;
923 VelY := 0;
924 AccelX := 0;
925 AccelY := 0;
926 State := STATE_STICK;
927 Break;
928 end;
930 X := X+s;
931 end;
932 end;
934 if dY <> 0 then
935 begin
936 if dY > 0 then
937 s := 1
938 else
939 s := -1;
941 dY := Abs(dY);
943 for b := 1 to dY do
944 begin
945 if (Y+s >= h) or (Y+s <= 0) then
946 begin
947 State := STATE_FREE;
948 Break;
949 end;
951 c := gCollideMap[Y+s, X];
953 if ByteBool(c and MARK_BLOCKED) then
954 begin // Ñòåíà/äâåðü
955 VelX := 0;
956 VelY := 0;
957 AccelX := 0;
958 AccelY := 0;
959 if (s > 0) and (State <> STATE_STICK) then
960 State := STATE_NORMAL
961 else
962 State := STATE_STICK;
963 Break;
964 end;
966 Y := Y+s;
967 end;
968 end;
969 end // if gAdvBlood
970 else
971 begin
972 dX := Round(VelX);
973 dY := Round(VelY);
975 if (X+dX >= w) or (Y+dY >= h) or
976 (X+dX <= 0) or (Y+dY <= 0) or
977 ByteBool(gCollideMap[Y+dY, X+dX] and MARK_BLOCKED) then
978 begin // Ñòåíà/äâåðü/ãðàíèöà
979 State := STATE_FREE;
980 VelX := 0;
981 VelY := 0;
982 end
983 else
984 begin
985 Y := Y + dY;
986 X := X + dX;
987 end;
988 end;
990 VelX := VelX + AccelX;
991 VelY := VelY + AccelY;
993 // Êðîâü ðàñòâîðÿåòñÿ â æèäêîñòè:
994 if ByteBool(gCollideMap[Y, X] and MARK_LIQUID) then
995 begin
996 Inc(Time);
998 Alpha := 255 - Trunc((255.0 * Time) / LiveTime);
999 end;
1000 end;
1002 PARTICLE_SPARK:
1003 begin
1004 dX := Round(VelX);
1005 dY := Round(VelY);
1007 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) and
1008 (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
1009 (not ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)) and
1010 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) then
1011 begin // Âèñèò â âîçäóõå
1012 VelY := 0.8;
1013 AccelY := 0.5;
1014 end;
1016 if dX <> 0 then
1017 begin
1018 if dX > 0 then
1019 s := 1
1020 else
1021 s := -1;
1023 dX := Abs(dX);
1025 for b := 1 to dX do
1026 begin
1027 if (X+s >= w) or (X+s <= 0) then
1028 begin
1029 State := STATE_FREE;
1030 Break;
1031 end;
1033 c := gCollideMap[Y, X+s];
1035 if ByteBool(c and MARK_BLOCKED) then
1036 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1037 VelX := 0;
1038 AccelX := 0;
1039 Break;
1040 end
1041 else // Ïóñòî:
1042 if c = MARK_FREE then
1043 X := X + s
1044 else // Îñòàëüíîå:
1045 begin
1046 State := STATE_FREE;
1047 Break;
1048 end;
1049 end;
1050 end;
1052 if dY <> 0 then
1053 begin
1054 if dY > 0 then
1055 s := 1
1056 else
1057 s := -1;
1059 dY := Abs(dY);
1061 for b := 1 to dY do
1062 begin
1063 if (Y+s >= h) or (Y+s <= 0) then
1064 begin
1065 State := STATE_FREE;
1066 Break;
1067 end;
1069 c := gCollideMap[Y+s, X];
1071 if ByteBool(c and MARK_BLOCKED) then
1072 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1073 if s < 0 then
1074 begin
1075 VelY := -VelY;
1076 AccelY := Abs(AccelY);
1077 end
1078 else // Èëè íå ïàäàåò
1079 begin
1080 VelX := 0;
1081 AccelX := 0;
1082 VelY := 0;
1083 AccelY := 0.8;
1084 end;
1086 Break;
1087 end
1088 else // Ïóñòî:
1089 if c = MARK_FREE then
1090 Y := Y + s
1091 else // Îñàëüíîå:
1092 begin
1093 State := STATE_FREE;
1094 Break;
1095 end;
1096 end;
1097 end;
1099 if VelX <> 0.0 then
1100 VelX := VelX + AccelX;
1101 if VelY <> 0.0 then
1102 begin
1103 if AccelY < 10 then
1104 AccelY := AccelY + 0.08;
1105 VelY := VelY + AccelY;
1106 end;
1108 Time := Time + 1;
1109 end;
1111 PARTICLE_WATER:
1112 begin
1113 if (State = STATE_STICK) and (Random(30) = 15) then
1114 begin // Ñòåêàåò/îòëèïàåò
1115 VelY := 0.5;
1116 AccelY := 0.15;
1117 if (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
1118 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)) then
1119 State := STATE_NORMAL;
1120 Continue;
1121 end;
1123 if not ByteBool(gCollideMap[Y, X] and MARK_BLOCKED) then
1124 begin
1125 if ByteBool(gCollideMap[Y, X] and MARK_LIFTUP) then
1126 begin // Ëèôò ââåðõ
1127 if VelY > -4-Random(3) then
1128 VelY := VelY - 0.8;
1129 if Abs(VelX) > 0.1 then
1130 VelX := VelX - VelX/10.0;
1131 VelX := VelX + (Random-Random)*0.2;
1132 AccelY := 0.15;
1133 end;
1134 if ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT) then
1135 begin // Ïîòîê âëåâî
1136 if VelX > -8-Random(3) then
1137 VelX := VelX - 0.8;
1138 AccelY := 0.15;
1139 end;
1140 if ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT) then
1141 begin // Ïîòîê âïðàâî
1142 if VelX < 8+Random(3) then
1143 VelX := VelX + 0.8;
1144 AccelY := 0.15;
1145 end;
1146 end;
1148 dX := Round(VelX);
1149 dY := Round(VelY);
1151 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
1152 if (State <> STATE_STICK) and
1153 (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
1154 (not ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)) and
1155 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) then
1156 begin // Âèñèò â âîçäóõå - êàïàåò
1157 VelY := 0.8;
1158 AccelY := 0.5;
1159 State := STATE_NORMAL;
1160 end;
1162 if dX <> 0 then
1163 begin
1164 if dX > 0 then
1165 s := 1
1166 else
1167 s := -1;
1169 for b := 1 to Abs(dX) do
1170 begin
1171 if (X+s >= w) or (X+s <= 0) then
1172 begin // Ñáîêó ãðàíèöà
1173 State := STATE_FREE;
1174 Break;
1175 end;
1177 c := gCollideMap[Y, X+s];
1179 if ByteBool(c and MARK_LIQUID) and (dY > 0) then
1180 begin // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1181 State := STATE_FREE;
1182 Break;
1183 end;
1185 if ByteBool(c and MARK_BLOCKED) then
1186 begin // Ñòåíà/äâåðü
1187 VelX := 0;
1188 VelY := 0;
1189 AccelX := 0;
1190 AccelY := 0;
1191 State := STATE_STICK;
1192 Break;
1193 end;
1195 X := X+s;
1196 end;
1197 end;
1199 if dY <> 0 then
1200 begin
1201 if dY > 0 then
1202 s := 1
1203 else
1204 s := -1;
1206 for b := 1 to Abs(dY) do
1207 begin
1208 if (Y+s >= h) or (Y+s <= 0) then
1209 begin // Ñíèçó/ñâåðõó ãðàíèöà
1210 State := STATE_FREE;
1211 Break;
1212 end;
1214 c := gCollideMap[Y+s, X];
1216 if ByteBool(c and MARK_LIQUID) and (dY > 0) then
1217 begin // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1218 State := STATE_FREE;
1219 Break;
1220 end;
1222 if ByteBool(c and MARK_BLOCKED) then
1223 begin // Ñòåíà/äâåðü
1224 VelX := 0;
1225 VelY := 0;
1226 AccelX := 0;
1227 AccelY := 0;
1228 if (s > 0) and (State <> STATE_STICK) then
1229 State := STATE_NORMAL
1230 else
1231 State := STATE_STICK;
1232 Break;
1233 end;
1235 Y := Y+s;
1236 end;
1237 end;
1239 VelX := VelX + AccelX;
1240 VelY := VelY + AccelY;
1242 Time := Time + 1;
1243 end;
1245 PARTICLE_BUBBLES:
1246 begin
1247 dY := Round(VelY);
1249 if dY <> 0 then
1250 begin
1251 if dY > 0 then
1252 s := 1
1253 else
1254 s := -1;
1256 for b := 1 to Abs(dY) do
1257 begin
1258 if (Y+s >= h) or (Y+s <= 0) then
1259 begin
1260 State := STATE_FREE;
1261 Break;
1262 end;
1264 if not ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID) then
1265 begin // Óæå íå æèäêîñòü
1266 State := STATE_FREE;
1267 Break;
1268 end;
1270 Y := Y+s;
1271 end;
1272 end;
1274 if VelY > -4 then
1275 VelY := VelY + AccelY;
1277 Time := Time + 1;
1278 end;
1279 end; // case
1281 CorrectOffsets(a);
1282 end;
1283 end; // Particles <> nil
1284 {$ENDIF}
1286 if OnceAnims <> nil then
1287 begin
1288 for a := 0 to High(OnceAnims) do
1289 if OnceAnims[a].Animation <> nil then
1290 begin
1291 case OnceAnims[a].AnimType of
1292 ONCEANIM_SMOKE:
1293 begin
1294 if Random(3) = 0 then
1295 OnceAnims[a].X := OnceAnims[a].X-1+Random(3);
1296 if Random(2) = 0 then
1297 OnceAnims[a].Y := OnceAnims[a].Y-Random(2);
1298 end;
1299 end;
1301 if OnceAnims[a].Animation.Played then
1302 begin
1303 OnceAnims[a].Animation.Free();
1304 OnceAnims[a].Animation := nil;
1305 end
1306 else
1307 OnceAnims[a].Animation.Update();
1308 end;
1309 end;
1310 end;
1312 procedure g_GFX_Draw();
1313 var
1314 a, len: Integer;
1315 begin
1316 if Particles <> nil then
1317 begin
1318 glDisable(GL_TEXTURE_2D);
1319 glPointSize(2);
1321 glEnable(GL_BLEND);
1322 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1324 glBegin(GL_POINTS);
1326 len := High(Particles);
1328 for a := 0 to len do
1329 with Particles[a] do
1330 if (State <> STATE_FREE) and (X >= sX) and (Y >= sY) and
1331 (X <= sX+sWidth) and (sY <= sY+sHeight) then
1332 begin
1333 glColor4ub(Red, Green, Blue, Alpha);
1334 glVertex2i(X + offsetX, Y + offsetY);
1335 end;
1337 glEnd();
1339 glDisable(GL_BLEND);
1340 end;
1342 if OnceAnims <> nil then
1343 for a := 0 to High(OnceAnims) do
1344 if OnceAnims[a].Animation <> nil then
1345 with OnceAnims[a] do
1346 Animation.Draw(X, Y, M_NONE);
1347 end;
1349 end.