DEADSOFTWARE

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