DEADSOFTWARE

faster blood 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 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
374 b: Integer;
375 s: ShortInt;
376 {$ELSE}
377 pan: TPanel;
378 ex, ey: Integer;
379 {$ENDIF}
380 begin
381 w := gMapInfo.Width;
382 h := gMapInfo.Height;
384 if gAdvBlood then
385 begin
386 if (State = STATE_STICK) then
387 begin
388 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
390 if (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
391 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) and
392 (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
393 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED))
394 then
396 if (not isBlockedAt(X, Y-1)) and
397 (not isBlockedAt(X, Y+1)) and
398 (not isBlockedAt(X-1, Y)) and
399 (not isBlockedAt(X+1, Y))
400 {$ELSE}
401 if not g_Map_CollidePanel(X-1, Y-1, 3, 3, (PANEL_STEP or PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR))
402 {$ENDIF}
403 then
404 begin // Îòëèïëà - êàïàåò
405 VelY := 0.5;
406 AccelY := 0.15;
407 State := STATE_NORMAL;
408 end
409 else if (Random(200) = 100) then
410 begin // Ïðèëåïëåíà - íî âîçìîæíî ñòåêàåò
411 VelY := 0.5;
412 AccelY := 0.15;
413 exit;
414 end;
415 end;
417 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
418 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
419 begin
420 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
421 begin // Ëèôò ââåðõ
422 if (VelY > -4-Random(3)) then VelY -= 0.8;
423 if (abs(VelX) > 0.1) then VelX -= VelX/10.0;
424 VelX += (Random-Random)*0.2;
425 AccelY := 0.15;
426 end;
427 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
428 begin // Ïîòîê âëåâî
429 if (VelX > -8-Random(3)) then VelX -= 0.8;
430 AccelY := 0.15;
431 end;
432 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
433 begin // Ïîòîê âïðàâî
434 if (VelX < 8+Random(3)) then VelX += 0.8;
435 AccelY := 0.15;
436 end;
437 end;
438 {$ELSE}
439 pan := g_Map_PanelAtPoint(X, Y, GridTagLift);
440 if (pan <> nil) then
441 begin
442 if ((pan.PanelType and PANEL_LIFTUP) <> 0) then
443 begin
444 if (VelY > -4-Random(3)) then VelY -= 0.8;
445 if (abs(VelX) > 0.1) then VelX -= VelX/10.0;
446 VelX += (Random-Random)*0.2;
447 AccelY := 0.15;
448 end;
449 if ((pan.PanelType and PANEL_LIFTLEFT) <> 0) then
450 begin
451 if (VelX > -8-Random(3)) then VelX -= 0.8;
452 AccelY := 0.15;
453 end;
454 if ((pan.PanelType and PANEL_LIFTRIGHT) <> 0) then
455 begin
456 if (VelX < 8+Random(3)) then VelX += 0.8;
457 AccelY := 0.15;
458 end;
459 end;
460 {$ENDIF}
462 dX := Round(VelX);
463 dY := Round(VelY);
465 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
466 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
467 begin
468 if (State <> STATE_STICK) and
469 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
470 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
471 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
472 begin // Âèñèò â âîçäóõå - êàïàåò
473 VelY := 0.8;
474 AccelY := 0.5;
475 State := STATE_NORMAL;
476 end;
477 end;
478 {$ELSE}
479 if (State <> STATE_STICK) and (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
480 begin
481 // Âèñèò â âîçäóõå - êàïàåò
482 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
483 begin
484 VelY := 0.8;
485 AccelY := 0.5;
486 State := STATE_NORMAL;
487 end;
488 end;
489 {$ENDIF}
491 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
492 // horizontal
493 if (dX <> 0) then
494 begin
495 pan := g_Map_traceToNearest(X, Y, X+dX, Y, (GridTagWall or GridTagDoor or GridTagStep), @ex, @ey);
496 X := ex;
497 // free to ride?
498 if (pan <> nil) then
499 begin
500 // Ñòåíà/äâåðü
501 VelX := 0;
502 VelY := 0;
503 AccelX := 0;
504 AccelY := 0;
505 State := STATE_STICK;
506 if (dX > 0) then stickDX := 1 else stickDX := -1;
507 end;
508 if (X < 0) or (X >= w) then begin die(); exit; end;
509 end;
510 // vertical
511 if (dY <> 0) then
512 begin
513 pan := g_Map_traceToNearest(X, Y, X, Y+dY, (GridTagWall or GridTagDoor or GridTagStep), @ex, @ey);
514 Y := ey;
515 // free to ride?
516 if (pan <> nil) then
517 begin
518 // Ñòåíà/äâåðü
519 VelX := 0;
520 VelY := 0;
521 AccelX := 0;
522 AccelY := 0;
523 if (dY > 0) and (State <> STATE_STICK) then
524 begin
525 State := STATE_NORMAL;
526 end
527 else
528 begin
529 State := STATE_STICK;
530 if (g_Map_PanelAtPoint(X-1, Y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := -1
531 else if (g_Map_PanelAtPoint(X+1, Y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := 1
532 else stickDX := 0;
533 end;
534 end;
535 if (Y < 0) or (Y >= h) then begin die(); exit; end;
536 end;
537 {$ELSE}
538 // horizontal
539 if (dX <> 0) then
540 begin
541 if (dX > 0) then s := 1 else s := -1;
542 dX := Abs(dX);
543 for b := 1 to dX do
544 begin
545 if (X+s >= w) or (X+s <= 0) then begin die(); break; end;
546 //c := gCollideMap[Y, X+s];
547 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
548 begin // Ñòåíà/äâåðü
549 VelX := 0;
550 VelY := 0;
551 AccelX := 0;
552 AccelY := 0;
553 State := STATE_STICK;
554 break;
555 end;
556 X := X+s;
557 end;
558 end;
559 // vertical
560 if (dY <> 0) then
561 begin
562 if (dY > 0) then s := 1 else s := -1;
563 dY := Abs(dY);
564 for b := 1 to dY do
565 begin
566 if (Y+s >= h) or (Y+s <= 0) then begin die(); break; end;
567 //c := gCollideMap[Y+s, X];
568 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
569 begin // Ñòåíà/äâåðü
570 VelX := 0;
571 VelY := 0;
572 AccelX := 0;
573 AccelY := 0;
574 if (s > 0) and (State <> STATE_STICK) then State := STATE_NORMAL else State := STATE_STICK;
575 break;
576 end;
577 Y := Y+s;
578 end;
579 end;
580 {$ENDIF}
581 end // if gAdvBlood
582 else
583 begin
584 dX := Round(VelX);
585 dY := Round(VelY);
586 if (X+dX >= w) or (Y+dY >= h) or (X+dX <= 0) or (Y+dY <= 0) or isBlockedAt(X+dX, Y+dY) {ByteBool(gCollideMap[Y+dY, X+dX] and MARK_BLOCKED)} then
587 begin // Ñòåíà/äâåðü/ãðàíèöà
588 die();
589 exit;
590 //VelX := 0;
591 //VelY := 0;
592 end
593 else
594 begin
595 Y += dY;
596 X += dX;
597 end;
598 end;
600 VelX += AccelX;
601 VelY += AccelY;
603 // Êðîâü ðàñòâîðÿåòñÿ â æèäêîñòè:
604 if isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
605 begin
606 Time += 1;
607 Alpha := 255-trunc((255.0*Time)/LiveTime);
608 end;
609 end;
613 // ////////////////////////////////////////////////////////////////////////// //
614 procedure TParticle.thinkerWater ();
615 var
616 dX, dY: SmallInt;
617 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
618 w, h: Integer;
619 b: Integer;
620 s: ShortInt;
621 {$ELSE}
622 pan: TPanel;
623 ex, ey: Integer;
624 {$ENDIF}
625 begin
626 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
627 w := gMapInfo.Width;
628 h := gMapInfo.Height;
629 {$ENDIF}
631 //TODO: trace wall end when water becomes stick
632 if (State = STATE_STICK) and (Random(30) = 15) then
633 begin // Ñòåêàåò/îòëèïàåò
634 VelY := 0.5;
635 AccelY := 0.15;
636 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
637 if (not isBlockedAt(X-1, Y) {ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)}) and
638 (not isBlockedAt(X+1, Y) {ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)}) then
639 State := STATE_NORMAL;
640 {$ELSE}
641 if (stickDX = 0) then
642 begin
643 // no walls around, drop
644 State := STATE_NORMAL;
645 end
646 else
647 begin
648 if (g_Map_PanelAtPoint(X+stickDX, Y, (GridTagWall or GridTagDoor or GridTagStep)) = nil) then State := STATE_NORMAL;
649 end;
650 {$ENDIF}
651 exit;
652 end;
654 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
655 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
656 begin
657 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
658 begin // Ëèôò ââåðõ
659 if VelY > -4-Random(3) then
660 VelY := VelY - 0.8;
661 if Abs(VelX) > 0.1 then
662 VelX := VelX - VelX/10.0;
663 VelX := VelX + (Random-Random)*0.2;
664 AccelY := 0.15;
665 end;
666 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
667 begin // Ïîòîê âëåâî
668 if VelX > -8-Random(3) then
669 VelX := VelX - 0.8;
670 AccelY := 0.15;
671 end;
672 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
673 begin // Ïîòîê âïðàâî
674 if VelX < 8+Random(3) then
675 VelX := VelX + 0.8;
676 AccelY := 0.15;
677 end;
678 end;
679 {$ELSE}
680 pan := g_Map_PanelAtPoint(X, Y, (GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagLift));
681 if (pan <> nil) then
682 begin
683 if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
684 if ((pan.PanelType and PANEL_LIFTUP) <> 0) then
685 begin
686 if (VelY > -4-Random(3)) then VelY -= 0.8;
687 if (Abs(VelX) > 0.1) then VelX -= VelX/10.0;
688 VelX += (Random-Random)*0.2;
689 AccelY := 0.15;
690 end;
691 if ((pan.PanelType and PANEL_LIFTLEFT) <> 0) then
692 begin
693 if (VelX > -8-Random(3)) then VelX -= 0.8;
694 AccelY := 0.15;
695 end;
696 if ((pan.PanelType and PANEL_LIFTRIGHT) <> 0) then
697 begin
698 if (VelX < 8+Random(3)) then VelX += 0.8;
699 AccelY := 0.15;
700 end;
701 end;
702 {$ENDIF}
704 dX := Round(VelX);
705 dY := Round(VelY);
707 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
708 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
709 begin
710 if (State <> STATE_STICK) and
711 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
712 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
713 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
714 begin // Âèñèò â âîçäóõå - êàïàåò
715 VelY := 0.8;
716 AccelY := 0.5;
717 State := STATE_NORMAL;
718 end;
719 end;
720 {$ELSE}
721 if (State <> STATE_STICK) and (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
722 begin
723 // Âèñèò â âîçäóõå - êàïàåò
724 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
725 begin
726 VelY := 0.8;
727 AccelY := 0.5;
728 State := STATE_NORMAL;
729 end;
730 end;
731 {$ENDIF}
733 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
734 // horizontal
735 if (dX <> 0) then
736 begin
737 pan := g_Map_traceToNearest(X, Y, X+dX, Y, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
738 X := ex;
739 // free to ride?
740 if (pan <> nil) then
741 begin
742 // nope
743 if (dY > 0) and ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
744 // Ñòåíà/äâåðü?
745 if ((pan.tag and (GridTagWall or GridTagDoor or GridTagStep)) <> 0) then
746 begin
747 VelX := 0;
748 VelY := 0;
749 AccelX := 0;
750 AccelY := 0;
751 State := STATE_STICK;
752 if (dX > 0) then stickDX := 1 else stickDX := -1;
753 end;
754 end;
755 if (X < 0) or (X >= gMapInfo.Width) then begin die(); exit; end;
756 end;
757 // vertical
758 if (dY <> 0) then
759 begin
760 pan := g_Map_traceToNearest(X, Y, X, Y+dY, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
761 Y := ey;
762 // free to ride?
763 if (pan <> nil) then
764 begin
765 // nope
766 if (dY > 0) and ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
767 // Ñòåíà/äâåðü?
768 if ((pan.tag and (GridTagWall or GridTagDoor or GridTagStep)) <> 0) then
769 begin
770 VelX := 0;
771 VelY := 0;
772 AccelX := 0;
773 AccelY := 0;
774 if (dY > 0) and (State <> STATE_STICK) then
775 begin
776 State := STATE_NORMAL;
777 end
778 else
779 begin
780 State := STATE_STICK;
781 if (g_Map_PanelAtPoint(X-1, Y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := -1
782 else if (g_Map_PanelAtPoint(X+1, Y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := 1
783 else stickDX := 0;
784 end;
785 end;
786 end;
787 if (Y < 0) or (Y >= gMapInfo.Height) then begin die(); exit; end;
788 end;
789 {$ELSE}
790 // horizontal
791 if (dX <> 0) then
792 begin
793 if (dX > 0) then s := 1 else s := -1;
794 for b := 1 to Abs(dX) do
795 begin
796 // Ñáîêó ãðàíèöà?
797 if (X+s >= w) or (X+s <= 0) then begin die(); break;end;
798 //c := gCollideMap[Y, X+s];
799 // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò?
800 if isLiquidAt(X+s, Y) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then begin die(); break; end;
801 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
802 begin // Ñòåíà/äâåðü
803 VelX := 0;
804 VelY := 0;
805 AccelX := 0;
806 AccelY := 0;
807 State := STATE_STICK;
808 Break;
809 end;
810 X := X+s;
811 end;
812 end;
813 // vertical
814 if (dY <> 0) then
815 begin
816 if (dY > 0) then s := 1 else s := -1;
817 for b := 1 to Abs(dY) do
818 begin
819 // Ñíèçó/ñâåðõó ãðàíèöà
820 if (Y+s >= h) or (Y+s <= 0) then begin die(); break; end;
821 //c := gCollideMap[Y+s, X];
822 // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
823 if isLiquidAt(X, Y+s) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then begin die(); break; end;
824 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
825 begin // Ñòåíà/äâåðü
826 VelX := 0;
827 VelY := 0;
828 AccelX := 0;
829 AccelY := 0;
830 if (s > 0) and (State <> STATE_STICK) then State := STATE_NORMAL else State := STATE_STICK;
831 break;
832 end;
833 Y := Y+s;
834 end;
835 end;
836 {$ENDIF}
838 VelX += AccelX;
839 VelY += AccelY;
841 Time += 1;
842 end;
845 // ////////////////////////////////////////////////////////////////////////// //
846 procedure TParticle.thinkerSpark ();
847 var
848 dX, dY: SmallInt;
849 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
850 b: Integer;
851 s: ShortInt;
852 {$ELSE}
853 pan: TPanel;
854 ex, ey: Integer;
855 {$ENDIF}
856 begin
857 dX := Round(VelX);
858 dY := Round(VelY);
860 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
861 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
862 begin
863 pan := g_Map_traceToNearest(X, Y-1, X, Y+1, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
864 end;
865 {$ELSE}
866 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) and
867 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
868 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
869 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
870 begin // Âèñèò â âîçäóõå
871 VelY := 0.8;
872 AccelY := 0.5;
873 end;
874 {$ENDIF}
876 if (dX <> 0) then
877 begin
878 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
879 pan := g_Map_traceToNearest(X, Y, X+dX, Y, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
880 //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);
881 X := ex;
882 // free to ride?
883 if (pan <> nil) then
884 begin
885 // nope
886 if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
887 VelX := 0;
888 AccelX := 0;
889 end;
890 if (X < 0) or (X >= gMapInfo.Width) then begin die(); exit; end;
891 {$ELSE}
892 if (dX > 0) then s := 1 else s := -1;
893 dX := Abs(dX);
894 for b := 1 to dX do
895 begin
896 if (X+s >= gMapInfo.Width) or (X+s <= 0) then begin die(); break; end;
897 //c := gCollideMap[Y, X+s];
898 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
899 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
900 VelX := 0;
901 AccelX := 0;
902 Break;
903 end
904 else // Ïóñòî:
905 if not isAnythingAt(X+s, Y) {c = MARK_FREE} then
906 X := X + s
907 else // Îñòàëüíîå:
908 begin
909 die();
910 break;
911 end;
912 end;
913 {$ENDIF}
914 end;
916 if (dY <> 0) then
917 begin
918 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
919 pan := g_Map_traceToNearest(X, Y, X, Y+dY, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
920 //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);
921 (*
922 if (pan <> nil) then
923 begin
924 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);
925 end
926 else
927 begin
928 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);
929 end;
930 *)
931 Y := ey;
932 // free to ride?
933 if (pan <> nil) then
934 begin
935 //die(); exit;
936 // nope
937 if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
938 if (dY < 0) then
939 begin
940 VelY := -VelY;
941 AccelY := abs(AccelY);
942 end
943 else
944 begin
945 VelX := 0;
946 AccelX := 0;
947 VelY := 0;
948 AccelY := 0.8;
949 end;
950 end;
951 if (Y < 0) or (Y >= gMapInfo.Height) then begin die(); exit; end;
952 {$ELSE}
953 if (dY > 0) then s := 1 else s := -1;
954 dY := Abs(dY);
955 for b := 1 to dY do
956 begin
957 if (Y+s >= gMapInfo.Height) or (Y+s <= 0) then begin die(); break; end;
958 //c := gCollideMap[Y+s, X];
959 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
960 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
961 if s < 0 then
962 begin
963 VelY := -VelY;
964 AccelY := Abs(AccelY);
965 end
966 else // Èëè íå ïàäàåò
967 begin
968 VelX := 0;
969 AccelX := 0;
970 VelY := 0;
971 AccelY := 0.8;
972 end;
974 Break;
975 end
976 else // Ïóñòî:
977 if not isAnythingAt(X, Y+s) {c = MARK_FREE} then
978 Y := Y + s
979 else // Îñàëüíîå:
980 begin
981 die();
982 break;
983 end;
984 end;
985 {$ENDIF}
986 end;
988 if (VelX <> 0.0) then VelX += AccelX;
990 if (VelY <> 0.0) then
991 begin
992 if (AccelY < 10) then AccelY += 0.08;
993 VelY += AccelY;
994 end;
996 Time += 1;
997 end;
999 // ////////////////////////////////////////////////////////////////////////// //
1000 procedure TParticle.thinkerBubble ();
1001 var
1002 h: Integer;
1003 dY: SmallInt;
1004 b: Integer;
1005 s: ShortInt;
1006 begin
1007 h := gMapInfo.Height;
1009 dY := Round(VelY);
1011 if dY <> 0 then
1012 begin
1013 if dY > 0 then
1014 s := 1
1015 else
1016 s := -1;
1018 for b := 1 to Abs(dY) do
1019 begin
1020 if (Y+s >= h) or (Y+s <= 0) then begin die(); break; end;
1022 (*
1023 if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
1024 begin // Óæå íå æèäêîñòü
1025 State := STATE_FREE;
1026 Break;
1027 end;
1028 *)
1029 // we traced liquid before, so don't bother checking
1030 if (Y+s <= liquidTopY) then begin die(); break; end;
1032 Y := Y+s;
1033 end;
1034 end;
1036 if VelY > -4 then
1037 VelY := VelY + AccelY;
1039 Time := Time + 1;
1040 end;
1043 // ////////////////////////////////////////////////////////////////////////// //
1044 procedure g_GFX_SparkVel (fX, fY: Integer; Count: Word; VX, VY: Integer; DevX, DevY: Byte);
1045 var
1046 a: Integer;
1047 DevX1, DevX2,
1048 DevY1, DevY2: Byte;
1049 l: Integer;
1050 begin
1051 l := Length(Particles);
1052 if l = 0 then exit;
1053 if Count > l then Count := l;
1055 DevX1 := DevX div 2;
1056 DevX2 := DevX + 1;
1057 DevY1 := DevY div 2;
1058 DevY2 := DevY + 1;
1060 for a := 1 to Count do
1061 begin
1062 with Particles[CurrentParticle] do
1063 begin
1064 X := fX-DevX1+Random(DevX2);
1065 Y := fY-DevY1+Random(DevY2);
1067 VelX := VX + (Random-Random)*3;
1068 VelY := VY + (Random-Random)*3;
1070 if VelY > -4 then
1071 if VelY-4 < -4 then
1072 VelY := -4
1073 else
1074 VelY := VelY-4;
1076 AccelX := -Sign(VelX)*Random/100;
1077 AccelY := 0.8;
1079 Red := 255;
1080 Green := 100+Random(155);
1081 Blue := 64;
1082 Alpha := 255;
1084 State := STATE_NORMAL;
1085 Time := 0;
1086 LiveTime := 30+Random(60);
1087 ParticleType := PARTICLE_SPARK;
1089 {CorrectOffsets(CurrentParticle);}
1090 end;
1092 if CurrentParticle+2 > MaxParticles then
1093 CurrentParticle := 0
1094 else
1095 CurrentParticle := CurrentParticle+1;
1096 end;
1097 end;
1100 procedure g_GFX_Blood(fX, fY: Integer; Count: Word; vx, vy: Integer;
1101 DevX, DevY: Word; CR, CG, CB: Byte; Kind: Byte = BLOOD_NORMAL);
1102 var
1103 a: Integer;
1104 DevX1, DevX2,
1105 DevY1, DevY2: Word;
1106 l: Integer;
1107 CRnd: Byte;
1108 CC: SmallInt;
1109 begin
1110 if Kind = BLOOD_SPARKS then
1111 begin
1112 g_GFX_SparkVel(fX, fY, 2 + Random(2), -VX div 2, -VY div 2, DevX, DevY);
1113 Exit;
1114 end;
1115 l := Length(Particles);
1116 if l = 0 then
1117 Exit;
1118 if Count > l then
1119 Count := l;
1121 DevX1 := DevX div 2;
1122 DevX2 := DevX + 1;
1123 DevY1 := DevY div 2;
1124 DevY2 := DevY + 1;
1126 for a := 1 to Count do
1127 begin
1128 with Particles[CurrentParticle] do
1129 begin
1130 X := fX - DevX1 + Random(DevX2);
1131 Y := fY - DevY1 + Random(DevY2);
1134 if (X < 0) or (X > gMapInfo.Width-1) or
1135 (Y < 0) or (Y > gMapInfo.Height-1) or
1136 ByteBool(gCollideMap[Y, X] and MARK_WALL) then
1137 Continue;
1139 if isWallAt(X, Y) then continue;
1141 VelX := vx + (Random-Random)*3;
1142 VelY := vy + (Random-Random)*3;
1144 if VelY > -4 then
1145 if VelY-4 < -4 then
1146 VelY := -4
1147 else
1148 VelY := VelY-4;
1150 AccelX := -Sign(VelX)*Random/100;
1151 AccelY := 0.8;
1153 CRnd := 20*Random(6);
1154 if CR > 0 then
1155 begin
1156 CC := CR + CRnd - 50;
1157 if CC < 0 then CC := 0;
1158 if CC > 255 then CC := 255;
1159 Red := CC;
1160 end else
1161 Red := 0;
1162 if CG > 0 then
1163 begin
1164 CC := CG + CRnd - 50;
1165 if CC < 0 then CC := 0;
1166 if CC > 255 then CC := 255;
1167 Green := CC;
1168 end else
1169 Green := 0;
1170 if CB > 0 then
1171 begin
1172 CC := CB + CRnd - 50;
1173 if CC < 0 then CC := 0;
1174 if CC > 255 then CC := 255;
1175 Blue := CC;
1176 end else
1177 Blue := 0;
1179 Alpha := 255;
1181 State := STATE_NORMAL;
1182 Time := 0;
1183 LiveTime := 120+Random(40);
1184 ParticleType := PARTICLE_BLOOD;
1186 {CorrectOffsets(CurrentParticle);}
1187 end;
1189 if CurrentParticle >= MaxParticles-1 then
1190 CurrentParticle := 0
1191 else
1192 CurrentParticle := CurrentParticle+1;
1193 end;
1194 end;
1197 procedure g_GFX_Spark(fX, fY: Integer; Count: Word; Angle: SmallInt; DevX, DevY: Byte);
1198 var
1199 a: Integer;
1200 b: Single;
1201 DevX1, DevX2,
1202 DevY1, DevY2: Byte;
1203 BaseVelX, BaseVelY: Single;
1204 l: Integer;
1205 begin
1206 l := Length(Particles);
1207 if l = 0 then
1208 Exit;
1209 if Count > l then
1210 Count := l;
1212 Angle := 360 - Angle;
1214 DevX1 := DevX div 2;
1215 DevX2 := DevX + 1;
1216 DevY1 := DevY div 2;
1217 DevY2 := DevY + 1;
1219 b := DegToRad(Angle);
1220 BaseVelX := cos(b);
1221 BaseVelY := 1.6*sin(b);
1222 if Abs(BaseVelX) < 0.01 then
1223 BaseVelX := 0.0;
1224 if Abs(BaseVelY) < 0.01 then
1225 BaseVelY := 0.0;
1226 for a := 1 to Count do
1227 begin
1228 with Particles[CurrentParticle] do
1229 begin
1230 X := fX-DevX1+Random(DevX2);
1231 Y := fY-DevY1+Random(DevY2);
1233 VelX := BaseVelX*Random;
1234 VelY := BaseVelY-Random;
1235 AccelX := VelX/3.0;
1236 AccelY := VelY/5.0;
1238 Red := 255;
1239 Green := 100+Random(155);
1240 Blue := 64;
1241 Alpha := 255;
1243 State := STATE_NORMAL;
1244 Time := 0;
1245 LiveTime := 30+Random(60);
1246 ParticleType := PARTICLE_SPARK;
1248 {CorrectOffsets(CurrentParticle);}
1249 end;
1251 if CurrentParticle+2 > MaxParticles then
1252 CurrentParticle := 0
1253 else
1254 CurrentParticle := CurrentParticle+1;
1255 end;
1256 end;
1258 procedure g_GFX_Water(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DevX, DevY, Color: Byte);
1259 var
1260 a: Integer;
1261 DevX1, DevX2,
1262 DevY1, DevY2: Byte;
1263 l: Integer;
1264 begin
1265 l := Length(Particles);
1266 if l = 0 then
1267 Exit;
1268 if Count > l then
1269 Count := l;
1271 if Abs(fVelX) < 3.0 then
1272 fVelX := 3.0 - 6.0*Random;
1274 DevX1 := DevX div 2;
1275 DevX2 := DevX + 1;
1276 DevY1 := DevY div 2;
1277 DevY2 := DevY + 1;
1279 for a := 1 to Count do
1280 begin
1281 with Particles[CurrentParticle] do
1282 begin
1283 X := fX-DevX1+Random(DevX2);
1284 Y := fY-DevY1+Random(DevY2);
1286 if Abs(fVelX) < 0.5 then
1287 VelX := 1.0 - 2.0*Random
1288 else
1289 VelX := fVelX*Random;
1290 if Random(10) < 7 then
1291 VelX := -VelX;
1292 VelY := fVelY*Random;
1293 AccelX := 0.0;
1294 AccelY := 0.8;
1296 case Color of
1297 1: // Êðàñíûé
1298 begin
1299 Red := 155 + Random(9)*10;
1300 Green := Trunc(150*Random);
1301 Blue := Green;
1302 end;
1303 2: // Çåëåíûé
1304 begin
1305 Red := Trunc(150*Random);
1306 Green := 175 + Random(9)*10;
1307 Blue := Red;
1308 end;
1309 3: // Ñèíèé
1310 begin
1311 Red := Trunc(200*Random);
1312 Green := Red;
1313 Blue := 175 + Random(9)*10;
1314 end;
1315 else // Ñåðûé
1316 begin
1317 Red := 90 + Random(12)*10;
1318 Green := Red;
1319 Blue := Red;
1320 end;
1321 end;
1323 Alpha := 255;
1325 State := STATE_NORMAL;
1326 Time := 0;
1327 LiveTime := 60+Random(60);
1328 ParticleType := PARTICLE_WATER;
1330 {CorrectOffsets(CurrentParticle);}
1331 end;
1333 if CurrentParticle+2 > MaxParticles then
1334 CurrentParticle := 0
1335 else
1336 CurrentParticle := CurrentParticle+1;
1337 end;
1338 end;
1340 procedure g_GFX_SimpleWater(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DefColor, CR, CG, CB: Byte);
1341 var
1342 a: Integer;
1343 l: Integer;
1344 begin
1345 l := Length(Particles);
1346 if l = 0 then
1347 Exit;
1348 if Count > l then
1349 Count := l;
1351 for a := 1 to Count do
1352 begin
1353 with Particles[CurrentParticle] do
1354 begin
1355 X := fX;
1356 Y := fY;
1358 VelX := fVelX;
1359 VelY := fVelY;
1360 AccelX := 0.0;
1361 AccelY := 0.8;
1363 case DefColor of
1364 1: // Êðàñíûé
1365 begin
1366 Red := 155 + Random(9)*10;
1367 Green := Trunc(150*Random);
1368 Blue := Green;
1369 end;
1370 2: // Çåëåíûé
1371 begin
1372 Red := Trunc(150*Random);
1373 Green := 175 + Random(9)*10;
1374 Blue := Red;
1375 end;
1376 3: // Ñèíèé
1377 begin
1378 Red := Trunc(200*Random);
1379 Green := Red;
1380 Blue := 175 + Random(9)*10;
1381 end;
1382 4: // Ñâîé öâåò, ñâåòëåå
1383 begin
1384 Red := 20 + Random(19)*10;
1385 Green := Red;
1386 Blue := Red;
1387 Red := Min(Red + CR, 255);
1388 Green := Min(Green + CG, 255);
1389 Blue := Min(Blue + CB, 255);
1390 end;
1391 5: // Ñâîé öâåò, òåìíåå
1392 begin
1393 Red := 20 + Random(19)*10;
1394 Green := Red;
1395 Blue := Red;
1396 Red := Max(CR - Red, 0);
1397 Green := Max(CG - Green, 0);
1398 Blue := Max(CB - Blue, 0);
1399 end;
1400 else // Ñåðûé
1401 begin
1402 Red := 90 + Random(12)*10;
1403 Green := Red;
1404 Blue := Red;
1405 end;
1406 end;
1408 Alpha := 255;
1410 State := STATE_NORMAL;
1411 Time := 0;
1412 LiveTime := 60+Random(60);
1413 ParticleType := PARTICLE_WATER;
1415 {CorrectOffsets(CurrentParticle);}
1416 end;
1418 if CurrentParticle+2 > MaxParticles then
1419 CurrentParticle := 0
1420 else
1421 CurrentParticle := CurrentParticle+1;
1422 end;
1423 end;
1426 procedure g_GFX_Bubbles(fX, fY: Integer; Count: Word; DevX, DevY: Byte);
1427 var
1428 a: Integer;
1429 DevX1, DevX2,
1430 DevY1, DevY2: Byte;
1431 l, liquidx: Integer;
1432 begin
1433 l := Length(Particles);
1434 if l = 0 then
1435 Exit;
1436 if Count > l then
1437 Count := l;
1439 DevX1 := DevX div 2;
1440 DevX2 := DevX + 1;
1441 DevY1 := DevY div 2;
1442 DevY2 := DevY + 1;
1444 for a := 1 to Count do
1445 begin
1446 with Particles[CurrentParticle] do
1447 begin
1448 X := fX-DevX1+Random(DevX2);
1449 Y := fY-DevY1+Random(DevY2);
1451 if (X >= gMapInfo.Width) or (X <= 0) or
1452 (Y >= gMapInfo.Height) or (Y <= 0) then
1453 Continue;
1455 (*
1456 // don't spawn bubbles outside of the liquid
1457 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1458 Continue;
1459 *)
1461 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1462 // tracer will return `false` if we started outside of the liquid
1463 if not g_Map_TraceLiquidNonPrecise(X, Y, 0, -8, liquidx, liquidTopY) then continue;
1465 VelX := 0;
1466 VelY := -1-Random;
1467 AccelX := 0;
1468 AccelY := VelY/10;
1470 Red := 255;
1471 Green := 255;
1472 Blue := 255;
1473 Alpha := 255;
1475 State := STATE_NORMAL;
1476 Time := 0;
1477 LiveTime := 65535;
1478 ParticleType := PARTICLE_BUBBLES;
1480 {CorrectOffsets(CurrentParticle);}
1481 end;
1483 if CurrentParticle+2 > MaxParticles then
1484 CurrentParticle := 0
1485 else
1486 CurrentParticle := CurrentParticle+1;
1487 end;
1488 end;
1490 procedure g_GFX_SetMax(Count: Integer);
1491 var
1492 a: Integer;
1493 begin
1494 if Count > 50000 then Count := 50000;
1495 if (Count < 1) then Count := 1;
1497 SetLength(Particles, Count);
1498 for a := 0 to High(Particles) do Particles[a].die();
1499 MaxParticles := Count;
1500 //if CurrentParticle >= Count then
1501 CurrentParticle := 0;
1502 end;
1504 function g_GFX_GetMax(): Integer;
1505 begin
1506 Result := MaxParticles;
1507 end;
1509 function FindOnceAnim: DWORD;
1510 var
1511 i: Integer;
1512 begin
1513 if OnceAnims <> nil then
1514 for i := 0 to High(OnceAnims) do
1515 if OnceAnims[i].Animation = nil then
1516 begin
1517 Result := i;
1518 Exit;
1519 end;
1521 if OnceAnims = nil then
1522 begin
1523 SetLength(OnceAnims, 16);
1524 Result := 0;
1525 end
1526 else
1527 begin
1528 Result := High(OnceAnims) + 1;
1529 SetLength(OnceAnims, Length(OnceAnims) + 16);
1530 end;
1531 end;
1533 procedure g_GFX_OnceAnim(X, Y: Integer; Anim: TAnimation; AnimType: Byte = 0);
1534 var
1535 find_id: DWORD;
1536 begin
1537 if Anim = nil then
1538 Exit;
1540 find_id := FindOnceAnim();
1542 OnceAnims[find_id].AnimType := AnimType;
1543 OnceAnims[find_id].Animation := TAnimation.Create(Anim.FramesID, Anim.Loop, Anim.Speed);
1544 OnceAnims[find_id].Animation.Blending := Anim.Blending;
1545 OnceAnims[find_id].Animation.Alpha := Anim.Alpha;
1546 OnceAnims[find_id].X := X;
1547 OnceAnims[find_id].Y := Y;
1548 end;
1550 procedure g_GFX_Update();
1551 var
1552 a: Integer;
1553 w, h: Integer;
1554 len: Integer;
1555 begin
1556 if not gpart_dbg_enabled then exit;
1557 if Particles <> nil then
1558 begin
1559 w := gMapInfo.Width;
1560 h := gMapInfo.Height;
1562 len := High(Particles);
1564 for a := 0 to len do
1565 begin
1566 if Particles[a].alive then
1567 begin
1568 with Particles[a] do
1569 begin
1570 if (Time = LiveTime) then begin die(); continue; end;
1571 if (X+1 >= w) or (Y+1 >= h) or (X <= 0) or (Y <= 0) then begin die(); end;
1572 //if not alive then Continue;
1573 //e_WriteLog(Format('particle #%d: %d', [State, ParticleType]), MSG_NOTIFY);
1574 think();
1575 {CorrectOffsets(a);}
1576 end; // with
1577 end; // if
1578 end; // for
1579 end; // Particles <> nil
1581 if OnceAnims <> nil then
1582 begin
1583 for a := 0 to High(OnceAnims) do
1584 if OnceAnims[a].Animation <> nil then
1585 begin
1586 case OnceAnims[a].AnimType of
1587 ONCEANIM_SMOKE:
1588 begin
1589 if Random(3) = 0 then
1590 OnceAnims[a].X := OnceAnims[a].X-1+Random(3);
1591 if Random(2) = 0 then
1592 OnceAnims[a].Y := OnceAnims[a].Y-Random(2);
1593 end;
1594 end;
1596 if OnceAnims[a].Animation.Played then
1597 begin
1598 OnceAnims[a].Animation.Free();
1599 OnceAnims[a].Animation := nil;
1600 end
1601 else
1602 OnceAnims[a].Animation.Update();
1603 end;
1604 end;
1605 end;
1607 procedure g_GFX_Draw();
1608 var
1609 a, len: Integer;
1610 begin
1611 if Particles <> nil then
1612 begin
1613 glDisable(GL_TEXTURE_2D);
1614 glPointSize(2);
1616 glEnable(GL_BLEND);
1617 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1619 glBegin(GL_POINTS);
1621 len := High(Particles);
1623 for a := 0 to len do
1624 with Particles[a] do
1625 if alive and (X >= sX) and (Y >= sY) and (X <= sX+sWidth) and (sY <= sY+sHeight) then
1626 begin
1627 glColor4ub(Red, Green, Blue, Alpha);
1628 glVertex2i(X + offsetX, Y + offsetY);
1629 end;
1631 glEnd();
1633 glDisable(GL_BLEND);
1634 end;
1636 if OnceAnims <> nil then
1637 for a := 0 to High(OnceAnims) do
1638 if OnceAnims[a].Animation <> nil then
1639 with OnceAnims[a] do
1640 Animation.Draw(X, Y, M_NONE);
1641 end;
1643 end.