DEADSOFTWARE

slightly faster (i hope) 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, xprofiler;
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;
92 // for blood
93 justSticked: Boolean;
94 stickEY: Integer;
95 // for all
96 onGround: Boolean;
97 awaken: Boolean;
99 //k8: sorry, i have to emulate virtual methods this way, 'cause i haet `Object`
101 procedure thinkerBlood ();
102 procedure thinkerSpark ();
103 procedure thinkerBubble ();
104 procedure thinkerWater ();
106 function alive (): Boolean; inline;
107 procedure die (); inline;
108 procedure think (); inline;
109 end;
111 TOnceAnim = record
112 AnimType: Byte;
113 X, Y: Integer;
114 Animation: TAnimation;
115 end;
117 const
118 PARTICLE_BLOOD = 0;
119 PARTICLE_SPARK = 1;
120 PARTICLE_BUBBLES = 2;
121 PARTICLE_WATER = 3;
122 STATE_FREE = 0;
123 STATE_NORMAL = 1;
124 STATE_STICK = 2;
126 var
127 Particles: array of TParticle;
128 OnceAnims: array of TOnceAnim;
129 MaxParticles: Integer;
130 CurrentParticle: Integer;
133 // ////////////////////////////////////////////////////////////////////////// //
134 function TParticle.alive (): Boolean; inline; begin result := (State <> STATE_FREE); end;
135 procedure TParticle.die (); inline; begin State := STATE_FREE; end;
137 procedure TParticle.think (); inline;
138 begin
139 case ParticleType of
140 PARTICLE_BLOOD: thinkerBlood();
141 PARTICLE_SPARK: thinkerSpark();
142 PARTICLE_BUBBLES: thinkerBubble();
143 PARTICLE_WATER: thinkerWater();
144 end;
145 end;
148 // ////////////////////////////////////////////////////////////////////////// //
149 function isBlockedAt (x, y: Integer): Boolean; inline;
150 begin
151 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
152 result := g_Map_HasAnyPanelAtPoint(x, y, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
153 end;
155 // ???
156 function isWallAt (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_WALL or PANEL_STEP));
160 end;
162 function isLiftUpAt (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_LIFTUP);
166 end;
168 function isLiftDownAt (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_LIFTDOWN);
172 end;
174 function isLiftLeftAt (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_LIFTLEFT);
178 end;
180 function isLiftRightAt (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_LIFTRIGHT);
184 end;
186 function isLiquidAt (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_WATER or PANEL_ACID1 or PANEL_ACID2));
190 end;
192 function isAnythingAt (x, y: Integer): Boolean; inline;
193 begin
194 if not gpart_dbg_phys_enabled then begin result := false; exit; end;
195 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));
196 end;
199 procedure g_Mark(x, y, Width, Height: Integer; t: Byte; st: Boolean);
200 {$IF not DEFINED(HAS_COLLIDE_BITMAP)}
201 var
202 part: PParticle;
203 f: Integer;
204 begin
205 for f := 0 to High(Particles) do
206 begin
207 part := @Particles[f];
208 if part.alive and (part.onGround or (not part.justSticked and (part.State = STATE_STICK))) and
209 (part.X >= x-2) and (part.Y >= y-2) and (part.X < x+Width+4) and (part.Y < y+Height+4) then
210 begin
211 // wakup this particle
213 if (part.ParticleType = PARTICLE_SPARK) then
214 begin
215 e_LogWritefln('waking up particle of type %s; justSticked=%s; onGround=%s; VelY=%s; AccelY=%s', [part.ParticleType, part.justSticked, part.onGround, part.VelY, part.AccelY]);
216 end;
218 part.justSticked := true; // so sticked state will be re-evaluated
219 if part.onGround then
220 begin
221 if (part.VelY = 0) then part.VelY := 0.1;
222 if (part.AccelY = 0) then part.AccelY := 0.5;
223 end;
224 part.onGround := false; // so onground state will be re-evaluated
225 part.awaken := true;
226 end;
227 end;
228 end;
229 {$ELSE}
230 var
231 yy, y2, xx, x2: Integer;
232 begin
233 if x < 0 then
234 begin
235 Width := Width + x;
236 x := 0;
237 end;
239 if Width < 0 then
240 Exit;
242 if y < 0 then
243 begin
244 Height := Height + y;
245 y := 0;
246 end;
248 if Height < 0 then
249 Exit;
251 if x > gMapInfo.Width then
252 Exit;
253 if y > gMapInfo.Height then
254 Exit;
256 y2 := y + Height - 1;
257 if y2 > gMapInfo.Height then
258 y2 := gMapInfo.Height;
260 x2 := x + Width - 1;
261 if x2 > gMapInfo.Width then
262 x2 := gMapInfo.Width;
264 if st then
265 begin // Óñòàíîâèòü ïðèçíàê
266 for yy := y to y2 do
267 for xx := x to x2 do
268 gCollideMap[yy][xx] := gCollideMap[yy][xx] or t;
269 end
270 else
271 begin // Óáðàòü ïðèçíàê
272 t := not t;
273 for yy := y to y2 do
274 for xx := x to x2 do
275 gCollideMap[yy][xx] := gCollideMap[yy][xx] and t;
276 end;
277 end;
278 {$ENDIF}
281 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
282 procedure CreateCollideMap();
283 var
284 a: Integer;
285 begin
286 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
287 SetLength(gCollideMap, gMapInfo.Height+1);
288 for a := 0 to High(gCollideMap) do
289 SetLength(gCollideMap[a], gMapInfo.Width+1);
291 if gWater <> nil then
292 begin
293 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 2/6', 0, True);
294 for a := 0 to High(gWater) do
295 with gWater[a] do
296 g_Mark(X, Y, Width, Height, MARK_WATER, True);
297 end;
299 if gAcid1 <> nil then
300 begin
301 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 3/6', 0, True);
302 for a := 0 to High(gAcid1) do
303 with gAcid1[a] do
304 g_Mark(X, Y, Width, Height, MARK_ACID, True);
305 end;
307 if gAcid2 <> nil then
308 begin
309 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 4/6', 0, True);
310 for a := 0 to High(gAcid2) do
311 with gAcid2[a] do
312 g_Mark(X, Y, Width, Height, MARK_ACID, True);
313 end;
315 if gLifts <> nil then
316 begin
317 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 5/6', 0, True);
318 for a := 0 to High(gLifts) do
319 with gLifts[a] do
320 begin
321 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
323 if LiftType = 0 then
324 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
325 else if LiftType = 1 then
326 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
327 else if LiftType = 2 then
328 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
329 else if LiftType = 3 then
330 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True)
331 end;
332 end;
334 if gWalls <> nil then
335 begin
336 g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 6/6', 0, True);
337 for a := 0 to High(gWalls) do
338 begin
339 if gWalls[a].Door then
340 begin
341 // Çàêðûòàÿ äâåðü:
342 if gWalls[a].Enabled then
343 with gWalls[a] do
344 g_Mark(X, Y, Width, Height, MARK_DOOR, True)
345 else // Îòêðûòàÿ äâåðü:
346 if gWalls[a].Enabled then
347 with gWalls[a] do
348 g_Mark(X, Y, Width, Height, MARK_DOOR, False);
349 end
350 else // Ñòåíà
351 with gWalls[a] do
352 g_Mark(X, Y, Width, Height, MARK_WALL, True);
353 end;
354 end;
355 end;
356 {$ENDIF}
359 procedure g_GFX_Init();
360 begin
361 //CreateCollideMap();
362 {$IFDEF HEADLESS}
363 gpart_dbg_enabled := False;
364 {$ENDIF}
365 end;
368 procedure g_GFX_Free();
369 var
370 a: Integer;
371 begin
372 Particles := nil;
373 SetLength(Particles, MaxParticles);
374 for a := 0 to High(Particles) do Particles[a].die();
375 CurrentParticle := 0;
377 if OnceAnims <> nil then
378 begin
379 for a := 0 to High(OnceAnims) do
380 OnceAnims[a].Animation.Free();
382 OnceAnims := nil;
383 end;
384 end;
387 // ////////////////////////////////////////////////////////////////////////// //
388 procedure TParticle.thinkerBlood ();
389 var
390 w, h: Integer;
391 dX, dY: SmallInt;
392 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
393 b: Integer;
394 s: ShortInt;
395 {$ELSE}
396 pan: TPanel;
397 ex, ey: Integer;
398 {$ENDIF}
399 begin
400 w := gMapInfo.Width;
401 h := gMapInfo.Height;
403 if gAdvBlood then
404 begin
405 if (State = STATE_STICK) then
406 begin
407 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
409 if (not ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)) and
410 (not ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)) and
411 (not ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)) and
412 (not ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED))
413 then
415 if (not isBlockedAt(X, Y-1)) and
416 (not isBlockedAt(X, Y+1)) and
417 (not isBlockedAt(X-1, Y)) and
418 (not isBlockedAt(X+1, Y))
419 {$ELSE}
420 if justSticked then
421 begin
422 if not mapGrid.traceOrthoRayWhileIn(ex, ey, X+stickDX, Y, X+stickDX, mapGrid.gridY0+mapGrid.gridHeight, GridTagWall or GridTagDoor or GridTagStep) then
423 begin
424 // îòëèïëà
425 State := STATE_NORMAL;
426 //e_LogWritefln('juststicked unsticked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s', [X, X+stickDX, stickDX, Y]);
427 end
428 else
429 begin
430 stickEY := ey+1;
431 if (nil <> g_Map_traceToNearest(X, Y, X, stickEY, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey)) then
432 begin
433 if (ey > stickEY) then stickEY := ey-1;
434 end;
435 justSticked := false;
436 //e_LogWritefln('juststicked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s; stickEY=%s', [X, X+stickDX, stickDX, Y, stickEY]);
437 end;
438 end;
439 if (State <> STATE_STICK) or (Y >= stickEY)
440 //if not g_Map_CollidePanel(X-1, Y-1, 3, 3, (PANEL_STEP or PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR))
441 {$ENDIF}
442 then
443 begin // Îòëèïëà - êàïàåò
444 VelY := 0.5;
445 AccelY := 0.15;
446 State := STATE_NORMAL;
447 end
448 else if (Random(200) = 100) then
449 begin // Ïðèëåïëåíà - íî âîçìîæíî ñòåêàåò
450 VelY := 0.5;
451 AccelY := 0.15;
452 exit;
453 end;
454 end;
456 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
457 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
458 begin
459 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
460 begin // Ëèôò ââåðõ
461 if (VelY > -4-Random(3)) then VelY -= 0.8;
462 if (abs(VelX) > 0.1) then VelX -= VelX/10.0;
463 VelX += (Random-Random)*0.2;
464 AccelY := 0.15;
465 end;
466 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
467 begin // Ïîòîê âëåâî
468 if (VelX > -8-Random(3)) then VelX -= 0.8;
469 AccelY := 0.15;
470 end;
471 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
472 begin // Ïîòîê âïðàâî
473 if (VelX < 8+Random(3)) then VelX += 0.8;
474 AccelY := 0.15;
475 end;
476 end;
477 {$ELSE}
478 pan := g_Map_PanelAtPoint(X, Y, GridTagLift);
479 if (pan <> nil) then
480 begin
481 if ((pan.PanelType and PANEL_LIFTUP) <> 0) then
482 begin
483 if (VelY > -4-Random(3)) then VelY -= 0.8;
484 if (abs(VelX) > 0.1) then VelX -= VelX/10.0;
485 VelX += (Random-Random)*0.2;
486 AccelY := 0.15;
487 end;
488 if ((pan.PanelType and PANEL_LIFTLEFT) <> 0) then
489 begin
490 if (VelX > -8-Random(3)) then VelX -= 0.8;
491 AccelY := 0.15;
492 end;
493 if ((pan.PanelType and PANEL_LIFTRIGHT) <> 0) then
494 begin
495 if (VelX < 8+Random(3)) then VelX += 0.8;
496 AccelY := 0.15;
497 end;
498 end;
499 {$ENDIF}
501 dX := Round(VelX);
502 dY := Round(VelY);
504 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
505 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
506 begin
507 if (State <> STATE_STICK) and
508 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
509 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
510 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
511 begin // Âèñèò â âîçäóõå - êàïàåò
512 VelY := 0.8;
513 AccelY := 0.5;
514 State := STATE_NORMAL;
515 end;
516 end;
517 {$ELSE}
518 if (State <> STATE_STICK) and (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
519 begin
520 // Âèñèò â âîçäóõå - êàïàåò
521 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
522 begin
523 VelY := 0.8;
524 AccelY := 0.5;
525 State := STATE_NORMAL;
526 end;
527 end;
528 {$ENDIF}
530 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
531 // horizontal
532 if (dX <> 0) then
533 begin
534 pan := g_Map_traceToNearest(X, Y, X+dX, Y, (GridTagWall or GridTagDoor or GridTagStep), @ex, @ey);
535 X := ex;
536 // free to ride?
537 if (pan <> nil) then
538 begin
539 // Ñòåíà/äâåðü
540 VelX := 0;
541 VelY := 0;
542 AccelX := 0;
543 AccelY := 0;
544 State := STATE_STICK;
545 justSticked := true;
546 if (dX > 0) then stickDX := 1 else stickDX := -1;
547 end;
548 if (X < 0) or (X >= w) then begin die(); exit; end;
549 end;
550 // vertical
551 if (dY <> 0) then
552 begin
553 if (dY < 0) or not onGround then
554 begin
555 pan := g_Map_traceToNearest(X, Y, X, Y+dY, (GridTagWall or GridTagDoor or GridTagStep), @ex, @ey);
556 Y := ey;
557 // free to ride?
558 if (pan <> nil) then
559 begin
560 // Ñòåíà/äâåðü
561 VelX := 0;
562 VelY := 0;
563 AccelX := 0;
564 AccelY := 0;
565 if (dY > 0) and (State <> STATE_STICK) then
566 begin
567 State := STATE_NORMAL;
568 end
569 else
570 begin
571 State := STATE_STICK;
572 if (g_Map_PanelAtPoint(X-1, Y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := -1
573 else if (g_Map_PanelAtPoint(X+1, Y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := 1
574 else stickDX := 0;
575 justSticked := true;
576 end;
577 end;
578 onGround := (VelY >= 0) and g_Map_HasAnyPanelAtPoint(X, Y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
579 end;
580 if (Y < 0) or (Y >= h) then begin die(); exit; end;
581 end;
582 {$ELSE}
583 // horizontal
584 if (dX <> 0) then
585 begin
586 if (dX > 0) then s := 1 else s := -1;
587 dX := Abs(dX);
588 for b := 1 to dX do
589 begin
590 if (X+s >= w) or (X+s <= 0) then begin die(); break; end;
591 //c := gCollideMap[Y, X+s];
592 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
593 begin // Ñòåíà/äâåðü
594 VelX := 0;
595 VelY := 0;
596 AccelX := 0;
597 AccelY := 0;
598 State := STATE_STICK;
599 justSticked := true;
600 break;
601 end;
602 X := X+s;
603 end;
604 end;
605 // vertical
606 if (dY <> 0) then
607 begin
608 if (dY > 0) then s := 1 else s := -1;
609 dY := Abs(dY);
610 for b := 1 to dY do
611 begin
612 if (Y+s >= h) or (Y+s <= 0) then begin die(); break; end;
613 //c := gCollideMap[Y+s, X];
614 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
615 begin // Ñòåíà/äâåðü
616 VelX := 0;
617 VelY := 0;
618 AccelX := 0;
619 AccelY := 0;
620 if (s > 0) and (State <> STATE_STICK) then State := STATE_NORMAL else State := STATE_STICK;
621 justSticked := (State = STATE_STICK);
622 break;
623 end;
624 Y := Y+s;
625 end;
626 end;
627 {$ENDIF}
628 end // if gAdvBlood
629 else
630 begin
631 dX := Round(VelX);
632 dY := Round(VelY);
633 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
634 begin // Ñòåíà/äâåðü/ãðàíèöà
635 die();
636 exit;
637 //VelX := 0;
638 //VelY := 0;
639 end
640 else
641 begin
642 Y += dY;
643 X += dX;
644 end;
645 end;
647 VelX += AccelX;
648 VelY += AccelY;
650 // Êðîâü ðàñòâîðÿåòñÿ â æèäêîñòè:
651 if isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
652 begin
653 Time += 1;
654 Alpha := 255-trunc((255.0*Time)/LiveTime);
655 end;
656 end;
660 // ////////////////////////////////////////////////////////////////////////// //
661 procedure TParticle.thinkerWater ();
662 var
663 dX, dY: SmallInt;
664 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
665 w, h: Integer;
666 b: Integer;
667 s: ShortInt;
668 {$ELSE}
669 pan: TPanel;
670 ex, ey: Integer;
671 {$ENDIF}
672 begin
673 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
674 w := gMapInfo.Width;
675 h := gMapInfo.Height;
676 {$ENDIF}
678 //TODO: trace wall end when water becomes stick
679 if (State = STATE_STICK) and (Random(30) = 15) then
680 begin // Ñòåêàåò/îòëèïàåò
681 VelY := 0.5;
682 AccelY := 0.15;
683 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
684 if (not isBlockedAt(X-1, Y) {ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)}) and
685 (not isBlockedAt(X+1, Y) {ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)}) then
686 State := STATE_NORMAL;
687 {$ELSE}
688 if (stickDX = 0) then
689 begin
690 // no walls around, drop
691 State := STATE_NORMAL;
692 end
693 else
694 begin
695 if justSticked then
696 begin
697 if not mapGrid.traceOrthoRayWhileIn(ex, ey, X+stickDX, Y, X+stickDX, mapGrid.gridY0+mapGrid.gridHeight, GridTagWall or GridTagDoor or GridTagStep) then
698 begin
699 // îòëèïëà
700 State := STATE_NORMAL;
701 //e_LogWritefln('juststicked unsticked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s', [X, X+stickDX, stickDX, Y]);
702 end
703 else
704 begin
705 stickEY := ey+1;
706 justSticked := false;
707 if (nil <> g_Map_traceToNearest(X, Y, X, stickEY, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey)) then
708 begin
709 if (ey > stickEY) then stickEY := ey-1;
710 end;
711 //e_LogWritefln('juststicked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s; stickEY=%s', [X, X+stickDX, stickDX, Y, stickEY]);
712 end;
713 end
714 else
715 begin
716 if (Y >= stickEY) then State := STATE_NORMAL;
717 end;
718 //if not g_Map_CollidePanel(X-1, Y-1, 3, 3, (PANEL_STEP or PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR))
719 end;
720 {$ENDIF}
721 exit;
722 end;
724 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
725 if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
726 begin
727 if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
728 begin // Ëèôò ââåðõ
729 if VelY > -4-Random(3) then
730 VelY := VelY - 0.8;
731 if Abs(VelX) > 0.1 then
732 VelX := VelX - VelX/10.0;
733 VelX := VelX + (Random-Random)*0.2;
734 AccelY := 0.15;
735 end;
736 if isLiftLeftAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTLEFT)} then
737 begin // Ïîòîê âëåâî
738 if VelX > -8-Random(3) then
739 VelX := VelX - 0.8;
740 AccelY := 0.15;
741 end;
742 if isLiftRightAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTRIGHT)} then
743 begin // Ïîòîê âïðàâî
744 if VelX < 8+Random(3) then
745 VelX := VelX + 0.8;
746 AccelY := 0.15;
747 end;
748 end;
749 {$ELSE}
750 pan := g_Map_PanelAtPoint(X, Y, (GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagLift));
751 if (pan <> nil) then
752 begin
753 if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
754 if ((pan.PanelType and PANEL_LIFTUP) <> 0) then
755 begin
756 if (VelY > -4-Random(3)) then VelY -= 0.8;
757 if (Abs(VelX) > 0.1) then VelX -= VelX/10.0;
758 VelX += (Random-Random)*0.2;
759 AccelY := 0.15;
760 end;
761 if ((pan.PanelType and PANEL_LIFTLEFT) <> 0) then
762 begin
763 if (VelX > -8-Random(3)) then VelX -= 0.8;
764 AccelY := 0.15;
765 end;
766 if ((pan.PanelType and PANEL_LIFTRIGHT) <> 0) then
767 begin
768 if (VelX < 8+Random(3)) then VelX += 0.8;
769 AccelY := 0.15;
770 end;
771 end;
772 {$ENDIF}
774 dX := Round(VelX);
775 dY := Round(VelY);
777 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
778 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
779 begin
780 if (State <> STATE_STICK) and
781 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
782 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
783 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
784 begin // Âèñèò â âîçäóõå - êàïàåò
785 VelY := 0.8;
786 AccelY := 0.5;
787 State := STATE_NORMAL;
788 end;
789 end;
790 {$ELSE}
791 if (State <> STATE_STICK) and (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
792 begin
793 // Âèñèò â âîçäóõå - êàïàåò
794 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
795 begin
796 VelY := 0.8;
797 AccelY := 0.5;
798 State := STATE_NORMAL;
799 end;
800 end;
801 {$ENDIF}
803 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
804 // horizontal
805 if (dX <> 0) then
806 begin
807 pan := g_Map_traceToNearest(X, Y, X+dX, Y, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
808 X := ex;
809 // free to ride?
810 if (pan <> nil) then
811 begin
812 // nope
813 if (dY > 0) and ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
814 // Ñòåíà/äâåðü?
815 if ((pan.tag and (GridTagWall or GridTagDoor or GridTagStep)) <> 0) then
816 begin
817 VelX := 0;
818 VelY := 0;
819 AccelX := 0;
820 AccelY := 0;
821 State := STATE_STICK;
822 justSticked := true;
823 if (dX > 0) then stickDX := 1 else stickDX := -1;
824 end;
825 end;
826 if (X < 0) or (X >= gMapInfo.Width) then begin die(); exit; end;
827 end;
828 // vertical
829 if (dY <> 0) then
830 begin
831 if (dY < 0) or not onGround then
832 begin
833 pan := g_Map_traceToNearest(X, Y, X, Y+dY, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
834 Y := ey;
835 // free to ride?
836 if (pan <> nil) then
837 begin
838 // nope
839 if (dY > 0) and ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
840 // Ñòåíà/äâåðü?
841 if ((pan.tag and (GridTagWall or GridTagDoor or GridTagStep)) <> 0) then
842 begin
843 VelX := 0;
844 VelY := 0;
845 AccelX := 0;
846 AccelY := 0;
847 if (dY > 0) and (State <> STATE_STICK) then
848 begin
849 State := STATE_NORMAL;
850 end
851 else
852 begin
853 State := STATE_STICK;
854 if (g_Map_PanelAtPoint(X-1, Y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := -1
855 else if (g_Map_PanelAtPoint(X+1, Y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := 1
856 else stickDX := 0;
857 justSticked := true;
858 end;
859 end;
860 end;
861 onGround := (VelY >= 0) and g_Map_HasAnyPanelAtPoint(X, Y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
862 end;
863 if (Y < 0) or (Y >= gMapInfo.Height) then begin die(); exit; end;
864 end;
865 {$ELSE}
866 // horizontal
867 if (dX <> 0) then
868 begin
869 if (dX > 0) then s := 1 else s := -1;
870 for b := 1 to Abs(dX) do
871 begin
872 // Ñáîêó ãðàíèöà?
873 if (X+s >= w) or (X+s <= 0) then begin die(); break;end;
874 //c := gCollideMap[Y, X+s];
875 // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò?
876 if isLiquidAt(X+s, Y) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then begin die(); break; end;
877 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
878 begin // Ñòåíà/äâåðü
879 VelX := 0;
880 VelY := 0;
881 AccelX := 0;
882 AccelY := 0;
883 State := STATE_STICK;
884 justSticked := true;
885 Break;
886 end;
887 X := X+s;
888 end;
889 end;
890 // vertical
891 if (dY <> 0) then
892 begin
893 if (dY > 0) then s := 1 else s := -1;
894 for b := 1 to Abs(dY) do
895 begin
896 // Ñíèçó/ñâåðõó ãðàíèöà
897 if (Y+s >= h) or (Y+s <= 0) then begin die(); break; end;
898 //c := gCollideMap[Y+s, X];
899 // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
900 if isLiquidAt(X, Y+s) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then begin die(); break; end;
901 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
902 begin // Ñòåíà/äâåðü
903 VelX := 0;
904 VelY := 0;
905 AccelX := 0;
906 AccelY := 0;
907 if (s > 0) and (State <> STATE_STICK) then State := STATE_NORMAL else State := STATE_STICK;
908 justSticked := (State = STATE_STICK);
909 break;
910 end;
911 Y := Y+s;
912 end;
913 end;
914 {$ENDIF}
916 VelX += AccelX;
917 VelY += AccelY;
919 Time += 1;
920 end;
923 // ////////////////////////////////////////////////////////////////////////// //
924 procedure TParticle.thinkerSpark ();
925 var
926 dX, dY: SmallInt;
927 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
928 b: Integer;
929 s: ShortInt;
930 {$ELSE}
931 pan: TPanel;
932 ex, ey: Integer;
933 {$ENDIF}
934 begin
935 dX := Round(VelX);
936 dY := Round(VelY);
938 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
939 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
940 begin
941 pan := g_Map_traceToNearest(X, Y-1, X, Y+1, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
942 end;
943 {$ELSE}
944 if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) and
945 (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
946 (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
947 (not isBlockedAt(X, Y+1) {ByteBool(gCollideMap[Y+1, X] and MARK_BLOCKED)}) then
948 begin // Âèñèò â âîçäóõå
949 VelY := 0.8;
950 AccelY := 0.5;
951 end;
952 {$ENDIF}
954 if (dX <> 0) then
955 begin
956 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
957 pan := g_Map_traceToNearest(X, Y, X+dX, Y, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
958 //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);
959 X := ex;
960 // free to ride?
961 if (pan <> nil) then
962 begin
963 // nope
964 if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
965 VelX := 0;
966 AccelX := 0;
967 end;
968 if (X < 0) or (X >= gMapInfo.Width) then begin die(); exit; end;
969 {$ELSE}
970 if (dX > 0) then s := 1 else s := -1;
971 dX := Abs(dX);
972 for b := 1 to dX do
973 begin
974 if (X+s >= gMapInfo.Width) or (X+s <= 0) then begin die(); break; end;
975 //c := gCollideMap[Y, X+s];
976 if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
977 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
978 VelX := 0;
979 AccelX := 0;
980 Break;
981 end
982 else // Ïóñòî:
983 if not isAnythingAt(X+s, Y) {c = MARK_FREE} then
984 X := X + s
985 else // Îñòàëüíîå:
986 begin
987 die();
988 break;
989 end;
990 end;
991 {$ENDIF}
992 end;
994 if (dY <> 0) then
995 begin
996 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
997 if (dY < 0) or not onGround then
998 begin
999 pan := g_Map_traceToNearest(X, Y, X, Y+dY, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
1000 Y := ey;
1002 if awaken then
1003 begin
1004 awaken := false;
1005 e_LogWritefln('AWAKEN particle of type %s; justSticked=%s; onGround=%s; VelY=%s; AccelY=%s; Y=%s; ey=%s', [ParticleType, justSticked, onGround, VelY, AccelY, Y, ey]);
1006 end;
1008 // free to ride?
1009 if (pan <> nil) then
1010 begin
1011 // nope
1012 if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
1013 if (dY < 0) then
1014 begin
1015 VelY := -VelY;
1016 AccelY := abs(AccelY);
1017 end
1018 else
1019 begin
1020 VelX := 0;
1021 AccelX := 0;
1022 VelY := 0;
1023 AccelY := 0.8;
1024 end;
1025 end;
1026 onGround := (VelY >= 0) and g_Map_HasAnyPanelAtPoint(X, Y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1027 end;
1028 if (Y < 0) or (Y >= gMapInfo.Height) then begin die(); exit; end;
1029 {$ELSE}
1030 if (dY > 0) then s := 1 else s := -1;
1031 dY := Abs(dY);
1032 for b := 1 to dY do
1033 begin
1034 if (Y+s >= gMapInfo.Height) or (Y+s <= 0) then begin die(); break; end;
1035 //c := gCollideMap[Y+s, X];
1036 if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
1037 begin // Ñòåíà/äâåðü - ïàäàåò âåðòèêàëüíî
1038 if s < 0 then
1039 begin
1040 VelY := -VelY;
1041 AccelY := Abs(AccelY);
1042 end
1043 else // Èëè íå ïàäàåò
1044 begin
1045 VelX := 0;
1046 AccelX := 0;
1047 VelY := 0;
1048 AccelY := 0.8;
1049 end;
1051 Break;
1052 end
1053 else // Ïóñòî:
1054 if not isAnythingAt(X, Y+s) {c = MARK_FREE} then
1055 Y := Y + s
1056 else // Îñàëüíîå:
1057 begin
1058 die();
1059 break;
1060 end;
1061 end;
1062 {$ENDIF}
1063 end;
1065 if (VelX <> 0.0) then VelX += AccelX;
1067 if (VelY <> 0.0) then
1068 begin
1069 if (AccelY < 10) then AccelY += 0.08;
1070 VelY += AccelY;
1071 end;
1073 Time += 1;
1074 end;
1076 // ////////////////////////////////////////////////////////////////////////// //
1077 procedure TParticle.thinkerBubble ();
1078 var
1079 h: Integer;
1080 dY: SmallInt;
1081 b: Integer;
1082 s: ShortInt;
1083 begin
1084 h := gMapInfo.Height;
1086 dY := Round(VelY);
1088 if dY <> 0 then
1089 begin
1090 if dY > 0 then
1091 s := 1
1092 else
1093 s := -1;
1095 for b := 1 to Abs(dY) do
1096 begin
1097 if (Y+s >= h) or (Y+s <= 0) then begin die(); break; end;
1099 (*
1100 if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
1101 begin // Óæå íå æèäêîñòü
1102 State := STATE_FREE;
1103 Break;
1104 end;
1105 *)
1106 // we traced liquid before, so don't bother checking
1107 if (Y+s <= liquidTopY) then begin die(); break; end;
1109 Y := Y+s;
1110 end;
1111 end;
1113 if VelY > -4 then
1114 VelY := VelY + AccelY;
1116 Time := Time + 1;
1117 end;
1120 // ////////////////////////////////////////////////////////////////////////// //
1121 procedure g_GFX_SparkVel (fX, fY: Integer; Count: Word; VX, VY: Integer; DevX, DevY: Byte);
1122 var
1123 a: Integer;
1124 DevX1, DevX2,
1125 DevY1, DevY2: Byte;
1126 l: Integer;
1127 begin
1128 if not gpart_dbg_enabled then Exit;
1129 l := Length(Particles);
1130 if l = 0 then exit;
1131 if Count > l then Count := l;
1133 DevX1 := DevX div 2;
1134 DevX2 := DevX + 1;
1135 DevY1 := DevY div 2;
1136 DevY2 := DevY + 1;
1138 for a := 1 to Count do
1139 begin
1140 with Particles[CurrentParticle] do
1141 begin
1142 X := fX-DevX1+Random(DevX2);
1143 Y := fY-DevY1+Random(DevY2);
1145 VelX := VX + (Random-Random)*3;
1146 VelY := VY + (Random-Random)*3;
1148 if VelY > -4 then
1149 if VelY-4 < -4 then
1150 VelY := -4
1151 else
1152 VelY := VelY-4;
1154 AccelX := -Sign(VelX)*Random/100;
1155 AccelY := 0.8;
1157 Red := 255;
1158 Green := 100+Random(155);
1159 Blue := 64;
1160 Alpha := 255;
1162 State := STATE_NORMAL;
1163 Time := 0;
1164 LiveTime := 30+Random(60);
1165 ParticleType := PARTICLE_SPARK;
1166 justSticked := false;
1167 onGround := (VelY >= 0) and g_Map_HasAnyPanelAtPoint(X, Y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1168 awaken := false;
1169 end;
1171 if CurrentParticle+2 > MaxParticles then
1172 CurrentParticle := 0
1173 else
1174 CurrentParticle := CurrentParticle+1;
1175 end;
1176 end;
1179 procedure g_GFX_Blood(fX, fY: Integer; Count: Word; vx, vy: Integer;
1180 DevX, DevY: Word; CR, CG, CB: Byte; Kind: Byte = BLOOD_NORMAL);
1181 var
1182 a: Integer;
1183 DevX1, DevX2,
1184 DevY1, DevY2: Word;
1185 l: Integer;
1186 CRnd: Byte;
1187 CC: SmallInt;
1188 begin
1189 if not gpart_dbg_enabled then Exit;
1190 if Kind = BLOOD_SPARKS then
1191 begin
1192 g_GFX_SparkVel(fX, fY, 2 + Random(2), -VX div 2, -VY div 2, DevX, DevY);
1193 Exit;
1194 end;
1195 l := Length(Particles);
1196 if l = 0 then
1197 Exit;
1198 if Count > l then
1199 Count := l;
1201 DevX1 := DevX div 2;
1202 DevX2 := DevX + 1;
1203 DevY1 := DevY div 2;
1204 DevY2 := DevY + 1;
1206 for a := 1 to Count do
1207 begin
1208 with Particles[CurrentParticle] do
1209 begin
1210 X := fX - DevX1 + Random(DevX2);
1211 Y := fY - DevY1 + Random(DevY2);
1214 if (X < 0) or (X > gMapInfo.Width-1) or
1215 (Y < 0) or (Y > gMapInfo.Height-1) or
1216 ByteBool(gCollideMap[Y, X] and MARK_WALL) then
1217 Continue;
1219 if isWallAt(X, Y) then continue;
1221 VelX := vx + (Random-Random)*3;
1222 VelY := vy + (Random-Random)*3;
1224 if VelY > -4 then
1225 if VelY-4 < -4 then
1226 VelY := -4
1227 else
1228 VelY := VelY-4;
1230 AccelX := -Sign(VelX)*Random/100;
1231 AccelY := 0.8;
1233 CRnd := 20*Random(6);
1234 if CR > 0 then
1235 begin
1236 CC := CR + CRnd - 50;
1237 if CC < 0 then CC := 0;
1238 if CC > 255 then CC := 255;
1239 Red := CC;
1240 end else
1241 Red := 0;
1242 if CG > 0 then
1243 begin
1244 CC := CG + CRnd - 50;
1245 if CC < 0 then CC := 0;
1246 if CC > 255 then CC := 255;
1247 Green := CC;
1248 end else
1249 Green := 0;
1250 if CB > 0 then
1251 begin
1252 CC := CB + CRnd - 50;
1253 if CC < 0 then CC := 0;
1254 if CC > 255 then CC := 255;
1255 Blue := CC;
1256 end else
1257 Blue := 0;
1259 Alpha := 255;
1261 State := STATE_NORMAL;
1262 Time := 0;
1263 LiveTime := 120+Random(40);
1264 ParticleType := PARTICLE_BLOOD;
1265 justSticked := false;
1266 onGround := (VelY >= 0) and g_Map_HasAnyPanelAtPoint(X, Y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1267 awaken := false;
1268 //stickEY := 0;
1269 end;
1271 if CurrentParticle >= MaxParticles-1 then
1272 CurrentParticle := 0
1273 else
1274 CurrentParticle := CurrentParticle+1;
1275 end;
1276 end;
1279 procedure g_GFX_Spark(fX, fY: Integer; Count: Word; Angle: SmallInt; DevX, DevY: Byte);
1280 var
1281 a: Integer;
1282 b: Single;
1283 DevX1, DevX2,
1284 DevY1, DevY2: Byte;
1285 BaseVelX, BaseVelY: Single;
1286 l: Integer;
1287 begin
1288 if not gpart_dbg_enabled then Exit;
1289 l := Length(Particles);
1290 if l = 0 then
1291 Exit;
1292 if Count > l then
1293 Count := l;
1295 Angle := 360 - Angle;
1297 DevX1 := DevX div 2;
1298 DevX2 := DevX + 1;
1299 DevY1 := DevY div 2;
1300 DevY2 := DevY + 1;
1302 b := DegToRad(Angle);
1303 BaseVelX := cos(b);
1304 BaseVelY := 1.6*sin(b);
1305 if Abs(BaseVelX) < 0.01 then
1306 BaseVelX := 0.0;
1307 if Abs(BaseVelY) < 0.01 then
1308 BaseVelY := 0.0;
1309 for a := 1 to Count do
1310 begin
1311 with Particles[CurrentParticle] do
1312 begin
1313 X := fX-DevX1+Random(DevX2);
1314 Y := fY-DevY1+Random(DevY2);
1316 VelX := BaseVelX*Random;
1317 VelY := BaseVelY-Random;
1318 AccelX := VelX/3.0;
1319 AccelY := VelY/5.0;
1321 Red := 255;
1322 Green := 100+Random(155);
1323 Blue := 64;
1324 Alpha := 255;
1326 State := STATE_NORMAL;
1327 Time := 0;
1328 LiveTime := 30+Random(60);
1329 ParticleType := PARTICLE_SPARK;
1330 justSticked := false;
1331 onGround := (VelY >= 0) and g_Map_HasAnyPanelAtPoint(X, Y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1332 awaken := false;
1333 end;
1335 if CurrentParticle+2 > MaxParticles then
1336 CurrentParticle := 0
1337 else
1338 CurrentParticle := CurrentParticle+1;
1339 end;
1340 end;
1342 procedure g_GFX_Water(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DevX, DevY, Color: Byte);
1343 var
1344 a: Integer;
1345 DevX1, DevX2,
1346 DevY1, DevY2: Byte;
1347 l: Integer;
1348 begin
1349 if not gpart_dbg_enabled then Exit;
1350 l := Length(Particles);
1351 if l = 0 then
1352 Exit;
1353 if Count > l then
1354 Count := l;
1356 if Abs(fVelX) < 3.0 then
1357 fVelX := 3.0 - 6.0*Random;
1359 DevX1 := DevX div 2;
1360 DevX2 := DevX + 1;
1361 DevY1 := DevY div 2;
1362 DevY2 := DevY + 1;
1364 for a := 1 to Count do
1365 begin
1366 with Particles[CurrentParticle] do
1367 begin
1368 X := fX-DevX1+Random(DevX2);
1369 Y := fY-DevY1+Random(DevY2);
1371 if Abs(fVelX) < 0.5 then
1372 VelX := 1.0 - 2.0*Random
1373 else
1374 VelX := fVelX*Random;
1375 if Random(10) < 7 then
1376 VelX := -VelX;
1377 VelY := fVelY*Random;
1378 AccelX := 0.0;
1379 AccelY := 0.8;
1381 case Color of
1382 1: // Êðàñíûé
1383 begin
1384 Red := 155 + Random(9)*10;
1385 Green := Trunc(150*Random);
1386 Blue := Green;
1387 end;
1388 2: // Çåëåíûé
1389 begin
1390 Red := Trunc(150*Random);
1391 Green := 175 + Random(9)*10;
1392 Blue := Red;
1393 end;
1394 3: // Ñèíèé
1395 begin
1396 Red := Trunc(200*Random);
1397 Green := Red;
1398 Blue := 175 + Random(9)*10;
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;
1414 justSticked := false;
1415 onGround := (VelY >= 0) and g_Map_HasAnyPanelAtPoint(X, Y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1416 awaken := false;
1417 end;
1419 if CurrentParticle+2 > MaxParticles then
1420 CurrentParticle := 0
1421 else
1422 CurrentParticle := CurrentParticle+1;
1423 end;
1424 end;
1426 procedure g_GFX_SimpleWater(fX, fY: Integer; Count: Word; fVelX, fVelY: Single; DefColor, CR, CG, CB: Byte);
1427 var
1428 a: Integer;
1429 l: Integer;
1430 begin
1431 if not gpart_dbg_enabled then Exit;
1432 l := Length(Particles);
1433 if l = 0 then
1434 Exit;
1435 if Count > l then
1436 Count := l;
1438 for a := 1 to Count do
1439 begin
1440 with Particles[CurrentParticle] do
1441 begin
1442 X := fX;
1443 Y := fY;
1445 VelX := fVelX;
1446 VelY := fVelY;
1447 AccelX := 0.0;
1448 AccelY := 0.8;
1450 case DefColor of
1451 1: // Êðàñíûé
1452 begin
1453 Red := 155 + Random(9)*10;
1454 Green := Trunc(150*Random);
1455 Blue := Green;
1456 end;
1457 2: // Çåëåíûé
1458 begin
1459 Red := Trunc(150*Random);
1460 Green := 175 + Random(9)*10;
1461 Blue := Red;
1462 end;
1463 3: // Ñèíèé
1464 begin
1465 Red := Trunc(200*Random);
1466 Green := Red;
1467 Blue := 175 + Random(9)*10;
1468 end;
1469 4: // Ñâîé öâåò, ñâåòëåå
1470 begin
1471 Red := 20 + Random(19)*10;
1472 Green := Red;
1473 Blue := Red;
1474 Red := Min(Red + CR, 255);
1475 Green := Min(Green + CG, 255);
1476 Blue := Min(Blue + CB, 255);
1477 end;
1478 5: // Ñâîé öâåò, òåìíåå
1479 begin
1480 Red := 20 + Random(19)*10;
1481 Green := Red;
1482 Blue := Red;
1483 Red := Max(CR - Red, 0);
1484 Green := Max(CG - Green, 0);
1485 Blue := Max(CB - Blue, 0);
1486 end;
1487 else // Ñåðûé
1488 begin
1489 Red := 90 + Random(12)*10;
1490 Green := Red;
1491 Blue := Red;
1492 end;
1493 end;
1495 Alpha := 255;
1497 State := STATE_NORMAL;
1498 Time := 0;
1499 LiveTime := 60+Random(60);
1500 ParticleType := PARTICLE_WATER;
1501 justSticked := false;
1502 onGround := (VelY >= 0) and g_Map_HasAnyPanelAtPoint(X, Y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1503 awaken := false;
1504 end;
1506 if CurrentParticle+2 > MaxParticles then
1507 CurrentParticle := 0
1508 else
1509 CurrentParticle := CurrentParticle+1;
1510 end;
1511 end;
1514 {.$DEFINE D2F_DEBUG_BUBBLES}
1515 procedure g_GFX_Bubbles(fX, fY: Integer; Count: Word; DevX, DevY: Byte);
1516 var
1517 a: Integer;
1518 DevX1, DevX2,
1519 DevY1, DevY2: Byte;
1520 l, liquidx: Integer;
1521 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1522 stt: UInt64;
1523 nptr, ptr: Boolean;
1524 {$ENDIF}
1525 begin
1526 if not gpart_dbg_enabled then Exit;
1527 l := Length(Particles);
1528 if l = 0 then
1529 Exit;
1530 if Count > l then
1531 Count := l;
1533 DevX1 := DevX div 2;
1534 DevX2 := DevX + 1;
1535 DevY1 := DevY div 2;
1536 DevY2 := DevY + 1;
1538 for a := 1 to Count do
1539 begin
1540 with Particles[CurrentParticle] do
1541 begin
1542 X := fX-DevX1+Random(DevX2);
1543 Y := fY-DevY1+Random(DevY2);
1545 if (X >= gMapInfo.Width) or (X <= 0) or
1546 (Y >= gMapInfo.Height) or (Y <= 0) then
1547 Continue;
1549 (*
1550 // don't spawn bubbles outside of the liquid
1551 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1552 Continue;
1553 *)
1555 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1556 // tracer will return `false` if we started outside of the liquid
1558 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1559 stt := curTimeMicro();
1560 ptr := mapGrid.traceOrthoRayWhileIn(liquidx, liquidTopY, X, Y, X, 0, GridTagWater or GridTagAcid1 or GridTagAcid2);
1561 stt := curTimeMicro()-stt;
1562 e_LogWritefln('traceOrthoRayWhileIn: time=%s (%s); liquidTopY=%s', [Integer(stt), ptr, liquidTopY]);
1563 //
1564 stt := curTimeMicro();
1565 nptr := g_Map_TraceLiquidNonPrecise(X, Y, 0, -8, liquidx, liquidTopY);
1566 stt := curTimeMicro()-stt;
1567 e_LogWritefln('g_Map_TraceLiquidNonPrecise: time=%s (%s); liquidTopY=%s', [Integer(stt), nptr, liquidTopY]);
1568 if not nptr then continue;
1569 {$ELSE}
1570 if not g_Map_TraceLiquidNonPrecise(X, Y, 0, -8, liquidx, liquidTopY) then continue;
1571 {$ENDIF}
1573 VelX := 0;
1574 VelY := -1-Random;
1575 AccelX := 0;
1576 AccelY := VelY/10;
1578 Red := 255;
1579 Green := 255;
1580 Blue := 255;
1581 Alpha := 255;
1583 State := STATE_NORMAL;
1584 Time := 0;
1585 LiveTime := 65535;
1586 ParticleType := PARTICLE_BUBBLES;
1587 justSticked := false;
1588 onGround := (VelY >= 0) and g_Map_HasAnyPanelAtPoint(X, Y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1589 awaken := false;
1590 end;
1592 if CurrentParticle+2 > MaxParticles then
1593 CurrentParticle := 0
1594 else
1595 CurrentParticle := CurrentParticle+1;
1596 end;
1597 end;
1599 procedure g_GFX_SetMax(Count: Integer);
1600 var
1601 a: Integer;
1602 begin
1603 if Count > 50000 then Count := 50000;
1604 if (Count < 1) then Count := 1;
1606 SetLength(Particles, Count);
1607 for a := 0 to High(Particles) do Particles[a].die();
1608 MaxParticles := Count;
1609 //if CurrentParticle >= Count then
1610 CurrentParticle := 0;
1611 end;
1613 function g_GFX_GetMax(): Integer;
1614 begin
1615 Result := MaxParticles;
1616 end;
1618 function FindOnceAnim: DWORD;
1619 var
1620 i: Integer;
1621 begin
1622 if OnceAnims <> nil then
1623 for i := 0 to High(OnceAnims) do
1624 if OnceAnims[i].Animation = nil then
1625 begin
1626 Result := i;
1627 Exit;
1628 end;
1630 if OnceAnims = nil then
1631 begin
1632 SetLength(OnceAnims, 16);
1633 Result := 0;
1634 end
1635 else
1636 begin
1637 Result := High(OnceAnims) + 1;
1638 SetLength(OnceAnims, Length(OnceAnims) + 16);
1639 end;
1640 end;
1642 procedure g_GFX_OnceAnim(X, Y: Integer; Anim: TAnimation; AnimType: Byte = 0);
1643 var
1644 find_id: DWORD;
1645 begin
1646 if not gpart_dbg_enabled then Exit;
1647 if Anim = nil then
1648 Exit;
1650 find_id := FindOnceAnim();
1652 OnceAnims[find_id].AnimType := AnimType;
1653 OnceAnims[find_id].Animation := TAnimation.Create(Anim.FramesID, Anim.Loop, Anim.Speed);
1654 OnceAnims[find_id].Animation.Blending := Anim.Blending;
1655 OnceAnims[find_id].Animation.Alpha := Anim.Alpha;
1656 OnceAnims[find_id].X := X;
1657 OnceAnims[find_id].Y := Y;
1658 end;
1660 procedure g_GFX_Update();
1661 var
1662 a: Integer;
1663 w, h: Integer;
1664 len: Integer;
1665 begin
1666 if not gpart_dbg_enabled then exit;
1667 if Particles <> nil then
1668 begin
1669 w := gMapInfo.Width;
1670 h := gMapInfo.Height;
1672 len := High(Particles);
1674 for a := 0 to len do
1675 begin
1676 if Particles[a].alive then
1677 begin
1678 with Particles[a] do
1679 begin
1680 if (Time = LiveTime) then begin die(); continue; end;
1681 if (X+1 >= w) or (Y+1 >= h) or (X <= 0) or (Y <= 0) then begin die(); end;
1682 //if not alive then Continue;
1683 //e_WriteLog(Format('particle #%d: %d', [State, ParticleType]), MSG_NOTIFY);
1684 think();
1685 end; // with
1686 end; // if
1687 end; // for
1688 end; // Particles <> nil
1690 if OnceAnims <> nil then
1691 begin
1692 for a := 0 to High(OnceAnims) do
1693 if OnceAnims[a].Animation <> nil then
1694 begin
1695 case OnceAnims[a].AnimType of
1696 ONCEANIM_SMOKE:
1697 begin
1698 if Random(3) = 0 then
1699 OnceAnims[a].X := OnceAnims[a].X-1+Random(3);
1700 if Random(2) = 0 then
1701 OnceAnims[a].Y := OnceAnims[a].Y-Random(2);
1702 end;
1703 end;
1705 if OnceAnims[a].Animation.Played then
1706 begin
1707 OnceAnims[a].Animation.Free();
1708 OnceAnims[a].Animation := nil;
1709 end
1710 else
1711 OnceAnims[a].Animation.Update();
1712 end;
1713 end;
1714 end;
1716 procedure g_GFX_Draw();
1717 var
1718 a, len: Integer;
1719 begin
1720 if Particles <> nil then
1721 begin
1722 glDisable(GL_TEXTURE_2D);
1723 glPointSize(2);
1725 glEnable(GL_BLEND);
1726 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1728 glBegin(GL_POINTS);
1730 len := High(Particles);
1732 for a := 0 to len do
1733 with Particles[a] do
1734 if alive and (X >= sX) and (Y >= sY) and (X <= sX+sWidth) and (sY <= sY+sHeight) then
1735 begin
1736 glColor4ub(Red, Green, Blue, Alpha);
1737 glVertex2i(X + offsetX, Y + offsetY);
1738 end;
1740 glEnd();
1742 glDisable(GL_BLEND);
1743 end;
1745 if OnceAnims <> nil then
1746 for a := 0 to High(OnceAnims) do
1747 if OnceAnims[a].Animation <> nil then
1748 with OnceAnims[a] do
1749 Animation.Draw(X, Y, M_NONE);
1750 end;
1752 end.