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);
68 var
73 //WARNING: only for Holmes!
80 // Wall: floorY is just before floor
81 // LiquidIn: floorY is liquid *start* (i.e. just in a liquid)
82 // LiquidOut: floorY is liquid *end* (i.e. just out of a liquid)
85 // note: this MUST be record, so we can keep it in
86 // dynamic array and has sequential memory access pattern
106 //k8: sorry, i have to emulate virtual methods this way, 'cause i haet `Object`
135 implementation
137 uses
143 const
146 var
149 // awakeMap has one bit for each map grid cell; on g_Mark,
150 // corresponding bits will be set, and in `think()` all particles
151 // in marked cells will be awaken
157 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
159 {$ENDIF}
162 // ////////////////////////////////////////////////////////////////////////// //
164 begin
165 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
170 begin
172 begin
174 end
175 else
176 begin
179 end
180 else
181 begin
184 {$ELSE}
186 {$ENDIF}
190 // ////////////////////////////////////////////////////////////////////////// //
191 // HACK! using mapgrid
193 begin
194 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
196 begin
200 {$ENDIF}
202 begin
210 begin
218 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
221 {$ENDIF}
222 //{$IF DEFINED(D2F_DEBUG)}
223 e_LogWritefln('particle awake map: %sx%s (for grid of size %sx%s)', [awakeMapW, awakeMapH, mapGrid.gridWidth, mapGrid.gridHeight]);
224 //{$ENDIF}
231 begin
235 begin
236 {$IF DEFINED(D2F_DEBUG)}
238 {$ENDIF}
240 end
241 else
242 begin
249 var
251 begin
255 begin
256 {$IF DEFINED(D2F_DEBUG)}
258 {$ENDIF}
266 // ////////////////////////////////////////////////////////////////////////// //
267 // st: set mark
268 // t: mark type
269 // currently unused
272 var
275 begin
281 // make some border, so we'll hit particles around the panel
292 // has something to do?
301 begin
303 begin
304 {$IF DEFINED(D2F_DEBUG)}
307 {$ENDIF}
315 // ////////////////////////////////////////////////////////////////////////// //
319 // remove velocities and acceleration
321 begin
322 // stop right there, you criminal scum!
330 // `true`: affected by air stream
332 var
335 begin
337 result := (pan <> nil) and WordBool(pan.PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT));
340 begin
342 LIFTTYPE_UP:
343 begin
349 LIFTTYPE_DOWN:
350 begin
354 LIFTTYPE_LEFT:
355 begin
359 LIFTTYPE_RIGHT:
360 begin
364 else
367 // awake
373 // switch to sleep mode
375 begin
377 begin
385 var
388 begin
390 // stuck in the wall? rescan, 'cause it can be mplat
392 begin
395 begin
396 // either in a wall, or in a liquid
398 begin
399 // we are in the wall, wtf?!
404 exit;
406 // we are in liquid, trace to liquid end
410 // are we in a liquid?
412 begin
413 // trace out of the liquid
414 //env := TEnvType.ELiquid;
416 //e_LogWritefln('tracing out of a liquid; floorY=%s; y=%s', [floorY, y]);
419 //e_LogWritefln(' traced out of a liquid; floorY=%s; y=%s', [floorY, y]);
420 end
421 else
422 begin
423 // in the air
425 //env := TEnvType.EAir;
426 pan := g_Map_traceToNearest(x, y, x, g_Map_MaxY, (GridTagObstacle or GridTagLiquid), @ex, @floorY);
428 begin
429 // wall or liquid
431 begin
432 // wall
434 end
435 else
436 begin
437 // liquid
441 end
442 else
443 begin
444 // out of the level; assume wall, but it doesn't really matter
453 var
455 begin
458 begin
466 begin
468 begin
469 //writeln('awaking particle at (', x, ',', y, ')');
471 begin
473 end
474 else
475 begin
476 // stuck to a wall, check if wall is still there
478 begin
481 begin
482 // a wall was moved out, start falling
489 end
490 else
491 begin
500 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
985 // check for level bounds
988 // in what environment we are starting in?
991 begin
992 // either in a wall, or in a liquid
995 end
996 else
997 begin
1005 begin
1033 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, color: Byte;
1035 var
1040 begin
1057 begin
1059 begin
1061 begin
1070 end
1071 else
1072 begin
1085 // check for level bounds
1088 // this hack will allow water spawned in water to fly out
1089 // it can happen when player fell from a huge height (see "DOOM2D.WAD:\MAP03", for example)
1091 begin
1092 // in what environment we are starting in?
1094 end
1095 else
1096 begin
1102 // color
1105 begin
1111 begin
1117 begin
1123 begin
1132 begin
1141 begin
1163 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
1164 begin
1169 // ////////////////////////////////////////////////////////////////////////// //
1171 var
1173 begin
1177 begin
1180 begin
1182 end
1183 else
1184 begin
1194 else
1199 {.$DEFINE D2F_DEBUG_BUBBLES}
1201 var
1205 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1208 {$ENDIF}
1209 begin
1222 begin
1224 begin
1230 // check for level bounds
1233 (*
1234 // don't spawn bubbles outside of the liquid
1235 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1236 Continue;
1237 *)
1239 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1240 // tracer will return `false` if we started outside of the liquid
1242 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1244 ptr := mapGrid.traceOrthoRayWhileIn(liquidx, liquidTopY, x, y, x, 0, GridTagWater or GridTagAcid1 or GridTagAcid2);
1246 e_LogWritefln('traceOrthoRayWhileIn: time=%s (%s); liquidTopY=%s', [Integer(stt), ptr, liquidTopY]);
1247 //
1251 e_LogWritefln('g_Map_TraceLiquidNonPrecise: time=%s (%s); liquidTopY=%s', [Integer(stt), nptr, liquidTopY]);
1253 {$ELSE}
1256 {$ENDIF}
1280 // ////////////////////////////////////////////////////////////////////////// //
1282 label
1283 _done;
1284 var
1288 begin
1294 //writeln('spark0: pos=(', x, ',', y, '); delta=(', dx, ',', dy, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1296 // apply gravity
1298 begin
1303 // flying
1305 begin
1306 // has some horizontal velocity
1312 begin
1314 // hit the wall; falling down vertically
1318 end
1320 begin
1321 // has some vertical velocity
1323 begin
1324 // flying up
1328 begin
1329 // oops, hit a ceiling
1334 // environment didn't changed
1335 end
1336 else
1337 begin
1338 // falling down
1342 begin
1343 // hit something except a floor?
1345 // otherwise, go to sleep
1348 // environment didn't changed
1353 _done:
1354 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
1359 begin
1364 //writeln('spark1: pos=(', x, ',', y, '); delta=(', velX:6:3, ',', velY:6:3, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1368 else
1373 // ////////////////////////////////////////////////////////////////////////// //
1375 var
1380 begin
1393 begin
1395 begin
1401 // check for level bounds
1404 // in what environment we are starting in?
1407 begin
1408 // either in a wall, or in a liquid
1409 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1410 //env := TEnvType.ELiquid;
1411 continue;
1412 end
1413 else
1414 begin
1422 begin
1449 var
1456 begin
1477 begin
1479 begin
1485 // check for level bounds
1488 // in what environment we are starting in?
1491 begin
1492 // either in a wall, or in a liquid
1493 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1494 //env := TEnvType.ELiquid;
1495 continue;
1496 end
1497 else
1498 begin
1526 // ////////////////////////////////////////////////////////////////////////// //
1528 var
1530 begin
1541 begin
1547 var
1549 begin
1553 begin
1555 Exit;
1559 begin
1562 end
1563 else
1564 begin
1572 var
1574 begin
1590 // ////////////////////////////////////////////////////////////////////////// //
1592 begin
1593 //g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
1594 //SetLength(gCollideMap, gMapInfo.Height+1);
1595 //for a := 0 to High(gCollideMap) do SetLength(gCollideMap[a], gMapInfo.Width+1);
1597 {$IFDEF HEADLESS}
1599 {$ENDIF}
1604 var
1606 begin
1613 begin
1619 // why not?
1625 // ////////////////////////////////////////////////////////////////////////// //
1627 var
1631 begin
1635 begin
1642 begin
1644 begin
1646 begin
1655 // clear awake map
1659 begin
1662 begin
1667 ONCEANIM_SMOKE:
1668 begin
1677 begin
1680 end
1681 else