DEADSOFTWARE

slightly better water 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 D2F_NEW_SPARK_THINKER}
18 unit g_gfx;
20 interface
22 uses
23 e_log, 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 var
64 gpart_dbg_enabled: Boolean = true;
65 gpart_dbg_phys_enabled: Boolean = true;
68 implementation
70 uses
71 g_map, g_panel, g_basic, Math, e_graphics, GL, GLExt,
72 g_options, g_console, SysUtils, g_triggers, MAPDEF,
73 g_game, g_language, g_net;
75 type
76 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 // for bubbles
89 liquidTopY: Integer; // don't float higher than this
90 // for water
91 stickDX: Integer;
93 //k8: sorry, i have to emulate virtual methods this way, 'cause i haet `Object`
95 procedure thinkerBlood ();
96 procedure thinkerSpark ();
97 procedure thinkerBubble ();
98 procedure thinkerWater ();
100 function alive (): Boolean; inline;
101 procedure die (); inline;
102 procedure think (); inline;
103 end;
105 TOnceAnim = record
106 AnimType: Byte;
107 X, Y: Integer;
108 Animation: TAnimation;
109 end;
111 const
112 PARTICLE_BLOOD = 0;
113 PARTICLE_SPARK = 1;
114 PARTICLE_BUBBLES = 2;
115 PARTICLE_WATER = 3;
116 STATE_FREE = 0;
117 STATE_NORMAL = 1;
118 STATE_STICK = 2;
120 var
121 Particles: array of TParticle;
122 OnceAnims: array of TOnceAnim;
123 MaxParticles: Integer;
124 CurrentParticle: Integer;
127 // ////////////////////////////////////////////////////////////////////////// //
128 function TParticle.alive (): Boolean; inline; begin result := (State <> STATE_FREE); end;
129 procedure TParticle.die (); inline; begin State := STATE_FREE; end;
131 procedure TParticle.think (); inline;
132 begin
133 case ParticleType of
134 PARTICLE_BLOOD: thinkerBlood();
135 PARTICLE_SPARK: thinkerSpark();
136 PARTICLE_BUBBLES: thinkerBubble();
137 PARTICLE_WATER: thinkerWater();
138 end;
139 end;
142 // ////////////////////////////////////////////////////////////////////////// //
143 function isBlockedAt (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_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
147 end;
149 // ???
150 function isWallAt (x, y: Integer): Boolean; inline;
151 begin
152 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
153 result := g_Map_HasAnyPanelAtPoint(x, y, (PANEL_WALL or PANEL_STEP));
154 end;
156 function isLiftUpAt (x, y: Integer): Boolean; inline;
157 begin
158 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
159 result := g_Map_HasAnyPanelAtPoint(x, y, PANEL_LIFTUP);
160 end;
162 function isLiftDownAt (x, y: Integer): Boolean; inline;
163 begin
164 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
165 result := g_Map_HasAnyPanelAtPoint(x, y, PANEL_LIFTDOWN);
166 end;
168 function isLiftLeftAt (x, y: Integer): Boolean; inline;
169 begin
170 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
171 result := g_Map_HasAnyPanelAtPoint(x, y, PANEL_LIFTLEFT);
172 end;
174 function isLiftRightAt (x, y: Integer): Boolean; inline;
175 begin
176 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
177 result := g_Map_HasAnyPanelAtPoint(x, y, PANEL_LIFTRIGHT);
178 end;
180 function isLiquidAt (x, y: Integer): Boolean; inline;
181 begin
182 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
183 result := g_Map_HasAnyPanelAtPoint(x, y, (PANEL_WATER or PANEL_ACID1 or PANEL_ACID2));
184 end;
186 function isAnythingAt (x, y: Integer): Boolean; inline;
187 begin
188 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
189 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));
190 end;
193 procedure g_Mark(x, y, Width, Height: Integer; t: Byte; st: Boolean);
194 {$IF not DEFINED(HAS_COLLIDE_BITMAP)}
195 begin
196 end;
197 {$ELSE}
198 var
199 yy, y2, xx, x2: Integer;
200 begin
201 if x < 0 then
202 begin
203 Width := Width + x;
204 x := 0;
205 end;
207 if Width < 0 then
208 Exit;
210 if y < 0 then
211 begin
212 Height := Height + y;
213 y := 0;
214 end;
216 if Height < 0 then
217 Exit;
219 if x > gMapInfo.Width then
220 Exit;
221 if y > gMapInfo.Height then
222 Exit;
224 y2 := y + Height - 1;
225 if y2 > gMapInfo.Height then
226 y2 := gMapInfo.Height;
228 x2 := x + Width - 1;
229 if x2 > gMapInfo.Width then
230 x2 := gMapInfo.Width;
232 if st then
233 begin // Óñòàíîâèòü ïðèçíàê
234 for yy := y to y2 do
235 for xx := x to x2 do
236 gCollideMap[yy][xx] := gCollideMap[yy][xx] or t;
237 end
238 else
239 begin // Óáðàòü ïðèçíàê
240 t := not t;
241 for yy := y to y2 do
242 for xx := x to x2 do
243 gCollideMap[yy][xx] := gCollideMap[yy][xx] and t;
244 end;
245 end;
246 {$ENDIF}
249 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
250 procedure CreateCollideMap();
251 var
252 a: Integer;
253 begin
254 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
255 SetLength(gCollideMap, gMapInfo.Height+1);
256 for a := 0 to High(gCollideMap) do
257 SetLength(gCollideMap[a], gMapInfo.Width+1);
259 if gWater <> nil then
260 begin
261 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 2/6', 0, True);
262 for a := 0 to High(gWater) do
263 with gWater[a] do
264 g_Mark(X, Y, Width, Height, MARK_WATER, True);
265 end;
267 if gAcid1 <> nil then
268 begin
269 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 3/6', 0, True);
270 for a := 0 to High(gAcid1) do
271 with gAcid1[a] do
272 g_Mark(X, Y, Width, Height, MARK_ACID, True);
273 end;
275 if gAcid2 <> nil then
276 begin
277 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 4/6', 0, True);
278 for a := 0 to High(gAcid2) do
279 with gAcid2[a] do
280 g_Mark(X, Y, Width, Height, MARK_ACID, True);
281 end;
283 if gLifts <> nil then
284 begin
285 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 5/6', 0, True);
286 for a := 0 to High(gLifts) do
287 with gLifts[a] do
288 begin
289 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
291 if LiftType = 0 then
292 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
293 else if LiftType = 1 then
294 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
295 else if LiftType = 2 then
296 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
297 else if LiftType = 3 then
298 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True)
299 end;
300 end;
302 if gWalls <> nil then
303 begin
304 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 6/6', 0, True);
305 for a := 0 to High(gWalls) do
306 begin
307 if gWalls[a].Door then
308 begin
309 // Çàêðûòàÿ äâåðü:
310 if gWalls[a].Enabled then
311 with gWalls[a] do
312 g_Mark(X, Y, Width, Height, MARK_DOOR, True)
313 else // Îòêðûòàÿ äâåðü:
314 if gWalls[a].Enabled then
315 with gWalls[a] do
316 g_Mark(X, Y, Width, Height, MARK_DOOR, False);
317 end
318 else // Ñòåíà
319 with gWalls[a] do
320 g_Mark(X, Y, Width, Height, MARK_WALL, True);
321 end;
322 end;
323 end;
324 {$ENDIF}
327 procedure g_GFX_Init();
328 begin
329 //CreateCollideMap();
330 end;
333 procedure g_GFX_Free();
334 var
335 a: Integer;
336 begin
337 Particles := nil;
338 SetLength(Particles, MaxParticles);
339 for a := 0 to High(Particles) do Particles[a].die();
340 CurrentParticle := 0;
342 if OnceAnims <> nil then
343 begin
344 for a := 0 to High(OnceAnims) do
345 OnceAnims[a].Animation.Free();
347 OnceAnims := nil;
348 end;
349 end;
353 procedure CorrectOffsets(id: Integer); inline;
354 var
355 part: PParticle;
356 begin
357 part := @Particles[id];
358 part.offsetX := 0;
359 part.offsetY := 0;
360 // check for upper wall
361 if isBlockedAt(part.X, part.Y-1) then part.offsetY := 1;
362 // check for left wall
363 if isBlockedAt(part.X-1, part.Y) then part.offsetX := 1;
364 end;
368 // ////////////////////////////////////////////////////////////////////////// //
369 procedure TParticle.thinkerBlood ();
370 var
371 w, h: Integer;
372 dX, dY: SmallInt;
373 b: Integer;
374 s: ShortInt;
375 begin
376 w := gMapInfo.Width;
377 h := gMapInfo.Height;
379 if gAdvBlood then
380 begin
381 if (State = STATE_STICK) then
383 if (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
384 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) and
385 (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
386 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED))
387 then
389 if (not isBlockedAt(X, Y-1)) and
390 (not isBlockedAt(X, Y+1)) and
391 (not isBlockedAt(X-1, Y)) and
392 (not isBlockedAt(X+1, Y))
393 then
394 begin // Îòëèïëà - êàïàåò
395 VelY := 0.5;
396 AccelY := 0.15;
397 State := STATE_NORMAL;
398 end
399 else
400 if Random(200) = 100 then
401 begin // Ïðèëåïëåíà - íî âîçìîæíî ñòåêàåò
402 VelY := 0.5;
403 AccelY := 0.15;
404 exit;
405 end;
407 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
408 begin
409 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
410 begin // Ëèôò ââåðõ
411 if VelY > -4-Random(3) then
412 VelY := VelY - 0.8;
413 if Abs(VelX) > 0.1 then
414 VelX := VelX - VelX/10.0;
415 VelX := VelX + (Random-Random)*0.2;
416 AccelY := 0.15;
417 end;
418 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
419 begin // Ïîòîê âëåâî
420 if VelX > -8-Random(3) then
421 VelX := VelX - 0.8;
422 AccelY := 0.15;
423 end;
424 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
425 begin // Ïîòîê âïðàâî
426 if VelX < 8+Random(3) then
427 VelX := VelX + 0.8;
428 AccelY := 0.15;
429 end;
430 end;
432 dX := Round(VelX);
433 dY := Round(VelY);
435 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
436 if (State <> STATE_STICK) and
437 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
438 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
439 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
440 begin // Âèñèò â âîçäóõå - êàïàåò
441 VelY := 0.8;
442 AccelY := 0.5;
443 State := STATE_NORMAL;
444 end;
446 if dX <> 0 then
447 begin
448 if dX > 0 then
449 s := 1
450 else
451 s := -1;
453 dX := Abs(dX);
455 for b := 1 to dX do
456 begin
457 if (X+s >= w) or (X+s <= 0) then begin die(); break; end;
459 //c := gCollideMap[Y, X+s];
461 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
462 begin // Ñòåíà/äâåðü
463 VelX := 0;
464 VelY := 0;
465 AccelX := 0;
466 AccelY := 0;
467 State := STATE_STICK;
468 Break;
469 end;
471 X := X+s;
472 end;
473 end;
475 if dY <> 0 then
476 begin
477 if dY > 0 then
478 s := 1
479 else
480 s := -1;
482 dY := Abs(dY);
484 for b := 1 to dY do
485 begin
486 if (Y+s >= h) or (Y+s <= 0) then begin die(); break; end;
488 //c := gCollideMap[Y+s, X];
490 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
491 begin // Ñòåíà/äâåðü
492 VelX := 0;
493 VelY := 0;
494 AccelX := 0;
495 AccelY := 0;
496 if (s > 0) and (State <> STATE_STICK) then
497 State := STATE_NORMAL
498 else
499 State := STATE_STICK;
500 Break;
501 end;
503 Y := Y+s;
504 end;
505 end;
506 end // if gAdvBlood
507 else
508 begin
509 dX := Round(VelX);
510 dY := Round(VelY);
512 if (X+dX >= w) or (Y+dY >= h) or
513 (X+dX <= 0) or (Y+dY <= 0) or
514 isBlockedAt(X+dX, Y+dY) {ByteBool(gCollideMap[Y+dY, X+dX] and MARK_BLOCKED)} then
515 begin // Ñòåíà/äâåðü/ãðàíèöà
516 die();
517 VelX := 0;
518 VelY := 0;
519 end
520 else
521 begin
522 Y := Y + dY;
523 X := X + dX;
524 end;
525 end;
527 VelX := VelX + AccelX;
528 VelY := VelY + AccelY;
530 // Êðîâü ðàñòâîðÿåòñÿ â æèäêîñòè:
531 if isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
532 begin
533 Inc(Time);
535 Alpha := 255 - Trunc((255.0 * Time) / LiveTime);
536 end;
537 end;
540 // ////////////////////////////////////////////////////////////////////////// //
541 procedure TParticle.thinkerSpark ();
542 var
543 dX, dY: SmallInt;
544 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
545 b: Integer;
546 s: ShortInt;
547 {$ELSE}
548 pan: TPanel;
549 ex, ey: Integer;
550 {$ENDIF}
551 begin
552 dX := Round(VelX);
553 dY := Round(VelY);
555 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
556 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
557 begin
558 pan := g_Map_traceToNearest(X, Y-1, X, Y+1, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
559 end;
560 {$ELSE}
561 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) and
562 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
563 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
564 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
565 begin // Âèñèò â âîçäóõå
566 VelY := 0.8;
567 AccelY := 0.5;
568 end;
569 {$ENDIF}
571 if (dX <> 0) then
572 begin
573 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
574 pan := g_Map_traceToNearest(X, Y, X+dX, Y, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
575 //e_WriteLog(Format('spark h-trace: (%d,%d)-(%d,%d); dx=%d; end=(%d,%d); hit=%d', [X, Y, X+dX, Y, dX, ex, ey, Integer(pan <> nil)]), MSG_NOTIFY);
576 X := ex;
577 // free to ride?
578 if (pan <> nil) then
579 begin
580 // nope
581 if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
582 VelX := 0;
583 AccelX := 0;
584 end;
585 if (X < 0) or (X >= gMapInfo.Width) then begin die(); exit; end;
586 {$ELSE}
587 if (dX > 0) then s := 1 else s := -1;
588 dX := Abs(dX);
589 for b := 1 to dX do
590 begin
591 if (X+s >= gMapInfo.Width) or (X+s <= 0) then begin die(); break; end;
592 //c := gCollideMap[Y, X+s];
593 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
594 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
595 VelX := 0;
596 AccelX := 0;
597 Break;
598 end
599 else // Ïóñòî:
600 if not isAnythingAt(X+s, Y) {c = MARK_FREE} then
601 X := X + s
602 else // Îñòàëüíîå:
603 begin
604 die();
605 break;
606 end;
607 end;
608 {$ENDIF}
609 end;
611 if (dY <> 0) then
612 begin
613 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
614 pan := g_Map_traceToNearest(X, Y, X, Y+dY, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
615 //e_WriteLog(Format('spark y-trace: (%d,%d)-(%d,%d); dy=%d; end=(%d,%d); hit=%d', [X, Y, X, Y+dY, dY, ex, ey, Integer(pan <> nil)]), MSG_NOTIFY);
616 (*
617 if (pan <> nil) then
618 begin
619 e_WriteLog(Format('spark y-trace: %08x (%d,%d)-(%d,%d); dy=%d; end=(%d,%d); hittag=%04x', [LongWord(@self), X, Y, X, Y+dY, dY, ex, ey, pan.tag]), MSG_NOTIFY);
620 end
621 else
622 begin
623 e_WriteLog(Format('spark y-trace: %08x (%d,%d)-(%d,%d); dy=%d; end=(%d,%d); hit=%d', [LongWord(@self), X, Y, X, Y+dY, dY, ex, ey, Integer(pan <> nil)]), MSG_NOTIFY);
624 end;
625 *)
626 Y := ey;
627 // free to ride?
628 if (pan <> nil) then
629 begin
630 //die(); exit;
631 // nope
632 if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
633 if (dY < 0) then
634 begin
635 VelY := -VelY;
636 AccelY := abs(AccelY);
637 end
638 else
639 begin
640 VelX := 0;
641 AccelX := 0;
642 VelY := 0;
643 AccelY := 0.8;
644 end;
645 end;
646 if (Y < 0) or (Y >= gMapInfo.Height) then begin die(); exit; end;
647 {$ELSE}
648 if (dY > 0) then s := 1 else s := -1;
649 dY := Abs(dY);
650 for b := 1 to dY do
651 begin
652 if (Y+s >= gMapInfo.Height) or (Y+s <= 0) then begin die(); break; end;
653 //c := gCollideMap[Y+s, X];
654 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
655 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
656 if s < 0 then
657 begin
658 VelY := -VelY;
659 AccelY := Abs(AccelY);
660 end
661 else // Èëè íå ïàäàåò
662 begin
663 VelX := 0;
664 AccelX := 0;
665 VelY := 0;
666 AccelY := 0.8;
667 end;
669 Break;
670 end
671 else // Ïóñòî:
672 if not isAnythingAt(X, Y+s) {c = MARK_FREE} then
673 Y := Y + s
674 else // Îñàëüíîå:
675 begin
676 die();
677 break;
678 end;
679 end;
680 {$ENDIF}
681 end;
683 if (VelX <> 0.0) then VelX += AccelX;
685 if (VelY <> 0.0) then
686 begin
687 if (AccelY < 10) then AccelY += 0.08;
688 VelY += AccelY;
689 end;
691 Time += 1;
692 end;
695 // ////////////////////////////////////////////////////////////////////////// //
696 procedure TParticle.thinkerWater ();
697 var
698 dX, dY: SmallInt;
699 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
700 w, h: Integer;
701 b: Integer;
702 s: ShortInt;
703 {$ELSE}
704 pan: TPanel;
705 ex, ey: Integer;
706 {$ENDIF}
707 begin
708 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
709 w := gMapInfo.Width;
710 h := gMapInfo.Height;
711 {$ENDIF}
713 //TODO: trace wall end when water becomes stick
714 if (State = STATE_STICK) and (Random(30) = 15) then
715 begin // Ñòåêàåò/îòëèïàåò
716 VelY := 0.5;
717 AccelY := 0.15;
718 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
719 if (not isBlockedAt(X-1, Y) {ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)}) and
720 (not isBlockedAt(X+1, Y) {ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)}) then
721 State := STATE_NORMAL;
722 {$ELSE}
723 if (stickDX = 0) then
724 begin
725 // no walls around, drop
726 State := STATE_NORMAL;
727 end
728 else
729 begin
730 if (g_Map_PanelAtPoint(X+stickDX, Y, (GridTagWall or GridTagDoor or GridTagStep)) = nil) then State := STATE_NORMAL;
731 end;
732 {$ENDIF}
733 exit;
734 end;
736 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
737 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
738 begin
739 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
740 begin // Ëèôò ââåðõ
741 if VelY > -4-Random(3) then
742 VelY := VelY - 0.8;
743 if Abs(VelX) > 0.1 then
744 VelX := VelX - VelX/10.0;
745 VelX := VelX + (Random-Random)*0.2;
746 AccelY := 0.15;
747 end;
748 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
749 begin // Ïîòîê âëåâî
750 if VelX > -8-Random(3) then
751 VelX := VelX - 0.8;
752 AccelY := 0.15;
753 end;
754 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
755 begin // Ïîòîê âïðàâî
756 if VelX < 8+Random(3) then
757 VelX := VelX + 0.8;
758 AccelY := 0.15;
759 end;
760 end;
761 {$ELSE}
762 pan := g_Map_PanelAtPoint(X, Y, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagLift));
763 if (pan <> nil) then
764 begin
765 if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
766 if ((pan.PanelType and PANEL_LIFTUP) <> 0) then
767 begin
768 if (VelY > -4-Random(3)) then VelY -= 0.8;
769 if (Abs(VelX) > 0.1) then VelX -= VelX/10.0;
770 VelX += (Random-Random)*0.2;
771 AccelY := 0.15;
772 end;
773 if ((pan.PanelType and PANEL_LIFTLEFT) <> 0) then
774 begin
775 if (VelX > -8-Random(3)) then VelX -= 0.8;
776 AccelY := 0.15;
777 end;
778 if ((pan.PanelType and PANEL_LIFTRIGHT) <> 0) then
779 begin
780 if (VelX < 8+Random(3)) then VelX += 0.8;
781 AccelY := 0.15;
782 end;
783 end;
784 {$ENDIF}
786 dX := Round(VelX);
787 dY := Round(VelY);
789 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
790 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
791 begin
792 if (State <> STATE_STICK) and
793 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
794 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
795 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
796 begin // Âèñèò â âîçäóõå - êàïàåò
797 VelY := 0.8;
798 AccelY := 0.5;
799 State := STATE_NORMAL;
800 end;
801 end;
802 {$ELSE}
803 if (State <> STATE_STICK) and (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
804 begin
805 // Âèñèò â âîçäóõå - êàïàåò
806 if (nil = g_Map_traceToNearest(X, Y-1, X, Y+1, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey)) then
807 begin
808 VelY := 0.8;
809 AccelY := 0.5;
810 State := STATE_NORMAL;
811 end;
812 end;
813 {$ENDIF}
815 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
816 // horizontal
817 if (dX <> 0) then
818 begin
819 pan := g_Map_traceToNearest(X, Y, X+dX, Y, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
820 X := ex;
821 // free to ride?
822 if (pan <> nil) then
823 begin
824 // nope
825 if (dY > 0) and ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
826 // Ñòåíà/äâåðü?
827 if ((pan.tag and (GridTagWall or GridTagDoor or GridTagStep)) <> 0) then
828 begin
829 VelX := 0;
830 VelY := 0;
831 AccelX := 0;
832 AccelY := 0;
833 State := STATE_STICK;
834 if (dX > 0) then stickDX := 1 else stickDX := -1;
835 end;
836 end;
837 if (X < 0) or (X >= gMapInfo.Width) then begin die(); exit; end;
838 end;
839 // vertical
840 if (dY <> 0) then
841 begin
842 pan := g_Map_traceToNearest(X, Y, X, Y+dY, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
843 Y := ey;
844 // free to ride?
845 if (pan <> nil) then
846 begin
847 // nope
848 if (dY > 0) and ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
849 // Ñòåíà/äâåðü?
850 if ((pan.tag and (GridTagWall or GridTagDoor or GridTagStep)) <> 0) then
851 begin
852 VelX := 0;
853 VelY := 0;
854 AccelX := 0;
855 AccelY := 0;
856 if (dY > 0) and (State <> STATE_STICK) then
857 begin
858 State := STATE_NORMAL;
859 end
860 else
861 begin
862 State := STATE_STICK;
863 if (g_Map_PanelAtPoint(X-1, Y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := -1
864 else if (g_Map_PanelAtPoint(X+1, Y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := 1
865 else stickDX := 0;
866 end;
867 end;
868 end;
869 if (Y < 0) or (Y >= gMapInfo.Height) then begin die(); exit; end;
870 end;
871 {$ELSE}
872 // horizontal
873 if (dX <> 0) then
874 begin
875 if (dX > 0) then s := 1 else s := -1;
876 for b := 1 to Abs(dX) do
877 begin
878 // Ñáîêó ãðàíèöà?
879 if (X+s >= w) or (X+s <= 0) then begin die(); break;end;
880 //c := gCollideMap[Y, X+s];
881 // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò?
882 if isLiquidAt(X+s, Y) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then begin die(); break; end;
883 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
884 begin // Ñòåíà/äâåðü
885 VelX := 0;
886 VelY := 0;
887 AccelX := 0;
888 AccelY := 0;
889 State := STATE_STICK;
890 Break;
891 end;
892 X := X+s;
893 end;
894 end;
895 // vertical
896 if (dY <> 0) then
897 begin
898 if (dY > 0) then s := 1 else s := -1;
899 for b := 1 to Abs(dY) do
900 begin
901 // Ñíèçó/ñâåðõó ãðàíèöà
902 if (Y+s >= h) or (Y+s <= 0) then begin die(); break; end;
903 //c := gCollideMap[Y+s, X];
904 // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
905 if isLiquidAt(X, Y+s) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then begin die(); break; end;
906 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
907 begin // Ñòåíà/äâåðü
908 VelX := 0;
909 VelY := 0;
910 AccelX := 0;
911 AccelY := 0;
912 if (s > 0) and (State <> STATE_STICK) then State := STATE_NORMAL else State := STATE_STICK;
913 break;
914 end;
915 Y := Y+s;
916 end;
917 end;
918 {$ENDIF}
920 VelX += AccelX;
921 VelY += AccelY;
923 Time += 1;
924 end;
927 // ////////////////////////////////////////////////////////////////////////// //
928 procedure TParticle.thinkerBubble ();
929 var
930 h: Integer;
931 dY: SmallInt;
932 b: Integer;
933 s: ShortInt;
934 begin
935 h := gMapInfo.Height;
937 dY := Round(VelY);
939 if dY <> 0 then
940 begin
941 if dY > 0 then
942 s := 1
943 else
944 s := -1;
946 for b := 1 to Abs(dY) do
947 begin
948 if (Y+s >= h) or (Y+s <= 0) then begin die(); break; end;
950 (*
951 if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
952 begin // Óæå íå æèäêîñòü
953 State := STATE_FREE;
954 Break;
955 end;
956 *)
957 // we traced liquid before, so don't bother checking
958 if (Y+s <= liquidTopY) then begin die(); break; end;
960 Y := Y+s;
961 end;
962 end;
964 if VelY > -4 then
965 VelY := VelY + AccelY;
967 Time := Time + 1;
968 end;
971 // ////////////////////////////////////////////////////////////////////////// //
972 procedure g_GFX_SparkVel (fX, fY: Integer; Count: Word; VX, VY: Integer; DevX, DevY: Byte);
973 var
974 a: Integer;
975 DevX1, DevX2,
976 DevY1, DevY2: Byte;
977 l: Integer;
978 begin
979 l := Length(Particles);
980 if l = 0 then exit;
981 if Count > l then Count := l;
983 DevX1 := DevX div 2;
984 DevX2 := DevX + 1;
985 DevY1 := DevY div 2;
986 DevY2 := DevY + 1;
988 for a := 1 to Count do
989 begin
990 with Particles[CurrentParticle] do
991 begin
992 X := fX-DevX1+Random(DevX2);
993 Y := fY-DevY1+Random(DevY2);
995 VelX := VX + (Random-Random)*3;
996 VelY := VY + (Random-Random)*3;
998 if VelY > -4 then
999 if VelY-4 < -4 then
1000 VelY := -4
1001 else
1002 VelY := VelY-4;
1004 AccelX := -Sign(VelX)*Random/100;
1005 AccelY := 0.8;
1007 Red := 255;
1008 Green := 100+Random(155);
1009 Blue := 64;
1010 Alpha := 255;
1012 State := STATE_NORMAL;
1013 Time := 0;
1014 LiveTime := 30+Random(60);
1015 ParticleType := PARTICLE_SPARK;
1017 {CorrectOffsets(CurrentParticle);}
1018 end;
1020 if CurrentParticle+2 > MaxParticles then
1021 CurrentParticle := 0
1022 else
1023 CurrentParticle := CurrentParticle+1;
1024 end;
1025 end;
1028 procedure g_GFX_Blood(fX, fY: Integer; Count: Word; vx, vy: Integer;
1029 DevX, DevY: Word; CR, CG, CB: Byte; Kind: Byte = BLOOD_NORMAL);
1030 var
1031 a: Integer;
1032 DevX1, DevX2,
1033 DevY1, DevY2: Word;
1034 l: Integer;
1035 CRnd: Byte;
1036 CC: SmallInt;
1037 begin
1038 if Kind = BLOOD_SPARKS then
1039 begin
1040 g_GFX_SparkVel(fX, fY, 2 + Random(2), -VX div 2, -VY div 2, DevX, DevY);
1041 Exit;
1042 end;
1043 l := Length(Particles);
1044 if l = 0 then
1045 Exit;
1046 if Count > l then
1047 Count := l;
1049 DevX1 := DevX div 2;
1050 DevX2 := DevX + 1;
1051 DevY1 := DevY div 2;
1052 DevY2 := DevY + 1;
1054 for a := 1 to Count do
1055 begin
1056 with Particles[CurrentParticle] do
1057 begin
1058 X := fX - DevX1 + Random(DevX2);
1059 Y := fY - DevY1 + Random(DevY2);
1062 if (X < 0) or (X > gMapInfo.Width-1) or
1063 (Y < 0) or (Y > gMapInfo.Height-1) or
1064 ByteBool(gCollideMap[Y, X] and MARK_WALL) then
1065 Continue;
1067 if isWallAt(X, Y) then continue;
1069 VelX := vx + (Random-Random)*3;
1070 VelY := vy + (Random-Random)*3;
1072 if VelY > -4 then
1073 if VelY-4 < -4 then
1074 VelY := -4
1075 else
1076 VelY := VelY-4;
1078 AccelX := -Sign(VelX)*Random/100;
1079 AccelY := 0.8;
1081 CRnd := 20*Random(6);
1082 if CR > 0 then
1083 begin
1084 CC := CR + CRnd - 50;
1085 if CC < 0 then CC := 0;
1086 if CC > 255 then CC := 255;
1087 Red := CC;
1088 end else
1089 Red := 0;
1090 if CG > 0 then
1091 begin
1092 CC := CG + CRnd - 50;
1093 if CC < 0 then CC := 0;
1094 if CC > 255 then CC := 255;
1095 Green := CC;
1096 end else
1097 Green := 0;
1098 if CB > 0 then
1099 begin
1100 CC := CB + CRnd - 50;
1101 if CC < 0 then CC := 0;
1102 if CC > 255 then CC := 255;
1103 Blue := CC;
1104 end else
1105 Blue := 0;
1107 Alpha := 255;
1109 State := STATE_NORMAL;
1110 Time := 0;
1111 LiveTime := 120+Random(40);
1112 ParticleType := PARTICLE_BLOOD;
1114 {CorrectOffsets(CurrentParticle);}
1115 end;
1117 if CurrentParticle >= MaxParticles-1 then
1118 CurrentParticle := 0
1119 else
1120 CurrentParticle := CurrentParticle+1;
1121 end;
1122 end;
1125 procedure g_GFX_Spark(fX, fY: Integer; Count: Word; Angle: SmallInt; DevX, DevY: Byte);
1126 var
1127 a: Integer;
1128 b: Single;
1129 DevX1, DevX2,
1130 DevY1, DevY2: Byte;
1131 BaseVelX, BaseVelY: Single;
1132 l: Integer;
1133 begin
1134 l := Length(Particles);
1135 if l = 0 then
1136 Exit;
1137 if Count > l then
1138 Count := l;
1140 Angle := 360 - Angle;
1142 DevX1 := DevX div 2;
1143 DevX2 := DevX + 1;
1144 DevY1 := DevY div 2;
1145 DevY2 := DevY + 1;
1147 b := DegToRad(Angle);
1148 BaseVelX := cos(b);
1149 BaseVelY := 1.6*sin(b);
1150 if Abs(BaseVelX) < 0.01 then
1151 BaseVelX := 0.0;
1152 if Abs(BaseVelY) < 0.01 then
1153 BaseVelY := 0.0;
1154 for a := 1 to Count do
1155 begin
1156 with Particles[CurrentParticle] do
1157 begin
1158 X := fX-DevX1+Random(DevX2);
1159 Y := fY-DevY1+Random(DevY2);
1161 VelX := BaseVelX*Random;
1162 VelY := BaseVelY-Random;
1163 AccelX := VelX/3.0;
1164 AccelY := VelY/5.0;
1166 Red := 255;
1167 Green := 100+Random(155);
1168 Blue := 64;
1169 Alpha := 255;
1171 State := STATE_NORMAL;
1172 Time := 0;
1173 LiveTime := 30+Random(60);
1174 ParticleType := PARTICLE_SPARK;
1176 {CorrectOffsets(CurrentParticle);}
1177 end;
1179 if CurrentParticle+2 > MaxParticles then
1180 CurrentParticle := 0
1181 else
1182 CurrentParticle := CurrentParticle+1;
1183 end;
1184 end;
1186 procedure g_GFX_Water(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DevX, DevY, Color: Byte);
1187 var
1188 a: Integer;
1189 DevX1, DevX2,
1190 DevY1, DevY2: Byte;
1191 l: Integer;
1192 begin
1193 l := Length(Particles);
1194 if l = 0 then
1195 Exit;
1196 if Count > l then
1197 Count := l;
1199 if Abs(fVelX) < 3.0 then
1200 fVelX := 3.0 - 6.0*Random;
1202 DevX1 := DevX div 2;
1203 DevX2 := DevX + 1;
1204 DevY1 := DevY div 2;
1205 DevY2 := DevY + 1;
1207 for a := 1 to Count do
1208 begin
1209 with Particles[CurrentParticle] do
1210 begin
1211 X := fX-DevX1+Random(DevX2);
1212 Y := fY-DevY1+Random(DevY2);
1214 if Abs(fVelX) < 0.5 then
1215 VelX := 1.0 - 2.0*Random
1216 else
1217 VelX := fVelX*Random;
1218 if Random(10) < 7 then
1219 VelX := -VelX;
1220 VelY := fVelY*Random;
1221 AccelX := 0.0;
1222 AccelY := 0.8;
1224 case Color of
1225 1: // Êðàñíûé
1226 begin
1227 Red := 155 + Random(9)*10;
1228 Green := Trunc(150*Random);
1229 Blue := Green;
1230 end;
1231 2: // Çåëåíûé
1232 begin
1233 Red := Trunc(150*Random);
1234 Green := 175 + Random(9)*10;
1235 Blue := Red;
1236 end;
1237 3: // Ñèíèé
1238 begin
1239 Red := Trunc(200*Random);
1240 Green := Red;
1241 Blue := 175 + Random(9)*10;
1242 end;
1243 else // Ñåðûé
1244 begin
1245 Red := 90 + Random(12)*10;
1246 Green := Red;
1247 Blue := Red;
1248 end;
1249 end;
1251 Alpha := 255;
1253 State := STATE_NORMAL;
1254 Time := 0;
1255 LiveTime := 60+Random(60);
1256 ParticleType := PARTICLE_WATER;
1258 {CorrectOffsets(CurrentParticle);}
1259 end;
1261 if CurrentParticle+2 > MaxParticles then
1262 CurrentParticle := 0
1263 else
1264 CurrentParticle := CurrentParticle+1;
1265 end;
1266 end;
1268 procedure g_GFX_SimpleWater(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DefColor, CR, CG, CB: Byte);
1269 var
1270 a: Integer;
1271 l: Integer;
1272 begin
1273 l := Length(Particles);
1274 if l = 0 then
1275 Exit;
1276 if Count > l then
1277 Count := l;
1279 for a := 1 to Count do
1280 begin
1281 with Particles[CurrentParticle] do
1282 begin
1283 X := fX;
1284 Y := fY;
1286 VelX := fVelX;
1287 VelY := fVelY;
1288 AccelX := 0.0;
1289 AccelY := 0.8;
1291 case DefColor of
1292 1: // Êðàñíûé
1293 begin
1294 Red := 155 + Random(9)*10;
1295 Green := Trunc(150*Random);
1296 Blue := Green;
1297 end;
1298 2: // Çåëåíûé
1299 begin
1300 Red := Trunc(150*Random);
1301 Green := 175 + Random(9)*10;
1302 Blue := Red;
1303 end;
1304 3: // Ñèíèé
1305 begin
1306 Red := Trunc(200*Random);
1307 Green := Red;
1308 Blue := 175 + Random(9)*10;
1309 end;
1310 4: // Ñâîé öâåò, ñâåòëåå
1311 begin
1312 Red := 20 + Random(19)*10;
1313 Green := Red;
1314 Blue := Red;
1315 Red := Min(Red + CR, 255);
1316 Green := Min(Green + CG, 255);
1317 Blue := Min(Blue + CB, 255);
1318 end;
1319 5: // Ñâîé öâåò, òåìíåå
1320 begin
1321 Red := 20 + Random(19)*10;
1322 Green := Red;
1323 Blue := Red;
1324 Red := Max(CR - Red, 0);
1325 Green := Max(CG - Green, 0);
1326 Blue := Max(CB - Blue, 0);
1327 end;
1328 else // Ñåðûé
1329 begin
1330 Red := 90 + Random(12)*10;
1331 Green := Red;
1332 Blue := Red;
1333 end;
1334 end;
1336 Alpha := 255;
1338 State := STATE_NORMAL;
1339 Time := 0;
1340 LiveTime := 60+Random(60);
1341 ParticleType := PARTICLE_WATER;
1343 {CorrectOffsets(CurrentParticle);}
1344 end;
1346 if CurrentParticle+2 > MaxParticles then
1347 CurrentParticle := 0
1348 else
1349 CurrentParticle := CurrentParticle+1;
1350 end;
1351 end;
1354 procedure g_GFX_Bubbles(fX, fY: Integer; Count: Word; DevX, DevY: Byte);
1355 var
1356 a: Integer;
1357 DevX1, DevX2,
1358 DevY1, DevY2: Byte;
1359 l, liquidx: Integer;
1360 begin
1361 l := Length(Particles);
1362 if l = 0 then
1363 Exit;
1364 if Count > l then
1365 Count := l;
1367 DevX1 := DevX div 2;
1368 DevX2 := DevX + 1;
1369 DevY1 := DevY div 2;
1370 DevY2 := DevY + 1;
1372 for a := 1 to Count do
1373 begin
1374 with Particles[CurrentParticle] do
1375 begin
1376 X := fX-DevX1+Random(DevX2);
1377 Y := fY-DevY1+Random(DevY2);
1379 if (X >= gMapInfo.Width) or (X <= 0) or
1380 (Y >= gMapInfo.Height) or (Y <= 0) then
1381 Continue;
1383 (*
1384 // don't spawn bubbles outside of the liquid
1385 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1386 Continue;
1387 *)
1389 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1390 // tracer will return `false` if we started outside of the liquid
1391 if not g_Map_TraceLiquidNonPrecise(X, Y, 0, -8, liquidx, liquidTopY) then continue;
1393 VelX := 0;
1394 VelY := -1-Random;
1395 AccelX := 0;
1396 AccelY := VelY/10;
1398 Red := 255;
1399 Green := 255;
1400 Blue := 255;
1401 Alpha := 255;
1403 State := STATE_NORMAL;
1404 Time := 0;
1405 LiveTime := 65535;
1406 ParticleType := PARTICLE_BUBBLES;
1408 {CorrectOffsets(CurrentParticle);}
1409 end;
1411 if CurrentParticle+2 > MaxParticles then
1412 CurrentParticle := 0
1413 else
1414 CurrentParticle := CurrentParticle+1;
1415 end;
1416 end;
1418 procedure g_GFX_SetMax(Count: Integer);
1419 var
1420 a: Integer;
1421 begin
1422 if Count > 50000 then Count := 50000;
1423 if (Count < 1) then Count := 1;
1425 SetLength(Particles, Count);
1426 for a := 0 to High(Particles) do Particles[a].die();
1427 MaxParticles := Count;
1428 //if CurrentParticle >= Count then
1429 CurrentParticle := 0;
1430 end;
1432 function g_GFX_GetMax(): Integer;
1433 begin
1434 Result := MaxParticles;
1435 end;
1437 function FindOnceAnim: DWORD;
1438 var
1439 i: Integer;
1440 begin
1441 if OnceAnims <> nil then
1442 for i := 0 to High(OnceAnims) do
1443 if OnceAnims[i].Animation = nil then
1444 begin
1445 Result := i;
1446 Exit;
1447 end;
1449 if OnceAnims = nil then
1450 begin
1451 SetLength(OnceAnims, 16);
1452 Result := 0;
1453 end
1454 else
1455 begin
1456 Result := High(OnceAnims) + 1;
1457 SetLength(OnceAnims, Length(OnceAnims) + 16);
1458 end;
1459 end;
1461 procedure g_GFX_OnceAnim(X, Y: Integer; Anim: TAnimation; AnimType: Byte = 0);
1462 var
1463 find_id: DWORD;
1464 begin
1465 if Anim = nil then
1466 Exit;
1468 find_id := FindOnceAnim();
1470 OnceAnims[find_id].AnimType := AnimType;
1471 OnceAnims[find_id].Animation := TAnimation.Create(Anim.FramesID, Anim.Loop, Anim.Speed);
1472 OnceAnims[find_id].Animation.Blending := Anim.Blending;
1473 OnceAnims[find_id].Animation.Alpha := Anim.Alpha;
1474 OnceAnims[find_id].X := X;
1475 OnceAnims[find_id].Y := Y;
1476 end;
1478 procedure g_GFX_Update();
1479 var
1480 a: Integer;
1481 w, h: Integer;
1482 len: Integer;
1483 begin
1484 if not gpart_dbg_enabled then exit;
1485 if Particles <> nil then
1486 begin
1487 w := gMapInfo.Width;
1488 h := gMapInfo.Height;
1490 len := High(Particles);
1492 for a := 0 to len do
1493 begin
1494 if Particles[a].alive then
1495 begin
1496 with Particles[a] do
1497 begin
1498 if (Time = LiveTime) then begin die(); continue; end;
1499 if (X+1 >= w) or (Y+1 >= h) or (X <= 0) or (Y <= 0) then begin die(); end;
1500 //if not alive then Continue;
1501 //e_WriteLog(Format('particle #%d: %d', [State, ParticleType]), MSG_NOTIFY);
1502 think();
1503 {CorrectOffsets(a);}
1504 end; // with
1505 end; // if
1506 end; // for
1507 end; // Particles <> nil
1509 if OnceAnims <> nil then
1510 begin
1511 for a := 0 to High(OnceAnims) do
1512 if OnceAnims[a].Animation <> nil then
1513 begin
1514 case OnceAnims[a].AnimType of
1515 ONCEANIM_SMOKE:
1516 begin
1517 if Random(3) = 0 then
1518 OnceAnims[a].X := OnceAnims[a].X-1+Random(3);
1519 if Random(2) = 0 then
1520 OnceAnims[a].Y := OnceAnims[a].Y-Random(2);
1521 end;
1522 end;
1524 if OnceAnims[a].Animation.Played then
1525 begin
1526 OnceAnims[a].Animation.Free();
1527 OnceAnims[a].Animation := nil;
1528 end
1529 else
1530 OnceAnims[a].Animation.Update();
1531 end;
1532 end;
1533 end;
1535 procedure g_GFX_Draw();
1536 var
1537 a, len: Integer;
1538 begin
1539 if Particles <> nil then
1540 begin
1541 glDisable(GL_TEXTURE_2D);
1542 glPointSize(2);
1544 glEnable(GL_BLEND);
1545 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1547 glBegin(GL_POINTS);
1549 len := High(Particles);
1551 for a := 0 to len do
1552 with Particles[a] do
1553 if alive and (X >= sX) and (Y >= sY) and (X <= sX+sWidth) and (sY <= sY+sHeight) then
1554 begin
1555 glColor4ub(Red, Green, Blue, Alpha);
1556 glVertex2i(X + offsetX, Y + offsetY);
1557 end;
1559 glEnd();
1561 glDisable(GL_BLEND);
1562 end;
1564 if OnceAnims <> nil then
1565 for a := 0 to High(OnceAnims) do
1566 if OnceAnims[a].Animation <> nil then
1567 with OnceAnims[a] do
1568 Animation.Draw(X, Y, M_NONE);
1569 end;
1571 end.