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, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 {.$DEFINE D2F_DEBUG_FALL_MPLAT}
17 {/$DEFINE D2F_DEBUG_PART_AWAKE}
20 interface
22 uses
25 const
54 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, color: Byte;
56 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
70 var
75 //WARNING: only for Holmes!
79 implementation
81 uses
82 {$INCLUDE ../nogl/noGLuses.inc}
88 const
92 type
96 // Wall: floorY is just before floor
97 // LiquidIn: floorY is liquid *start* (i.e. just in a liquid)
98 // LiquidOut: floorY is liquid *end* (i.e. just out of a liquid)
101 // note: this MUST be record, so we can keep it in
102 // dynamic array and has sequential memory access pattern
121 //k8: sorry, i have to emulate virtual methods this way, 'cause i haet `Object`
146 var
151 // awakeMap has one bit for each map grid cell; on g_Mark,
152 // corresponding bits will be set, and in `think()` all particles
153 // in marked cells will be awaken
159 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
161 {$ENDIF}
164 // ////////////////////////////////////////////////////////////////////////// //
166 begin
167 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
172 begin
174 begin
176 end
177 else
178 begin
181 end
182 else
183 begin
186 {$ELSE}
188 {$ENDIF}
192 // ////////////////////////////////////////////////////////////////////////// //
193 // HACK! using mapgrid
195 begin
196 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
198 begin
202 {$ENDIF}
204 begin
212 begin
220 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
223 {$ENDIF}
224 //{$IF DEFINED(D2F_DEBUG)}
225 e_LogWritefln('particle awake map: %sx%s (for grid of size %sx%s)', [awakeMapW, awakeMapH, mapGrid.gridWidth, mapGrid.gridHeight]);
226 //{$ENDIF}
233 begin
237 begin
238 {$IF DEFINED(D2F_DEBUG)}
240 {$ENDIF}
242 end
243 else
244 begin
251 var
253 begin
257 begin
258 {$IF DEFINED(D2F_DEBUG)}
260 {$ENDIF}
268 // ////////////////////////////////////////////////////////////////////////// //
269 // st: set mark
270 // t: mark type
271 // currently unused
274 var
277 begin
283 // make some border, so we'll hit particles around the panel
294 // has something to do?
303 begin
305 begin
306 {$IF DEFINED(D2F_DEBUG)}
309 {$ENDIF}
317 // ////////////////////////////////////////////////////////////////////////// //
321 // remove velocities and acceleration
323 begin
324 // stop right there, you criminal scum!
332 // `true`: affected by air stream
334 var
337 begin
339 result := (pan <> nil) and WordBool(pan.PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT));
342 begin
344 LIFTTYPE_UP:
345 begin
351 LIFTTYPE_DOWN:
352 begin
356 LIFTTYPE_LEFT:
357 begin
361 LIFTTYPE_RIGHT:
362 begin
366 else
369 // awake
375 // switch to sleep mode
377 begin
379 begin
387 var
390 begin
392 // stuck in the wall? rescan, 'cause it can be mplat
394 begin
397 begin
398 // either in a wall, or in a liquid
400 begin
401 // we are in the wall, wtf?!
406 exit;
408 // we are in liquid, trace to liquid end
412 // are we in a liquid?
414 begin
415 // trace out of the liquid
416 //env := TEnvType.ELiquid;
418 //e_LogWritefln('tracing out of a liquid; floorY=%s; y=%s', [floorY, y]);
421 //e_LogWritefln(' traced out of a liquid; floorY=%s; y=%s', [floorY, y]);
422 end
423 else
424 begin
425 // in the air
427 //env := TEnvType.EAir;
428 pan := g_Map_traceToNearest(x, y, x, g_Map_MaxY, (GridTagObstacle or GridTagLiquid), @ex, @floorY);
430 begin
431 // wall or liquid
433 begin
434 // wall
436 end
437 else
438 begin
439 // liquid
443 end
444 else
445 begin
446 // out of the level; assume wall, but it doesn't really matter
455 var
457 begin
460 begin
468 begin
470 begin
471 //writeln('awaking particle at (', x, ',', y, ')');
473 begin
475 end
476 else
477 begin
478 // stuck to a wall, check if wall is still there
480 begin
483 begin
484 // a wall was moved out, start falling
491 end
492 else
493 begin
502 begin
503 // awake sleeping particle, if necessary
505 begin
507 {
508 case state of
509 TPartState.Sleeping, TPartState.Stuck:
510 if awmIsSet(x, y) then awake();
511 else
512 if (env = TEnvType.EWall) and awmIsSet(x, y) then awake();
513 end;
514 }
524 // ////////////////////////////////////////////////////////////////////////// //
527 begin
535 var
537 begin
541 // find next floor transition
543 // find `wallEndY`
544 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
548 begin
555 // `true`: didn't, get outa thinker
557 begin
564 begin
567 // if we're falling from ceiling, switch to normal mode
572 // switch to freefall mode
574 begin
581 begin
584 begin
587 end
588 else
589 begin
595 label
597 var
603 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
605 {$ENDIF}
606 begin
610 begin
611 // still check for air streams when sleeping (no)
612 if (state = TPartState.Sleeping) then begin {checkAirStreams();} goto _done; end; // so blood will dissolve
614 // process stuck particles
616 begin
617 // stuck to a ceiling?
619 begin
620 // yeah, stuck to a ceiling
622 // dropped from a ceiling?
624 begin
625 // yep
629 end
630 else
631 begin
632 // otherwise, try to drip
635 end
636 else
637 begin
638 // stuck to a wall
640 begin
641 // this can happen if mplat was moved out; find new `wallEndY`
644 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
646 _stuckagain:
647 // floor transition?
649 begin
653 begin
654 // check if our ground wasn't moved since the last scan
657 begin
661 // otherwise, do it again
665 begin
666 // rescan, so we'll know when we'll exit the liquid
670 begin
671 // rescan, so we'll know when we'll enter something interesting
677 // wall transition?
679 begin
680 // just unstuck from the wall, switch to freefall mode
683 end
684 else
685 begin
686 // otherwise, try to drip
690 // nope, process as usual
693 // it is important to have it here
699 // gravity, if not stuck
701 begin
704 _gravityagain:
705 // floor transition?
707 begin
710 begin
711 // check if our ground wasn't moved since the last scan
713 begin
718 // otherwise, nothing to do
721 begin
722 // rescan, so we'll know when we'll exit the liquid
727 begin
728 // rescan, so we'll know when we'll enter something interesting
730 if (floorType <> TFloorType.Wall) or (floorY <> y) then applyGravity(floorType = TFloorType.LiquidIn);
733 end
734 else
735 begin
736 // looks like we're in the air
741 // trace movement
743 begin
744 // has some horizontal velocity
748 begin
749 // do not stuck inside step
751 // check for step panel below
755 begin
756 // stick to panel edges
767 begin
768 // dunno yet
771 // check environment (air/liquid)
772 if (g_Map_PanelAtPoint(x, y, GridTagLiquid) <> nil) then env := TEnvType.ELiquid else env := TEnvType.EAir;
775 begin
778 else
779 begin
780 // we stuck
781 // the only case when we can have both ceiling and wall is corner; stick to wall in this case
782 // check if we stuck to a wall
785 begin
786 // stuck to a wall
788 end
789 else
790 begin
791 // stuck to a ceiling
796 end
798 begin
799 // has only vertical velocity
801 begin
802 // flying up
806 // environment didn't changed
807 end
808 else
809 begin
811 begin
812 // falling down
817 //e_LogWritefln('floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
819 begin
820 // floor transition
823 //e_LogWritefln(' HIT FLOORY: floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
826 begin
827 // check if our ground wasn't moved since the last scan
829 begin
830 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
832 {$ENDIF}
834 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
836 begin
837 e_LogWritefln('force rescanning vpart at (%s,%s); oldFloorY=%s; floorY=%s', [x, y, oldFloorY, floorY]);
839 {$ENDIF}
843 // environment didn't changed
848 begin
849 // we're entered the liquid
851 // rescan, so we'll know when we'll exit the liquid
855 begin
856 // we're exited the liquid
858 // rescan, so we'll know when we'll enter something interesting
861 begin
867 end
868 else
869 begin
876 else
877 begin
878 // simple blood
886 _done:
887 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
892 // blood will dissolve in other liquids
894 begin
896 begin
899 else
907 end
908 else
909 begin
910 // water will disappear in any liquid
914 else
916 // dry water
926 // ////////////////////////////////////////////////////////////////////////// //
927 procedure g_GFX_SparkVel (fX, fY: Integer; count: Word; vx, vy: Integer; devX, devY: Byte); forward;
933 begin
935 begin
940 end
941 else
942 begin
947 var
953 begin
957 begin
959 exit;
960 end
962 begin
977 begin
979 begin
983 // check for level bounds
986 // in what environment we are starting in?
989 begin
990 // either in a wall, or in a liquid
993 end
994 else
995 begin
1003 begin
1031 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, color: Byte;
1033 var
1038 begin
1055 begin
1057 begin
1059 begin
1068 end
1069 else
1070 begin
1080 // check for level bounds
1083 // this hack will allow water spawned in water to fly out
1084 // it can happen when player fell from a huge height (see "DOOM2D.WAD:\MAP03", for example)
1086 begin
1087 // in what environment we are starting in?
1089 end
1090 else
1091 begin
1097 // color
1100 begin
1106 begin
1112 begin
1118 begin
1127 begin
1136 begin
1158 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
1159 begin
1164 // ////////////////////////////////////////////////////////////////////////// //
1166 var
1168 begin
1172 begin
1175 begin
1177 end
1178 else
1179 begin
1189 else
1194 {.$DEFINE D2F_DEBUG_BUBBLES}
1196 var
1200 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1203 {$ENDIF}
1204 begin
1217 begin
1219 begin
1223 // check for level bounds
1226 (*
1227 // don't spawn bubbles outside of the liquid
1228 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1229 Continue;
1230 *)
1232 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1233 // tracer will return `false` if we started outside of the liquid
1235 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1237 ptr := mapGrid.traceOrthoRayWhileIn(liquidx, liquidTopY, x, y, x, 0, GridTagWater or GridTagAcid1 or GridTagAcid2);
1239 e_LogWritefln('traceOrthoRayWhileIn: time=%s (%s); liquidTopY=%s', [Integer(stt), ptr, liquidTopY]);
1240 //
1244 e_LogWritefln('g_Map_TraceLiquidNonPrecise: time=%s (%s); liquidTopY=%s', [Integer(stt), nptr, liquidTopY]);
1246 {$ELSE}
1249 {$ENDIF}
1273 // ////////////////////////////////////////////////////////////////////////// //
1275 label
1276 _done;
1277 var
1281 begin
1287 //writeln('spark0: pos=(', x, ',', y, '); delta=(', dx, ',', dy, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1289 // apply gravity
1291 begin
1296 // flying
1298 begin
1299 // has some horizontal velocity
1305 begin
1307 // hit the wall; falling down vertically
1311 end
1313 begin
1314 // has some vertical velocity
1316 begin
1317 // flying up
1321 begin
1322 // oops, hit a ceiling
1327 // environment didn't changed
1328 end
1329 else
1330 begin
1331 // falling down
1335 begin
1336 // hit something except a floor?
1338 // otherwise, go to sleep
1341 // environment didn't changed
1346 _done:
1347 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
1352 begin
1357 //writeln('spark1: pos=(', x, ',', y, '); delta=(', velX:6:3, ',', velY:6:3, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1361 else
1366 // ////////////////////////////////////////////////////////////////////////// //
1368 var
1373 begin
1386 begin
1388 begin
1392 // check for level bounds
1395 // in what environment we are starting in?
1398 begin
1399 // either in a wall, or in a liquid
1400 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1401 //env := TEnvType.ELiquid;
1402 continue;
1403 end
1404 else
1405 begin
1413 begin
1440 var
1447 begin
1468 begin
1470 begin
1474 // check for level bounds
1477 // in what environment we are starting in?
1480 begin
1481 // either in a wall, or in a liquid
1482 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1483 //env := TEnvType.ELiquid;
1484 continue;
1485 end
1486 else
1487 begin
1515 // ////////////////////////////////////////////////////////////////////////// //
1517 var
1519 begin
1530 begin
1536 var
1538 begin
1542 begin
1544 Exit;
1548 begin
1551 end
1552 else
1553 begin
1561 var
1563 begin
1579 // ////////////////////////////////////////////////////////////////////////// //
1581 begin
1582 //g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
1583 //SetLength(gCollideMap, gMapInfo.Height+1);
1584 //for a := 0 to High(gCollideMap) do SetLength(gCollideMap[a], gMapInfo.Width+1);
1586 {$IFDEF HEADLESS}
1588 {$ENDIF}
1593 var
1595 begin
1602 begin
1608 // why not?
1614 // ////////////////////////////////////////////////////////////////////////// //
1616 var
1620 begin
1624 begin
1631 begin
1633 begin
1635 begin
1644 // clear awake map
1648 begin
1651 begin
1653 ONCEANIM_SMOKE:
1654 begin
1663 begin
1666 end
1667 else
1675 var
1677 begin
1681 begin
1695 begin
1697 begin
1700 begin
1713 begin
1716 begin
1718 begin