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}
20 interface
22 uses
25 const
52 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, Color: Byte);
53 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
67 var
72 implementation
74 uses
80 const
84 type
88 // Wall: floorY is just before floor
89 // LiquidIn: floorY is liquid *start* (i.e. just in a liquid)
90 // LiquidOut: floorY is liquid *end* (i.e. just out of a liquid)
93 // note: this MUST be record, so we can keep it in
94 // dynamic array and has sequential memory access pattern
106 // for bubbles
108 // for water
116 // for all
121 //k8: sorry, i have to emulate virtual methods this way, 'cause i haet `Object`
148 var
153 // awakeMap has one bit for each map grid cell; on g_Mark,
154 // corresponding bits will be set, and in `think()` all particles
155 // in marked cells will be awaken
162 // ////////////////////////////////////////////////////////////////////////// //
163 // HACK! using mapgrid
165 begin
171 begin
179 {$IF DEFINED(D2F_DEBUG)}
180 e_LogWritefln('particle awake map: %sx%s (for grid of size %sx%s)', [awakeMapW, awakeMapH, mapGrid.gridWidth, mapGrid.gridHeight]);
181 {$ENDIF}
187 begin
191 begin
192 {$IF DEFINED(D2F_DEBUG)}
194 {$ENDIF}
196 end
197 else
198 begin
205 var
207 begin
211 begin
212 {$IF DEFINED(D2F_DEBUG)}
214 {$ENDIF}
221 // ////////////////////////////////////////////////////////////////////////// //
226 begin
231 begin
233 begin
234 // wakeup this particle
235 {
236 if (part.ParticleType = PARTICLE_SPARK) then
237 begin
238 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]);
239 end;
240 }
243 begin
253 // remove velocities and acceleration
255 begin
256 // stop right there, you criminal scum!
264 // switch to sleep mode
266 begin
273 var
276 begin
278 // stuck in the wall? rescan, 'cause it can be mplat
280 begin
283 begin
284 // either in a wall, or in a liquid
286 begin
287 // we are in the wall, wtf?!
292 exit;
294 // we are in liquid, trace to liquid end
298 // are we in a liquid?
300 begin
301 // trace out of the liquid
302 //env := TEnvType.ELiquid;
304 //e_LogWritefln('tracing out of a liquid; floorY=%s; y=%s', [floorY, y]);
307 //e_LogWritefln(' traced out of a liquid; floorY=%s; y=%s', [floorY, y]);
308 end
309 else
310 begin
311 // in the air
313 //env := TEnvType.EAir;
314 pan := g_Map_traceToNearest(x, y, x, g_Map_MaxY, (GridTagObstacle or GridTagLiquid), @ex, @floorY);
316 begin
317 // wall or liquid
319 begin
320 // wall
322 end
323 else
324 begin
325 // liquid
329 end
330 else
331 begin
332 // out of the level; assume wall, but it doesn't really matter
341 var
343 begin
346 begin
353 begin
354 // awake sleeping particle, if necessary
358 //TPartType.Spark: thinkerSpark();
359 //TPartType.Bubbles: thinkerBubble();
360 //TPartType.Water: thinkerWater();
365 // ////////////////////////////////////////////////////////////////////////// //
368 begin
376 var
378 begin
382 // find next floor transition
384 // find `wallEndY`
385 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
386 //if (wallEndY > floorY) then wallEndY := floorY; // just in case
390 begin
397 // `true`: didn't, get outa thinker
399 begin
404 // `true`: affected by air stream
406 var
408 begin
412 begin
414 begin
419 end
421 begin
424 end
426 begin
429 end
430 else
431 begin
434 // awake
439 // switch to freefall mode
441 begin
448 begin
451 begin
454 end
455 else
456 begin
462 label
463 _done;
464 var
468 begin
470 begin
471 // still check for air streams when sleeping
472 if (state = TPartState.Sleeping) then begin checkAirStreams(); goto _done; end; // so blood will dissolve
474 // process stuck particles
476 begin
477 // stuck to a ceiling?
479 begin
480 // yeah, stuck to a ceiling
482 // dropped from a ceiling?
484 begin
485 // yep
489 end
490 else
491 begin
492 // otherwise, try to drip
495 end
496 else
497 begin
498 // stuck to a wall
500 // floor transition?
502 begin
505 begin
510 begin
511 // rescan, so we'll know when we'll exit the liquid
515 begin
516 // rescan, so we'll know when we'll enter something interesting
522 // wall transition?
524 begin
525 // just unstuck from the wall, switch to freefall mode
527 end
528 else
529 begin
530 // otherwise, try to drip
534 // nope, process as usual
537 // it is important to have it here
541 // gravity, if not stuck
543 begin
545 // floor transition?
547 begin
550 begin
551 // nothing to do
554 begin
555 // rescan, so we'll know when we'll exit the liquid
560 begin
561 // rescan, so we'll know when we'll enter something interesting
563 if (floorType <> TFloorType.Wall) or (floorY <> y) then applyGravity(floorType = TFloorType.LiquidIn);
566 end
567 else
568 begin
569 // looks like we're in the air
574 // trace movement
576 begin
577 // has some horizontal velocity
584 begin
585 // we stuck
586 // the only case when we can have both ceiling and wall is corner; stick to wall in this case
587 // check environment (air/liquid)
588 if (g_Map_PanelAtPoint(x, y, GridTagLiquid) <> nil) then env := TEnvType.ELiquid else env := TEnvType.EAir;
589 // check if we stuck to a wall
592 begin
593 // stuck to a wall
595 end
596 else
597 begin
598 // stuck to a ceiling
602 end
604 begin
605 // has only vertical velocity
607 begin
608 // flying up
612 // environmend didn't changed
613 end
614 else
615 begin
617 begin
618 // falling down
621 //e_LogWritefln('floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
623 begin
624 // floor transition
627 //e_LogWritefln(' HIT FLOORY: floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
630 begin
631 // environmend didn't changed
636 begin
637 // we're entered the liquid
639 // rescan, so we'll know when we'll exit the liquid
643 begin
644 // we're exited the liquid
646 // rescan, so we'll know when we'll enter something interesting
649 begin
655 end
656 else
657 begin
664 else
665 begin
666 // simple blood
671 if (x > g_Map_MaxX) or (y > g_Map_MaxY) or (x < g_Map_MinX) or (y < g_Map_MinY) then begin die(); exit; end;
675 _done:
679 // blood will dissolve in other liquids
681 begin
691 procedure g_GFX_SparkVel (fX, fY: Integer; count: Word; VX, VY: Integer; devX, devY: Byte); forward;
697 begin
699 begin
704 end
705 else
706 begin
711 var
717 begin
721 begin
723 exit;
736 begin
738 begin
742 // check for level bounds
745 // in what environment we are starting in?
748 begin
749 // either in a wall, or in a liquid
752 end
753 else
754 begin
762 begin
789 // ////////////////////////////////////////////////////////////////////////// //
791 begin
793 result := g_Map_HasAnyPanelAtPoint(x, y, (PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR or PANEL_STEP));
796 // ???
798 begin
804 begin
810 begin
816 begin
822 begin
828 begin
834 begin
836 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));
840 // ////////////////////////////////////////////////////////////////////////// //
842 var
844 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
848 {$ELSE}
851 {$ENDIF}
852 begin
853 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
856 {$ENDIF}
858 //TODO: trace wall end when water becomes stick
863 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
867 {$ELSE}
869 begin
870 // no walls around, drop
872 end
873 else
874 begin
876 begin
877 if not mapGrid.traceOrthoRayWhileIn(ex, ey, x+stickDX, y, x+stickDX, mapGrid.gridY0+mapGrid.gridHeight, GridTagWall or GridTagDoor or GridTagStep) then
878 begin
879 // îòëèïëà
881 //e_LogWritefln('juststicked unsticked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s', [X, X+stickDX, stickDX, Y]);
882 end
883 else
884 begin
887 if (nil <> g_Map_traceToNearest(x, y, x, stickEY, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey)) then
888 begin
891 //e_LogWritefln('juststicked: X=%s; X+stickDX=%s; stickDX=%s; Y=%s; stickEY=%s', [X, X+stickDX, stickDX, Y, stickEY]);
893 end
894 else
895 begin
898 //if not g_Map_CollidePanel(X-1, Y-1, 3, 3, (PANEL_STEP or PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR))
900 {$ENDIF}
901 exit;
904 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
906 begin
929 {$ELSE}
932 begin
933 if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
935 begin
942 begin
947 begin
952 {$ENDIF}
957 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
959 begin
970 {$ELSE}
972 begin
973 // Âèñèò â âîçäóõå - êàïàåò
974 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
975 begin
981 {$ENDIF}
983 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
984 // horizontal
986 begin
987 pan := g_Map_traceToNearest(x, y, x+dX, y, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
990 // free to ride?
992 begin
993 // nope
994 if (dY > 0) and ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
995 // Ñòåíà/äâåðü?
997 begin
1009 // vertical
1011 begin
1013 begin
1014 pan := g_Map_traceToNearest(x, y, x, y+dY, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
1016 // free to ride?
1018 begin
1019 // nope
1020 if (dY > 0) and ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
1021 // Ñòåíà/äâåðü?
1023 begin
1029 begin
1031 end
1032 else
1033 begin
1035 if (g_Map_PanelAtPoint(x-1, y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := -1
1036 else if (g_Map_PanelAtPoint(x+1, y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := 1
1042 onGround := (velY >= 0) and g_Map_HasAnyPanelAtPoint(x, y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1046 {$ELSE}
1047 // horizontal
1049 begin
1052 begin
1053 // Ñáîêó ãðàíèöà?
1055 //c := gCollideMap[Y, X+s];
1056 // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò?
1066 Break;
1071 // vertical
1073 begin
1076 begin
1077 // Ñíèçó/ñâåðõó ãðàíèöà
1079 //c := gCollideMap[Y+s, X];
1080 // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
1088 if (s > 0) and (state <> TPartState.Stuck) then state := TPartState.Normal else state := TPartState.Stuck;
1090 break;
1095 {$ENDIF}
1104 // ////////////////////////////////////////////////////////////////////////// //
1106 var
1108 {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
1111 {$ELSE}
1114 {$ENDIF}
1115 begin
1119 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
1121 begin
1122 pan := g_Map_traceToNearest(x, y-1, x, y+1, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
1124 {$ELSE}
1133 {$ENDIF}
1136 begin
1137 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
1138 pan := g_Map_traceToNearest(x, y, x+dX, y, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
1139 //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);
1142 // free to ride?
1144 begin
1145 // nope
1146 if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
1151 {$ELSE}
1155 begin
1157 //c := gCollideMap[Y, X+s];
1162 Break;
1163 end
1168 begin
1170 break;
1173 {$ENDIF}
1177 begin
1178 {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
1180 begin
1181 pan := g_Map_traceToNearest(x, y, x, y+dY, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
1183 {
1184 if awaken then
1185 begin
1186 awaken := false;
1187 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]);
1188 end;
1189 }
1190 // free to ride?
1192 begin
1193 // nope
1194 if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
1196 begin
1199 end
1200 else
1201 begin
1208 onGround := (velY >= 0) and g_Map_HasAnyPanelAtPoint(x, y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1211 {$ELSE}
1215 begin
1217 //c := gCollideMap[Y+s, X];
1221 begin
1224 end
1226 begin
1233 Break;
1234 end
1239 begin
1241 break;
1244 {$ENDIF}
1250 begin
1258 // ////////////////////////////////////////////////////////////////////////// //
1260 var
1265 begin
1271 begin
1274 else
1278 begin
1281 (*
1282 if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
1283 begin // Óæå íå æèäêîñòü
1284 State := STATE_FREE;
1285 Break;
1286 end;
1287 *)
1288 // we traced liquid before, so don't bother checking
1302 // ////////////////////////////////////////////////////////////////////////// //
1304 var
1309 begin
1310 exit;
1322 begin
1324 begin
1334 else
1350 onGround := (velY >= 0) and g_Map_HasAnyPanelAtPoint(x, y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1356 else
1363 var
1370 begin
1371 exit;
1375 Exit;
1394 begin
1396 begin
1415 onGround := (velY >= 0) and g_Map_HasAnyPanelAtPoint(x, y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1421 else
1427 procedure g_GFX_Water(fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, Color: Byte);
1428 var
1433 begin
1434 exit;
1438 Exit;
1451 begin
1453 begin
1459 else
1469 begin
1475 begin
1481 begin
1487 begin
1501 onGround := (velY >= 0) and g_Map_HasAnyPanelAtPoint(x, y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1507 else
1513 procedure g_GFX_SimpleWater(fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
1514 var
1517 begin
1518 exit;
1522 Exit;
1527 begin
1529 begin
1540 begin
1546 begin
1552 begin
1558 begin
1567 begin
1576 begin
1590 onGround := (velY >= 0) and g_Map_HasAnyPanelAtPoint(x, y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1596 else
1602 {.$DEFINE D2F_DEBUG_BUBBLES}
1604 var
1609 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1612 {$ENDIF}
1613 begin
1614 exit;
1618 Exit;
1628 begin
1630 begin
1636 Continue;
1638 (*
1639 // don't spawn bubbles outside of the liquid
1640 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1641 Continue;
1642 *)
1644 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1645 // tracer will return `false` if we started outside of the liquid
1647 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1649 ptr := mapGrid.traceOrthoRayWhileIn(liquidx, liquidTopY, x, y, x, 0, GridTagWater or GridTagAcid1 or GridTagAcid2);
1651 e_LogWritefln('traceOrthoRayWhileIn: time=%s (%s); liquidTopY=%s', [Integer(stt), ptr, liquidTopY]);
1652 //
1656 e_LogWritefln('g_Map_TraceLiquidNonPrecise: time=%s (%s); liquidTopY=%s', [Integer(stt), nptr, liquidTopY]);
1658 {$ELSE}
1660 {$ENDIF}
1677 onGround := (velY >= 0) and g_Map_HasAnyPanelAtPoint(x, y+1, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_STEP));
1683 else
1689 // ////////////////////////////////////////////////////////////////////////// //
1691 var
1693 begin
1700 //if CurrentParticle >= Count then
1706 begin
1712 var
1714 begin
1718 begin
1720 Exit;
1724 begin
1727 end
1728 else
1729 begin
1737 var
1739 begin
1742 Exit;
1755 // ////////////////////////////////////////////////////////////////////////// //
1756 // st: set mark
1757 // t: mark type
1758 // currently unused
1760 var
1763 begin
1765 // make some border, so we'll hit particles lying around the panel
1772 begin
1775 begin
1784 // ////////////////////////////////////////////////////////////////////////// //
1785 {$IF DEFINED(HAS_COLLIDE_BITMAP)}
1787 var
1789 begin
1790 //g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
1791 //SetLength(gCollideMap, gMapInfo.Height+1);
1792 //for a := 0 to High(gCollideMap) do SetLength(gCollideMap[a], gMapInfo.Width+1);
1794 {$ENDIF}
1798 begin
1799 //CreateCollideMap();
1801 {$IFDEF HEADLESS}
1803 {$ENDIF}
1808 var
1810 begin
1817 begin
1823 // why not?
1829 // ////////////////////////////////////////////////////////////////////////// //
1831 var
1835 begin
1839 begin
1846 begin
1848 begin
1850 begin
1853 //if not alive then Continue;
1854 //e_WriteLog(Format('particle #%d: %d', [State, ParticleType]), MSG_NOTIFY);
1861 // clear awake map
1865 begin
1868 begin
1870 ONCEANIM_SMOKE:
1871 begin
1880 begin
1883 end
1884 else
1892 var
1894 begin
1896 begin
1910 begin