DEADSOFTWARE

map ray tracer now using grid instead of tree
[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 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 implementation
64 uses
65 g_map, g_basic, Math, e_graphics, GL, GLExt,
66 g_options, g_console, SysUtils, g_triggers, MAPDEF,
67 g_game, g_language, g_net;
69 type
70 PParticle = ^TParticle;
71 TParticle = record
72 X, Y: Integer;
73 VelX, VelY: Single;
74 AccelX, AccelY: Single;
75 Red, Green, Blue: Byte;
76 Alpha: Byte;
77 Time, LiveTime: Word;
78 State: Byte;
79 ParticleType: Byte;
80 offsetX, offsetY: ShortInt;
81 end;
83 TOnceAnim = record
84 AnimType: Byte;
85 X, Y: Integer;
86 Animation: TAnimation;
87 end;
89 const
90 PARTICLE_BLOOD = 0;
91 PARTICLE_SPARK = 1;
92 PARTICLE_BUBBLES = 2;
93 PARTICLE_WATER = 3;
94 STATE_FREE = 0;
95 STATE_NORMAL = 1;
96 STATE_STICK = 2;
98 var
99 Particles: array of TParticle;
100 OnceAnims: array of TOnceAnim;
101 MaxParticles: Integer;
102 CurrentParticle: Integer;
105 function isBlockedAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
106 begin
107 result := g_Map_CollidePanel(x, y, w, h, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
108 end;
111 // ???
112 function isWallAt (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_STEP));
115 end;
118 function isLiftUpAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
119 begin
120 result := g_Map_CollidePanel(x, y, w, h, PANEL_LIFTUP);
121 end;
123 function isLiftDownAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
124 begin
125 result := g_Map_CollidePanel(x, y, w, h, PANEL_LIFTDOWN);
126 end;
128 function isLiftLeftAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
129 begin
130 result := g_Map_CollidePanel(x, y, w, h, PANEL_LIFTLEFT);
131 end;
133 function isLiftRightAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
134 begin
135 result := g_Map_CollidePanel(x, y, w, h, PANEL_LIFTRIGHT);
136 end;
139 function isLiquidAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
140 begin
141 result := g_Map_CollidePanel(x, y, w, h, (PANEL_WATER or PANEL_ACID1 or PANEL_ACID2));
142 end;
145 function isAnythingAt (x, y: Integer; w: Integer=1; h: Integer=1): Boolean; inline;
146 begin
147 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));
148 end;
151 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
152 procedure g_Mark(x, y, Width, Height: Integer; t: Byte; st: Boolean);
153 var
154 yy, y2, xx, x2: Integer;
155 begin
156 if x < 0 then
157 begin
158 Width := Width + x;
159 x := 0;
160 end;
162 if Width < 0 then
163 Exit;
165 if y < 0 then
166 begin
167 Height := Height + y;
168 y := 0;
169 end;
171 if Height < 0 then
172 Exit;
174 if x > gMapInfo.Width then
175 Exit;
176 if y > gMapInfo.Height then
177 Exit;
179 y2 := y + Height - 1;
180 if y2 > gMapInfo.Height then
181 y2 := gMapInfo.Height;
183 x2 := x + Width - 1;
184 if x2 > gMapInfo.Width then
185 x2 := gMapInfo.Width;
187 if st then
188 begin // Óñòàíîâèòü ïðèçíàê
189 for yy := y to y2 do
190 for xx := x to x2 do
191 gCollideMap[yy][xx] := gCollideMap[yy][xx] or t;
192 end
193 else
194 begin // Óáðàòü ïðèçíàê
195 t := not t;
196 for yy := y to y2 do
197 for xx := x to x2 do
198 gCollideMap[yy][xx] := gCollideMap[yy][xx] and t;
199 end;
200 end;
201 {$ENDIF}
204 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
205 procedure CreateCollideMap();
206 var
207 a: Integer;
208 begin
209 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
210 SetLength(gCollideMap, gMapInfo.Height+1);
211 for a := 0 to High(gCollideMap) do
212 SetLength(gCollideMap[a], gMapInfo.Width+1);
214 if gWater <> nil then
215 begin
216 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 2/6', 0, True);
217 for a := 0 to High(gWater) do
218 with gWater[a] do
219 g_Mark(X, Y, Width, Height, MARK_WATER, True);
220 end;
222 if gAcid1 <> nil then
223 begin
224 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 3/6', 0, True);
225 for a := 0 to High(gAcid1) do
226 with gAcid1[a] do
227 g_Mark(X, Y, Width, Height, MARK_ACID, True);
228 end;
230 if gAcid2 <> nil then
231 begin
232 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 4/6', 0, True);
233 for a := 0 to High(gAcid2) do
234 with gAcid2[a] do
235 g_Mark(X, Y, Width, Height, MARK_ACID, True);
236 end;
238 if gLifts <> nil then
239 begin
240 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 5/6', 0, True);
241 for a := 0 to High(gLifts) do
242 with gLifts[a] do
243 begin
244 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
246 if LiftType = 0 then
247 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
248 else if LiftType = 1 then
249 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
250 else if LiftType = 2 then
251 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
252 else if LiftType = 3 then
253 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True)
254 end;
255 end;
257 if gWalls <> nil then
258 begin
259 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 6/6', 0, True);
260 for a := 0 to High(gWalls) do
261 begin
262 if gWalls[a].Door then
263 begin
264 // Çàêðûòàÿ äâåðü:
265 if gWalls[a].Enabled then
266 with gWalls[a] do
267 g_Mark(X, Y, Width, Height, MARK_DOOR, True)
268 else // Îòêðûòàÿ äâåðü:
269 if gWalls[a].Enabled then
270 with gWalls[a] do
271 g_Mark(X, Y, Width, Height, MARK_DOOR, False);
272 end
273 else // Ñòåíà
274 with gWalls[a] do
275 g_Mark(X, Y, Width, Height, MARK_WALL, True);
276 end;
277 end;
278 end;
279 {$ENDIF}
282 procedure g_GFX_Init();
283 begin
284 //CreateCollideMap();
285 end;
288 procedure g_GFX_Free();
289 var
290 a: Integer;
291 begin
292 Particles := nil;
293 SetLength(Particles, MaxParticles);
294 for a := 0 to High(Particles) do Particles[a].State := STATE_FREE;
295 CurrentParticle := 0;
297 if OnceAnims <> nil then
298 begin
299 for a := 0 to High(OnceAnims) do
300 OnceAnims[a].Animation.Free();
302 OnceAnims := nil;
303 end;
304 end;
307 procedure CorrectOffsets(id: Integer);
308 var
309 part: PParticle;
310 begin
311 part := @Particles[id];
312 part.offsetX := 0;
313 part.offsetY := 0;
314 // check for upper wall
315 if isBlockedAt(part.X, part.Y-1) then part.offsetY := 1;
316 // check for left wall
317 if isBlockedAt(part.X-1, part.Y) then part.offsetX := 1;
318 end;
321 procedure g_GFX_SparkVel (fX, fY: Integer; Count: Word; VX, VY: Integer; DevX, DevY: Byte);
322 var
323 a: Integer;
324 DevX1, DevX2,
325 DevY1, DevY2: Byte;
326 l: Integer;
327 begin
328 l := Length(Particles);
329 if l = 0 then exit;
330 if Count > l then Count := l;
332 DevX1 := DevX div 2;
333 DevX2 := DevX + 1;
334 DevY1 := DevY div 2;
335 DevY2 := DevY + 1;
337 for a := 1 to Count do
338 begin
339 with Particles[CurrentParticle] do
340 begin
341 X := fX-DevX1+Random(DevX2);
342 Y := fY-DevY1+Random(DevY2);
344 VelX := VX + (Random-Random)*3;
345 VelY := VY + (Random-Random)*3;
347 if VelY > -4 then
348 if VelY-4 < -4 then
349 VelY := -4
350 else
351 VelY := VelY-4;
353 AccelX := -Sign(VelX)*Random/100;
354 AccelY := 0.8;
356 Red := 255;
357 Green := 100+Random(155);
358 Blue := 64;
359 Alpha := 255;
361 State := STATE_NORMAL;
362 Time := 0;
363 LiveTime := 30+Random(60);
364 ParticleType := PARTICLE_SPARK;
366 CorrectOffsets(CurrentParticle);
367 end;
369 if CurrentParticle+2 > MaxParticles then
370 CurrentParticle := 0
371 else
372 CurrentParticle := CurrentParticle+1;
373 end;
374 end;
377 procedure g_GFX_Blood(fX, fY: Integer; Count: Word; vx, vy: Integer;
378 DevX, DevY: Word; CR, CG, CB: Byte; Kind: Byte = BLOOD_NORMAL);
379 var
380 a: Integer;
381 DevX1, DevX2,
382 DevY1, DevY2: Word;
383 l: Integer;
384 CRnd: Byte;
385 CC: SmallInt;
386 begin
387 if Kind = BLOOD_SPARKS then
388 begin
389 g_GFX_SparkVel(fX, fY, 2 + Random(2), -VX div 2, -VY div 2, DevX, DevY);
390 Exit;
391 end;
392 l := Length(Particles);
393 if l = 0 then
394 Exit;
395 if Count > l then
396 Count := l;
398 DevX1 := DevX div 2;
399 DevX2 := DevX + 1;
400 DevY1 := DevY div 2;
401 DevY2 := DevY + 1;
403 for a := 1 to Count do
404 begin
405 with Particles[CurrentParticle] do
406 begin
407 X := fX - DevX1 + Random(DevX2);
408 Y := fY - DevY1 + Random(DevY2);
411 if (X < 0) or (X > gMapInfo.Width-1) or
412 (Y < 0) or (Y > gMapInfo.Height-1) or
413 ByteBool(gCollideMap[Y, X] and MARK_WALL) then
414 Continue;
416 if isWallAt(X, Y) then continue;
418 VelX := vx + (Random-Random)*3;
419 VelY := vy + (Random-Random)*3;
421 if VelY > -4 then
422 if VelY-4 < -4 then
423 VelY := -4
424 else
425 VelY := VelY-4;
427 AccelX := -Sign(VelX)*Random/100;
428 AccelY := 0.8;
430 CRnd := 20*Random(6);
431 if CR > 0 then
432 begin
433 CC := CR + CRnd - 50;
434 if CC < 0 then CC := 0;
435 if CC > 255 then CC := 255;
436 Red := CC;
437 end else
438 Red := 0;
439 if CG > 0 then
440 begin
441 CC := CG + CRnd - 50;
442 if CC < 0 then CC := 0;
443 if CC > 255 then CC := 255;
444 Green := CC;
445 end else
446 Green := 0;
447 if CB > 0 then
448 begin
449 CC := CB + CRnd - 50;
450 if CC < 0 then CC := 0;
451 if CC > 255 then CC := 255;
452 Blue := CC;
453 end else
454 Blue := 0;
456 Alpha := 255;
458 State := STATE_NORMAL;
459 Time := 0;
460 LiveTime := 120+Random(40);
461 ParticleType := PARTICLE_BLOOD;
463 CorrectOffsets(CurrentParticle);
464 end;
466 if CurrentParticle >= MaxParticles-1 then
467 CurrentParticle := 0
468 else
469 CurrentParticle := CurrentParticle+1;
470 end;
471 end;
474 procedure g_GFX_Spark(fX, fY: Integer; Count: Word; Angle: SmallInt; DevX, DevY: Byte);
475 var
476 a: Integer;
477 b: Single;
478 DevX1, DevX2,
479 DevY1, DevY2: Byte;
480 BaseVelX, BaseVelY: Single;
481 l: Integer;
482 begin
483 l := Length(Particles);
484 if l = 0 then
485 Exit;
486 if Count > l then
487 Count := l;
489 Angle := 360 - Angle;
491 DevX1 := DevX div 2;
492 DevX2 := DevX + 1;
493 DevY1 := DevY div 2;
494 DevY2 := DevY + 1;
496 b := DegToRad(Angle);
497 BaseVelX := cos(b);
498 BaseVelY := 1.6*sin(b);
499 if Abs(BaseVelX) < 0.01 then
500 BaseVelX := 0.0;
501 if Abs(BaseVelY) < 0.01 then
502 BaseVelY := 0.0;
503 for a := 1 to Count do
504 begin
505 with Particles[CurrentParticle] do
506 begin
507 X := fX-DevX1+Random(DevX2);
508 Y := fY-DevY1+Random(DevY2);
510 VelX := BaseVelX*Random;
511 VelY := BaseVelY-Random;
512 AccelX := VelX/3.0;
513 AccelY := VelY/5.0;
515 Red := 255;
516 Green := 100+Random(155);
517 Blue := 64;
518 Alpha := 255;
520 State := STATE_NORMAL;
521 Time := 0;
522 LiveTime := 30+Random(60);
523 ParticleType := PARTICLE_SPARK;
525 CorrectOffsets(CurrentParticle);
526 end;
528 if CurrentParticle+2 > MaxParticles then
529 CurrentParticle := 0
530 else
531 CurrentParticle := CurrentParticle+1;
532 end;
533 end;
535 procedure g_GFX_Water(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DevX, DevY, Color: Byte);
536 var
537 a: Integer;
538 DevX1, DevX2,
539 DevY1, DevY2: Byte;
540 l: Integer;
541 begin
542 l := Length(Particles);
543 if l = 0 then
544 Exit;
545 if Count > l then
546 Count := l;
548 if Abs(fVelX) < 3.0 then
549 fVelX := 3.0 - 6.0*Random;
551 DevX1 := DevX div 2;
552 DevX2 := DevX + 1;
553 DevY1 := DevY div 2;
554 DevY2 := DevY + 1;
556 for a := 1 to Count do
557 begin
558 with Particles[CurrentParticle] do
559 begin
560 X := fX-DevX1+Random(DevX2);
561 Y := fY-DevY1+Random(DevY2);
563 if Abs(fVelX) < 0.5 then
564 VelX := 1.0 - 2.0*Random
565 else
566 VelX := fVelX*Random;
567 if Random(10) < 7 then
568 VelX := -VelX;
569 VelY := fVelY*Random;
570 AccelX := 0.0;
571 AccelY := 0.8;
573 case Color of
574 1: // Êðàñíûé
575 begin
576 Red := 155 + Random(9)*10;
577 Green := Trunc(150*Random);
578 Blue := Green;
579 end;
580 2: // Çåëåíûé
581 begin
582 Red := Trunc(150*Random);
583 Green := 175 + Random(9)*10;
584 Blue := Red;
585 end;
586 3: // Ñèíèé
587 begin
588 Red := Trunc(200*Random);
589 Green := Red;
590 Blue := 175 + Random(9)*10;
591 end;
592 else // Ñåðûé
593 begin
594 Red := 90 + Random(12)*10;
595 Green := Red;
596 Blue := Red;
597 end;
598 end;
600 Alpha := 255;
602 State := STATE_NORMAL;
603 Time := 0;
604 LiveTime := 60+Random(60);
605 ParticleType := PARTICLE_WATER;
607 CorrectOffsets(CurrentParticle);
608 end;
610 if CurrentParticle+2 > MaxParticles then
611 CurrentParticle := 0
612 else
613 CurrentParticle := CurrentParticle+1;
614 end;
615 end;
617 procedure g_GFX_SimpleWater(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DefColor, CR, CG, CB: Byte);
618 var
619 a: Integer;
620 l: Integer;
621 begin
622 l := Length(Particles);
623 if l = 0 then
624 Exit;
625 if Count > l then
626 Count := l;
628 for a := 1 to Count do
629 begin
630 with Particles[CurrentParticle] do
631 begin
632 X := fX;
633 Y := fY;
635 VelX := fVelX;
636 VelY := fVelY;
637 AccelX := 0.0;
638 AccelY := 0.8;
640 case DefColor of
641 1: // Êðàñíûé
642 begin
643 Red := 155 + Random(9)*10;
644 Green := Trunc(150*Random);
645 Blue := Green;
646 end;
647 2: // Çåëåíûé
648 begin
649 Red := Trunc(150*Random);
650 Green := 175 + Random(9)*10;
651 Blue := Red;
652 end;
653 3: // Ñèíèé
654 begin
655 Red := Trunc(200*Random);
656 Green := Red;
657 Blue := 175 + Random(9)*10;
658 end;
659 4: // Ñâîé öâåò, ñâåòëåå
660 begin
661 Red := 20 + Random(19)*10;
662 Green := Red;
663 Blue := Red;
664 Red := Min(Red + CR, 255);
665 Green := Min(Green + CG, 255);
666 Blue := Min(Blue + CB, 255);
667 end;
668 5: // Ñâîé öâåò, òåìíåå
669 begin
670 Red := 20 + Random(19)*10;
671 Green := Red;
672 Blue := Red;
673 Red := Max(CR - Red, 0);
674 Green := Max(CG - Green, 0);
675 Blue := Max(CB - Blue, 0);
676 end;
677 else // Ñåðûé
678 begin
679 Red := 90 + Random(12)*10;
680 Green := Red;
681 Blue := Red;
682 end;
683 end;
685 Alpha := 255;
687 State := STATE_NORMAL;
688 Time := 0;
689 LiveTime := 60+Random(60);
690 ParticleType := PARTICLE_WATER;
692 CorrectOffsets(CurrentParticle);
693 end;
695 if CurrentParticle+2 > MaxParticles then
696 CurrentParticle := 0
697 else
698 CurrentParticle := CurrentParticle+1;
699 end;
700 end;
702 procedure g_GFX_Bubbles(fX, fY: Integer; Count: Word; DevX, DevY: Byte);
703 var
704 a: Integer;
705 DevX1, DevX2,
706 DevY1, DevY2: Byte;
707 l: Integer;
708 begin
709 l := Length(Particles);
710 if l = 0 then
711 Exit;
712 if Count > l then
713 Count := l;
715 DevX1 := DevX div 2;
716 DevX2 := DevX + 1;
717 DevY1 := DevY div 2;
718 DevY2 := DevY + 1;
720 for a := 1 to Count do
721 begin
722 with Particles[CurrentParticle] do
723 begin
724 X := fX-DevX1+Random(DevX2);
725 Y := fY-DevY1+Random(DevY2);
727 if (X >= gMapInfo.Width) or (X <= 0) or
728 (Y >= gMapInfo.Height) or (Y <= 0) then
729 Continue;
731 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
732 Continue;
734 VelX := 0;
735 VelY := -1-Random;
736 AccelX := 0;
737 AccelY := VelY/10;
739 Red := 255;
740 Green := 255;
741 Blue := 255;
742 Alpha := 255;
744 State := STATE_NORMAL;
745 Time := 0;
746 LiveTime := 65535;
747 ParticleType := PARTICLE_BUBBLES;
749 CorrectOffsets(CurrentParticle);
750 end;
752 if CurrentParticle+2 > MaxParticles then
753 CurrentParticle := 0
754 else
755 CurrentParticle := CurrentParticle+1;
756 end;
757 end;
759 procedure g_GFX_SetMax(Count: Integer);
760 var
761 a: Integer;
762 begin
763 if Count > 50000 then Count := 50000;
764 if (Count < 1) then Count := 1;
766 SetLength(Particles, Count);
767 for a := 0 to High(Particles) do Particles[a].State := STATE_FREE;
768 MaxParticles := Count;
769 //if CurrentParticle >= Count then
770 CurrentParticle := 0;
771 end;
773 function g_GFX_GetMax(): Integer;
774 begin
775 Result := MaxParticles;
776 end;
778 function FindOnceAnim: DWORD;
779 var
780 i: Integer;
781 begin
782 if OnceAnims <> nil then
783 for i := 0 to High(OnceAnims) do
784 if OnceAnims[i].Animation = nil then
785 begin
786 Result := i;
787 Exit;
788 end;
790 if OnceAnims = nil then
791 begin
792 SetLength(OnceAnims, 16);
793 Result := 0;
794 end
795 else
796 begin
797 Result := High(OnceAnims) + 1;
798 SetLength(OnceAnims, Length(OnceAnims) + 16);
799 end;
800 end;
802 procedure g_GFX_OnceAnim(X, Y: Integer; Anim: TAnimation; AnimType: Byte = 0);
803 var
804 find_id: DWORD;
805 begin
806 if Anim = nil then
807 Exit;
809 find_id := FindOnceAnim();
811 OnceAnims[find_id].AnimType := AnimType;
812 OnceAnims[find_id].Animation := TAnimation.Create(Anim.FramesID, Anim.Loop, Anim.Speed);
813 OnceAnims[find_id].Animation.Blending := Anim.Blending;
814 OnceAnims[find_id].Animation.Alpha := Anim.Alpha;
815 OnceAnims[find_id].X := X;
816 OnceAnims[find_id].Y := Y;
817 end;
819 procedure g_GFX_Update();
820 var
821 a: Integer;
822 w, h: Integer;
823 dX, dY: SmallInt;
824 b, len: Integer;
825 s: ShortInt;
826 //c: Byte;
827 begin
828 if Particles <> nil then
829 begin
830 w := gMapInfo.Width;
831 h := gMapInfo.Height;
833 len := High(Particles);
835 for a := 0 to len do
836 if Particles[a].State <> 0 then
837 with Particles[a] do
838 begin
839 if Time = LiveTime then
840 State := STATE_FREE;
841 if (X+1 >= w) or (Y+1 >= h) or (X <= 0) or (Y <= 0) then
842 State := STATE_FREE;
843 if State = STATE_FREE then
844 Continue;
846 case ParticleType of
847 PARTICLE_BLOOD:
848 begin
849 if gAdvBlood then
850 begin
851 if (State = STATE_STICK) then
853 if (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
854 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) and
855 (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
856 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED))
857 then
859 if (not isBlockedAt(X, Y-1)) and
860 (not isBlockedAt(X, Y+1)) and
861 (not isBlockedAt(X-1, Y)) and
862 (not isBlockedAt(X+1, Y))
863 then
864 begin // Îòëèïëà - êàïàåò
865 VelY := 0.5;
866 AccelY := 0.15;
867 State := STATE_NORMAL;
868 end
869 else
870 if Random(200) = 100 then
871 begin // Ïðèëåïëåíà - íî âîçìîæíî ñòåêàåò
872 VelY := 0.5;
873 AccelY := 0.15;
874 Continue;
875 end;
877 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
878 begin
879 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
880 begin // Ëèôò ââåðõ
881 if VelY > -4-Random(3) then
882 VelY := VelY - 0.8;
883 if Abs(VelX) > 0.1 then
884 VelX := VelX - VelX/10.0;
885 VelX := VelX + (Random-Random)*0.2;
886 AccelY := 0.15;
887 end;
888 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
889 begin // Ïîòîê âëåâî
890 if VelX > -8-Random(3) then
891 VelX := VelX - 0.8;
892 AccelY := 0.15;
893 end;
894 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
895 begin // Ïîòîê âïðàâî
896 if VelX < 8+Random(3) then
897 VelX := VelX + 0.8;
898 AccelY := 0.15;
899 end;
900 end;
902 dX := Round(VelX);
903 dY := Round(VelY);
905 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
906 if (State <> STATE_STICK) and
907 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
908 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
909 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
910 begin // Âèñèò â âîçäóõå - êàïàåò
911 VelY := 0.8;
912 AccelY := 0.5;
913 State := STATE_NORMAL;
914 end;
916 if dX <> 0 then
917 begin
918 if dX > 0 then
919 s := 1
920 else
921 s := -1;
923 dX := Abs(dX);
925 for b := 1 to dX do
926 begin
927 if (X+s >= w) or (X+s <= 0) then
928 begin
929 State := STATE_FREE;
930 Break;
931 end;
933 //c := gCollideMap[Y, X+s];
935 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
936 begin // Ñòåíà/äâåðü
937 VelX := 0;
938 VelY := 0;
939 AccelX := 0;
940 AccelY := 0;
941 State := STATE_STICK;
942 Break;
943 end;
945 X := X+s;
946 end;
947 end;
949 if dY <> 0 then
950 begin
951 if dY > 0 then
952 s := 1
953 else
954 s := -1;
956 dY := Abs(dY);
958 for b := 1 to dY do
959 begin
960 if (Y+s >= h) or (Y+s <= 0) then
961 begin
962 State := STATE_FREE;
963 Break;
964 end;
966 //c := gCollideMap[Y+s, X];
968 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
969 begin // Ñòåíà/äâåðü
970 VelX := 0;
971 VelY := 0;
972 AccelX := 0;
973 AccelY := 0;
974 if (s > 0) and (State <> STATE_STICK) then
975 State := STATE_NORMAL
976 else
977 State := STATE_STICK;
978 Break;
979 end;
981 Y := Y+s;
982 end;
983 end;
984 end // if gAdvBlood
985 else
986 begin
987 dX := Round(VelX);
988 dY := Round(VelY);
990 if (X+dX >= w) or (Y+dY >= h) or
991 (X+dX <= 0) or (Y+dY <= 0) or
992 isBlockedAt(X+dX, Y+dY) {ByteBool(gCollideMap[Y+dY, X+dX] and MARK_BLOCKED)} then
993 begin // Ñòåíà/äâåðü/ãðàíèöà
994 State := STATE_FREE;
995 VelX := 0;
996 VelY := 0;
997 end
998 else
999 begin
1000 Y := Y + dY;
1001 X := X + dX;
1002 end;
1003 end;
1005 VelX := VelX + AccelX;
1006 VelY := VelY + AccelY;
1008 // Êðîâü ðàñòâîðÿåòñÿ â æèäêîñòè:
1009 if isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1010 begin
1011 Inc(Time);
1013 Alpha := 255 - Trunc((255.0 * Time) / LiveTime);
1014 end;
1015 end;
1017 PARTICLE_SPARK:
1018 begin
1019 dX := Round(VelX);
1020 dY := Round(VelY);
1022 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) and
1023 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
1024 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
1025 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
1026 begin // Âèñèò â âîçäóõå
1027 VelY := 0.8;
1028 AccelY := 0.5;
1029 end;
1031 if dX <> 0 then
1032 begin
1033 if dX > 0 then
1034 s := 1
1035 else
1036 s := -1;
1038 dX := Abs(dX);
1040 for b := 1 to dX do
1041 begin
1042 if (X+s >= w) or (X+s <= 0) then
1043 begin
1044 State := STATE_FREE;
1045 Break;
1046 end;
1048 //c := gCollideMap[Y, X+s];
1050 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
1051 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1052 VelX := 0;
1053 AccelX := 0;
1054 Break;
1055 end
1056 else // Ïóñòî:
1057 if not isAnythingAt(X+s, Y) {c = MARK_FREE} then
1058 X := X + s
1059 else // Îñòàëüíîå:
1060 begin
1061 State := STATE_FREE;
1062 Break;
1063 end;
1064 end;
1065 end;
1067 if dY <> 0 then
1068 begin
1069 if dY > 0 then
1070 s := 1
1071 else
1072 s := -1;
1074 dY := Abs(dY);
1076 for b := 1 to dY do
1077 begin
1078 if (Y+s >= h) or (Y+s <= 0) then
1079 begin
1080 State := STATE_FREE;
1081 Break;
1082 end;
1084 //c := gCollideMap[Y+s, X];
1086 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
1087 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1088 if s < 0 then
1089 begin
1090 VelY := -VelY;
1091 AccelY := Abs(AccelY);
1092 end
1093 else // Èëè íå ïàäàåò
1094 begin
1095 VelX := 0;
1096 AccelX := 0;
1097 VelY := 0;
1098 AccelY := 0.8;
1099 end;
1101 Break;
1102 end
1103 else // Ïóñòî:
1104 if not isAnythingAt(X, Y+s) {c = MARK_FREE} then
1105 Y := Y + s
1106 else // Îñàëüíîå:
1107 begin
1108 State := STATE_FREE;
1109 Break;
1110 end;
1111 end;
1112 end;
1114 if VelX <> 0.0 then
1115 VelX := VelX + AccelX;
1116 if VelY <> 0.0 then
1117 begin
1118 if AccelY < 10 then
1119 AccelY := AccelY + 0.08;
1120 VelY := VelY + AccelY;
1121 end;
1123 Time := Time + 1;
1124 end;
1126 PARTICLE_WATER:
1127 begin
1128 if (State = STATE_STICK) and (Random(30) = 15) then
1129 begin // Ñòåêàåò/îòëèïàåò
1130 VelY := 0.5;
1131 AccelY := 0.15;
1132 if (not isBlockedAt(X-1, Y) {ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)}) and
1133 (not isBlockedAt(X+1, Y) {ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)}) then
1134 State := STATE_NORMAL;
1135 Continue;
1136 end;
1138 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
1139 begin
1140 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
1141 begin // Ëèôò ââåðõ
1142 if VelY > -4-Random(3) then
1143 VelY := VelY - 0.8;
1144 if Abs(VelX) > 0.1 then
1145 VelX := VelX - VelX/10.0;
1146 VelX := VelX + (Random-Random)*0.2;
1147 AccelY := 0.15;
1148 end;
1149 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
1150 begin // Ïîòîê âëåâî
1151 if VelX > -8-Random(3) then
1152 VelX := VelX - 0.8;
1153 AccelY := 0.15;
1154 end;
1155 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
1156 begin // Ïîòîê âïðàâî
1157 if VelX < 8+Random(3) then
1158 VelX := VelX + 0.8;
1159 AccelY := 0.15;
1160 end;
1161 end;
1163 dX := Round(VelX);
1164 dY := Round(VelY);
1166 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
1167 if (State <> STATE_STICK) and
1168 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
1169 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
1170 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
1171 begin // Âèñèò â âîçäóõå - êàïàåò
1172 VelY := 0.8;
1173 AccelY := 0.5;
1174 State := STATE_NORMAL;
1175 end;
1177 if dX <> 0 then
1178 begin
1179 if dX > 0 then
1180 s := 1
1181 else
1182 s := -1;
1184 for b := 1 to Abs(dX) do
1185 begin
1186 if (X+s >= w) or (X+s <= 0) then
1187 begin // Ñáîêó ãðàíèöà
1188 State := STATE_FREE;
1189 Break;
1190 end;
1192 //c := gCollideMap[Y, X+s];
1194 if isLiquidAt(X+s, Y) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then
1195 begin // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1196 State := STATE_FREE;
1197 Break;
1198 end;
1200 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
1201 begin // Ñòåíà/äâåðü
1202 VelX := 0;
1203 VelY := 0;
1204 AccelX := 0;
1205 AccelY := 0;
1206 State := STATE_STICK;
1207 Break;
1208 end;
1210 X := X+s;
1211 end;
1212 end;
1214 if dY <> 0 then
1215 begin
1216 if dY > 0 then
1217 s := 1
1218 else
1219 s := -1;
1221 for b := 1 to Abs(dY) do
1222 begin
1223 if (Y+s >= h) or (Y+s <= 0) then
1224 begin // Ñíèçó/ñâåðõó ãðàíèöà
1225 State := STATE_FREE;
1226 Break;
1227 end;
1229 //c := gCollideMap[Y+s, X];
1231 if isLiquidAt(X, Y+s) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then
1232 begin // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1233 State := STATE_FREE;
1234 Break;
1235 end;
1237 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
1238 begin // Ñòåíà/äâåðü
1239 VelX := 0;
1240 VelY := 0;
1241 AccelX := 0;
1242 AccelY := 0;
1243 if (s > 0) and (State <> STATE_STICK) then
1244 State := STATE_NORMAL
1245 else
1246 State := STATE_STICK;
1247 Break;
1248 end;
1250 Y := Y+s;
1251 end;
1252 end;
1254 VelX := VelX + AccelX;
1255 VelY := VelY + AccelY;
1257 Time := Time + 1;
1258 end;
1260 PARTICLE_BUBBLES:
1261 begin
1262 dY := Round(VelY);
1264 if dY <> 0 then
1265 begin
1266 if dY > 0 then
1267 s := 1
1268 else
1269 s := -1;
1271 for b := 1 to Abs(dY) do
1272 begin
1273 if (Y+s >= h) or (Y+s <= 0) then
1274 begin
1275 State := STATE_FREE;
1276 Break;
1277 end;
1279 if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
1280 begin // Óæå íå æèäêîñòü
1281 State := STATE_FREE;
1282 Break;
1283 end;
1285 Y := Y+s;
1286 end;
1287 end;
1289 if VelY > -4 then
1290 VelY := VelY + AccelY;
1292 Time := Time + 1;
1293 end;
1294 end; // case
1296 CorrectOffsets(a);
1297 end;
1298 end; // Particles <> nil
1300 if OnceAnims <> nil then
1301 begin
1302 for a := 0 to High(OnceAnims) do
1303 if OnceAnims[a].Animation <> nil then
1304 begin
1305 case OnceAnims[a].AnimType of
1306 ONCEANIM_SMOKE:
1307 begin
1308 if Random(3) = 0 then
1309 OnceAnims[a].X := OnceAnims[a].X-1+Random(3);
1310 if Random(2) = 0 then
1311 OnceAnims[a].Y := OnceAnims[a].Y-Random(2);
1312 end;
1313 end;
1315 if OnceAnims[a].Animation.Played then
1316 begin
1317 OnceAnims[a].Animation.Free();
1318 OnceAnims[a].Animation := nil;
1319 end
1320 else
1321 OnceAnims[a].Animation.Update();
1322 end;
1323 end;
1324 end;
1326 procedure g_GFX_Draw();
1327 var
1328 a, len: Integer;
1329 begin
1330 if Particles <> nil then
1331 begin
1332 glDisable(GL_TEXTURE_2D);
1333 glPointSize(2);
1335 glEnable(GL_BLEND);
1336 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1338 glBegin(GL_POINTS);
1340 len := High(Particles);
1342 for a := 0 to len do
1343 with Particles[a] do
1344 if (State <> STATE_FREE) and (X >= sX) and (Y >= sY) and
1345 (X <= sX+sWidth) and (sY <= sY+sHeight) then
1346 begin
1347 glColor4ub(Red, Green, Blue, Alpha);
1348 glVertex2i(X + offsetX, Y + offsetY);
1349 end;
1351 glEnd();
1353 glDisable(GL_BLEND);
1354 end;
1356 if OnceAnims <> nil then
1357 for a := 0 to High(OnceAnims) do
1358 if OnceAnims[a].Animation <> nil then
1359 with OnceAnims[a] do
1360 Animation.Draw(X, Y, M_NONE);
1361 end;
1363 end.