DEADSOFTWARE

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