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_DEBUG_FALL_MPLAT}
18 {/$DEFINE D2F_DEBUG_PART_AWAKE}
21 interface
23 uses
26 const
55 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, color: Byte;
57 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
71 var
76 //WARNING: only for Holmes!
80 implementation
82 uses
83 {$INCLUDE ../nogl/noGLuses.inc}
89 const
93 type
97 // Wall: floorY is just before floor
98 // LiquidIn: floorY is liquid *start* (i.e. just in a liquid)
99 // LiquidOut: floorY is liquid *end* (i.e. just out of a liquid)
102 // note: this MUST be record, so we can keep it in
103 // dynamic array and has sequential memory access pattern
122 //k8: sorry, i have to emulate virtual methods this way, 'cause i haet `Object`
147 var
152 // awakeMap has one bit for each map grid cell; on g_Mark,
153 // corresponding bits will be set, and in `think()` all particles
154 // in marked cells will be awaken
160 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
162 {$ENDIF}
165 // ////////////////////////////////////////////////////////////////////////// //
167 begin
168 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
173 begin
175 begin
177 end
178 else
179 begin
182 end
183 else
184 begin
187 {$ELSE}
189 {$ENDIF}
193 // ////////////////////////////////////////////////////////////////////////// //
194 // HACK! using mapgrid
196 begin
197 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
199 begin
203 {$ENDIF}
205 begin
213 begin
221 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
224 {$ENDIF}
225 //{$IF DEFINED(D2F_DEBUG)}
226 e_LogWritefln('particle awake map: %sx%s (for grid of size %sx%s)', [awakeMapW, awakeMapH, mapGrid.gridWidth, mapGrid.gridHeight]);
227 //{$ENDIF}
234 begin
238 begin
239 {$IF DEFINED(D2F_DEBUG)}
241 {$ENDIF}
243 end
244 else
245 begin
252 var
254 begin
258 begin
259 {$IF DEFINED(D2F_DEBUG)}
261 {$ENDIF}
269 // ////////////////////////////////////////////////////////////////////////// //
270 // st: set mark
271 // t: mark type
272 // currently unused
275 var
278 begin
284 // make some border, so we'll hit particles around the panel
295 // has something to do?
304 begin
306 begin
307 {$IF DEFINED(D2F_DEBUG)}
310 {$ENDIF}
318 // ////////////////////////////////////////////////////////////////////////// //
322 // remove velocities and acceleration
324 begin
325 // stop right there, you criminal scum!
333 // `true`: affected by air stream
335 var
338 begin
340 result := (pan <> nil) and WordBool(pan.PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT));
343 begin
345 LIFTTYPE_UP:
346 begin
352 LIFTTYPE_DOWN:
353 begin
357 LIFTTYPE_LEFT:
358 begin
362 LIFTTYPE_RIGHT:
363 begin
367 else
370 // awake
376 // switch to sleep mode
378 begin
380 begin
388 var
391 begin
393 // stuck in the wall? rescan, 'cause it can be mplat
395 begin
398 begin
399 // either in a wall, or in a liquid
401 begin
402 // we are in the wall, wtf?!
407 exit;
409 // we are in liquid, trace to liquid end
413 // are we in a liquid?
415 begin
416 // trace out of the liquid
417 //env := TEnvType.ELiquid;
419 //e_LogWritefln('tracing out of a liquid; floorY=%s; y=%s', [floorY, y]);
422 //e_LogWritefln(' traced out of a liquid; floorY=%s; y=%s', [floorY, y]);
423 end
424 else
425 begin
426 // in the air
428 //env := TEnvType.EAir;
429 pan := g_Map_traceToNearest(x, y, x, g_Map_MaxY, (GridTagObstacle or GridTagLiquid), @ex, @floorY);
431 begin
432 // wall or liquid
434 begin
435 // wall
437 end
438 else
439 begin
440 // liquid
444 end
445 else
446 begin
447 // out of the level; assume wall, but it doesn't really matter
456 var
458 begin
461 begin
469 begin
471 begin
472 //writeln('awaking particle at (', x, ',', y, ')');
474 begin
476 end
477 else
478 begin
479 // stuck to a wall, check if wall is still there
481 begin
484 begin
485 // a wall was moved out, start falling
492 end
493 else
494 begin
503 begin
504 // awake sleeping particle, if necessary
506 begin
508 {
509 case state of
510 TPartState.Sleeping, TPartState.Stuck:
511 if awmIsSet(x, y) then awake();
512 else
513 if (env = TEnvType.EWall) and awmIsSet(x, y) then awake();
514 end;
515 }
525 // ////////////////////////////////////////////////////////////////////////// //
528 begin
536 var
538 begin
542 // find next floor transition
544 // find `wallEndY`
545 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
549 begin
556 // `true`: didn't, get outa thinker
558 begin
565 begin
568 // if we're falling from ceiling, switch to normal mode
573 // switch to freefall mode
575 begin
582 begin
585 begin
588 end
589 else
590 begin
596 label
598 var
604 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
606 {$ENDIF}
607 begin
611 begin
612 // still check for air streams when sleeping (no)
613 if (state = TPartState.Sleeping) then begin {checkAirStreams();} goto _done; end; // so blood will dissolve
615 // process stuck particles
617 begin
618 // stuck to a ceiling?
620 begin
621 // yeah, stuck to a ceiling
623 // dropped from a ceiling?
625 begin
626 // yep
630 end
631 else
632 begin
633 // otherwise, try to drip
636 end
637 else
638 begin
639 // stuck to a wall
641 begin
642 // this can happen if mplat was moved out; find new `wallEndY`
645 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
647 _stuckagain:
648 // floor transition?
650 begin
654 begin
655 // check if our ground wasn't moved since the last scan
658 begin
662 // otherwise, do it again
666 begin
667 // rescan, so we'll know when we'll exit the liquid
671 begin
672 // rescan, so we'll know when we'll enter something interesting
678 // wall transition?
680 begin
681 // just unstuck from the wall, switch to freefall mode
684 end
685 else
686 begin
687 // otherwise, try to drip
691 // nope, process as usual
694 // it is important to have it here
700 // gravity, if not stuck
702 begin
705 _gravityagain:
706 // floor transition?
708 begin
711 begin
712 // check if our ground wasn't moved since the last scan
714 begin
719 // otherwise, nothing to do
722 begin
723 // rescan, so we'll know when we'll exit the liquid
728 begin
729 // rescan, so we'll know when we'll enter something interesting
731 if (floorType <> TFloorType.Wall) or (floorY <> y) then applyGravity(floorType = TFloorType.LiquidIn);
734 end
735 else
736 begin
737 // looks like we're in the air
742 // trace movement
744 begin
745 // has some horizontal velocity
749 begin
750 // do not stuck inside step
752 // check for step panel below
756 begin
757 // stick to panel edges
768 begin
769 // dunno yet
772 // check environment (air/liquid)
773 if (g_Map_PanelAtPoint(x, y, GridTagLiquid) <> nil) then env := TEnvType.ELiquid else env := TEnvType.EAir;
776 begin
779 else
780 begin
781 // we stuck
782 // the only case when we can have both ceiling and wall is corner; stick to wall in this case
783 // check if we stuck to a wall
786 begin
787 // stuck to a wall
789 end
790 else
791 begin
792 // stuck to a ceiling
797 end
799 begin
800 // has only vertical velocity
802 begin
803 // flying up
807 // environment didn't changed
808 end
809 else
810 begin
812 begin
813 // falling down
818 //e_LogWritefln('floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
820 begin
821 // floor transition
824 //e_LogWritefln(' HIT FLOORY: floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
827 begin
828 // check if our ground wasn't moved since the last scan
830 begin
831 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
833 {$ENDIF}
835 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
837 begin
838 e_LogWritefln('force rescanning vpart at (%s,%s); oldFloorY=%s; floorY=%s', [x, y, oldFloorY, floorY]);
840 {$ENDIF}
844 // environment didn't changed
849 begin
850 // we're entered the liquid
852 // rescan, so we'll know when we'll exit the liquid
856 begin
857 // we're exited the liquid
859 // rescan, so we'll know when we'll enter something interesting
862 begin
868 end
869 else
870 begin
877 else
878 begin
879 // simple blood
887 _done:
888 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
893 // blood will dissolve in other liquids
895 begin
897 begin
900 else
908 end
909 else
910 begin
911 // water will disappear in any liquid
915 else
917 // dry water
927 // ////////////////////////////////////////////////////////////////////////// //
928 procedure g_GFX_SparkVel (fX, fY: Integer; count: Word; vx, vy: Integer; devX, devY: Byte); forward;
934 begin
936 begin
941 end
942 else
943 begin
948 var
954 begin
958 begin
960 exit;
961 end
963 begin
978 begin
980 begin
984 // check for level bounds
987 // in what environment we are starting in?
990 begin
991 // either in a wall, or in a liquid
994 end
995 else
996 begin
1004 begin
1032 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, color: Byte;
1034 var
1039 begin
1056 begin
1058 begin
1060 begin
1069 end
1070 else
1071 begin
1081 // check for level bounds
1084 // this hack will allow water spawned in water to fly out
1085 // it can happen when player fell from a huge height (see "DOOM2D.WAD:\MAP03", for example)
1087 begin
1088 // in what environment we are starting in?
1090 end
1091 else
1092 begin
1098 // color
1101 begin
1107 begin
1113 begin
1119 begin
1128 begin
1137 begin
1159 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
1160 begin
1165 // ////////////////////////////////////////////////////////////////////////// //
1167 var
1169 begin
1173 begin
1176 begin
1178 end
1179 else
1180 begin
1190 else
1195 {.$DEFINE D2F_DEBUG_BUBBLES}
1197 var
1201 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1204 {$ENDIF}
1205 begin
1218 begin
1220 begin
1224 // check for level bounds
1227 (*
1228 // don't spawn bubbles outside of the liquid
1229 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1230 Continue;
1231 *)
1233 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1234 // tracer will return `false` if we started outside of the liquid
1236 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1238 ptr := mapGrid.traceOrthoRayWhileIn(liquidx, liquidTopY, x, y, x, 0, GridTagWater or GridTagAcid1 or GridTagAcid2);
1240 e_LogWritefln('traceOrthoRayWhileIn: time=%s (%s); liquidTopY=%s', [Integer(stt), ptr, liquidTopY]);
1241 //
1245 e_LogWritefln('g_Map_TraceLiquidNonPrecise: time=%s (%s); liquidTopY=%s', [Integer(stt), nptr, liquidTopY]);
1247 {$ELSE}
1250 {$ENDIF}
1274 // ////////////////////////////////////////////////////////////////////////// //
1276 label
1277 _done;
1278 var
1282 begin
1288 //writeln('spark0: pos=(', x, ',', y, '); delta=(', dx, ',', dy, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1290 // apply gravity
1292 begin
1297 // flying
1299 begin
1300 // has some horizontal velocity
1306 begin
1308 // hit the wall; falling down vertically
1312 end
1314 begin
1315 // has some vertical velocity
1317 begin
1318 // flying up
1322 begin
1323 // oops, hit a ceiling
1328 // environment didn't changed
1329 end
1330 else
1331 begin
1332 // falling down
1336 begin
1337 // hit something except a floor?
1339 // otherwise, go to sleep
1342 // environment didn't changed
1347 _done:
1348 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
1353 begin
1358 //writeln('spark1: pos=(', x, ',', y, '); delta=(', velX:6:3, ',', velY:6:3, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1362 else
1367 // ////////////////////////////////////////////////////////////////////////// //
1369 var
1374 begin
1387 begin
1389 begin
1393 // check for level bounds
1396 // in what environment we are starting in?
1399 begin
1400 // either in a wall, or in a liquid
1401 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1402 //env := TEnvType.ELiquid;
1403 continue;
1404 end
1405 else
1406 begin
1414 begin
1441 var
1448 begin
1469 begin
1471 begin
1475 // check for level bounds
1478 // in what environment we are starting in?
1481 begin
1482 // either in a wall, or in a liquid
1483 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1484 //env := TEnvType.ELiquid;
1485 continue;
1486 end
1487 else
1488 begin
1516 // ////////////////////////////////////////////////////////////////////////// //
1518 var
1520 begin
1531 begin
1537 var
1539 begin
1543 begin
1545 Exit;
1549 begin
1552 end
1553 else
1554 begin
1562 var
1564 begin
1580 // ////////////////////////////////////////////////////////////////////////// //
1582 begin
1583 //g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
1584 //SetLength(gCollideMap, gMapInfo.Height+1);
1585 //for a := 0 to High(gCollideMap) do SetLength(gCollideMap[a], gMapInfo.Width+1);
1587 {$IFDEF HEADLESS}
1589 {$ENDIF}
1594 var
1596 begin
1603 begin
1609 // why not?
1615 // ////////////////////////////////////////////////////////////////////////// //
1617 var
1621 begin
1625 begin
1632 begin
1634 begin
1636 begin
1645 // clear awake map
1649 begin
1652 begin
1654 ONCEANIM_SMOKE:
1655 begin
1664 begin
1667 end
1668 else
1676 var
1678 {$IFDEF USE_NANOGL}
1679 type
1684 var
1687 {$ENDIF}
1688 begin
1692 begin
1702 {$IFDEF USE_NANOGL}
1706 begin
1708 begin
1710 begin
1729 {$ELSE}
1734 begin
1736 begin
1739 begin
1747 {$ENDIF}
1753 begin
1756 begin
1758 begin