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
122 //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
161 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
163 {$ENDIF}
166 // ////////////////////////////////////////////////////////////////////////// //
168 begin
169 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
174 begin
176 begin
178 end
179 else
180 begin
183 end
184 else
185 begin
188 {$ELSE}
190 {$ENDIF}
194 // ////////////////////////////////////////////////////////////////////////// //
195 // HACK! using mapgrid
197 begin
198 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
200 begin
204 {$ENDIF}
206 begin
214 begin
222 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
225 {$ENDIF}
226 //{$IF DEFINED(D2F_DEBUG)}
227 e_LogWritefln('particle awake map: %sx%s (for grid of size %sx%s)', [awakeMapW, awakeMapH, mapGrid.gridWidth, mapGrid.gridHeight]);
228 //{$ENDIF}
235 begin
239 begin
240 {$IF DEFINED(D2F_DEBUG)}
242 {$ENDIF}
244 end
245 else
246 begin
253 var
255 begin
259 begin
260 {$IF DEFINED(D2F_DEBUG)}
262 {$ENDIF}
270 // ////////////////////////////////////////////////////////////////////////// //
271 // st: set mark
272 // t: mark type
273 // currently unused
276 var
279 begin
285 // make some border, so we'll hit particles around the panel
296 // has something to do?
305 begin
307 begin
308 {$IF DEFINED(D2F_DEBUG)}
311 {$ENDIF}
319 // ////////////////////////////////////////////////////////////////////////// //
323 // remove velocities and acceleration
325 begin
326 // stop right there, you criminal scum!
334 // `true`: affected by air stream
336 var
339 begin
341 result := (pan <> nil) and WordBool(pan.PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT));
344 begin
346 LIFTTYPE_UP:
347 begin
353 LIFTTYPE_DOWN:
354 begin
358 LIFTTYPE_LEFT:
359 begin
363 LIFTTYPE_RIGHT:
364 begin
368 else
371 // awake
377 // switch to sleep mode
379 begin
381 begin
389 var
392 begin
394 // stuck in the wall? rescan, 'cause it can be mplat
396 begin
399 begin
400 // either in a wall, or in a liquid
402 begin
403 // we are in the wall, wtf?!
408 exit;
410 // we are in liquid, trace to liquid end
414 // are we in a liquid?
416 begin
417 // trace out of the liquid
418 //env := TEnvType.ELiquid;
420 //e_LogWritefln('tracing out of a liquid; floorY=%s; y=%s', [floorY, y]);
423 //e_LogWritefln(' traced out of a liquid; floorY=%s; y=%s', [floorY, y]);
424 end
425 else
426 begin
427 // in the air
429 //env := TEnvType.EAir;
430 pan := g_Map_traceToNearest(x, y, x, g_Map_MaxY, (GridTagObstacle or GridTagLiquid), @ex, @floorY);
432 begin
433 // wall or liquid
435 begin
436 // wall
438 end
439 else
440 begin
441 // liquid
445 end
446 else
447 begin
448 // out of the level; assume wall, but it doesn't really matter
457 var
459 begin
462 begin
470 begin
472 begin
473 //writeln('awaking particle at (', x, ',', y, ')');
475 begin
477 end
478 else
479 begin
480 // stuck to a wall, check if wall is still there
482 begin
485 begin
486 // a wall was moved out, start falling
493 end
494 else
495 begin
504 begin
507 // awake sleeping particle, if necessary
509 begin
511 {
512 case state of
513 TPartState.Sleeping, TPartState.Stuck:
514 if awmIsSet(x, y) then awake();
515 else
516 if (env = TEnvType.EWall) and awmIsSet(x, y) then awake();
517 end;
518 }
528 // ////////////////////////////////////////////////////////////////////////// //
531 begin
539 var
541 begin
545 // find next floor transition
547 // find `wallEndY`
548 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
552 begin
559 // `true`: didn't, get outa thinker
561 begin
568 begin
571 // if we're falling from ceiling, switch to normal mode
576 // switch to freefall mode
578 begin
585 begin
588 begin
591 end
592 else
593 begin
599 label
601 var
607 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
609 {$ENDIF}
610 begin
614 begin
615 // still check for air streams when sleeping (no)
616 if (state = TPartState.Sleeping) then begin {checkAirStreams();} goto _done; end; // so blood will dissolve
618 // process stuck particles
620 begin
621 // stuck to a ceiling?
623 begin
624 // yeah, stuck to a ceiling
626 // dropped from a ceiling?
628 begin
629 // yep
633 end
634 else
635 begin
636 // otherwise, try to drip
639 end
640 else
641 begin
642 // stuck to a wall
644 begin
645 // this can happen if mplat was moved out; find new `wallEndY`
648 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
650 _stuckagain:
651 // floor transition?
653 begin
657 begin
658 // check if our ground wasn't moved since the last scan
661 begin
665 // otherwise, do it again
669 begin
670 // rescan, so we'll know when we'll exit the liquid
674 begin
675 // rescan, so we'll know when we'll enter something interesting
681 // wall transition?
683 begin
684 // just unstuck from the wall, switch to freefall mode
687 end
688 else
689 begin
690 // otherwise, try to drip
694 // nope, process as usual
697 // it is important to have it here
703 // gravity, if not stuck
705 begin
708 _gravityagain:
709 // floor transition?
711 begin
714 begin
715 // check if our ground wasn't moved since the last scan
717 begin
722 // otherwise, nothing to do
725 begin
726 // rescan, so we'll know when we'll exit the liquid
731 begin
732 // rescan, so we'll know when we'll enter something interesting
734 if (floorType <> TFloorType.Wall) or (floorY <> y) then applyGravity(floorType = TFloorType.LiquidIn);
737 end
738 else
739 begin
740 // looks like we're in the air
745 // trace movement
747 begin
748 // has some horizontal velocity
752 begin
753 // do not stuck inside step
755 // check for step panel below
759 begin
760 // stick to panel edges
771 begin
772 // dunno yet
775 // check environment (air/liquid)
776 if (g_Map_PanelAtPoint(x, y, GridTagLiquid) <> nil) then env := TEnvType.ELiquid else env := TEnvType.EAir;
779 begin
782 else
783 begin
784 // we stuck
785 // the only case when we can have both ceiling and wall is corner; stick to wall in this case
786 // check if we stuck to a wall
789 begin
790 // stuck to a wall
792 end
793 else
794 begin
795 // stuck to a ceiling
800 end
802 begin
803 // has only vertical velocity
805 begin
806 // flying up
810 // environment didn't changed
811 end
812 else
813 begin
815 begin
816 // falling down
821 //e_LogWritefln('floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
823 begin
824 // floor transition
827 //e_LogWritefln(' HIT FLOORY: floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
830 begin
831 // check if our ground wasn't moved since the last scan
833 begin
834 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
836 {$ENDIF}
838 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
840 begin
841 e_LogWritefln('force rescanning vpart at (%s,%s); oldFloorY=%s; floorY=%s', [x, y, oldFloorY, floorY]);
843 {$ENDIF}
847 // environment didn't changed
852 begin
853 // we're entered the liquid
855 // rescan, so we'll know when we'll exit the liquid
859 begin
860 // we're exited the liquid
862 // rescan, so we'll know when we'll enter something interesting
865 begin
871 end
872 else
873 begin
880 else
881 begin
882 // simple blood
890 _done:
891 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
896 // blood will dissolve in other liquids
898 begin
900 begin
903 else
911 end
912 else
913 begin
914 // water will disappear in any liquid
918 else
920 // dry water
930 // ////////////////////////////////////////////////////////////////////////// //
931 procedure g_GFX_SparkVel (fX, fY: Integer; count: Word; vx, vy: Integer; devX, devY: Byte); forward;
937 begin
939 begin
944 end
945 else
946 begin
951 var
957 begin
961 begin
963 exit;
964 end
966 begin
981 begin
983 begin
989 // check for level bounds
992 // in what environment we are starting in?
995 begin
996 // either in a wall, or in a liquid
999 end
1000 else
1001 begin
1009 begin
1037 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, color: Byte;
1039 var
1044 begin
1061 begin
1063 begin
1065 begin
1074 end
1075 else
1076 begin
1089 // check for level bounds
1092 // this hack will allow water spawned in water to fly out
1093 // it can happen when player fell from a huge height (see "DOOM2D.WAD:\MAP03", for example)
1095 begin
1096 // in what environment we are starting in?
1098 end
1099 else
1100 begin
1106 // color
1109 begin
1115 begin
1121 begin
1127 begin
1136 begin
1145 begin
1167 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
1168 begin
1173 // ////////////////////////////////////////////////////////////////////////// //
1175 var
1177 begin
1181 begin
1184 begin
1186 end
1187 else
1188 begin
1198 else
1203 {.$DEFINE D2F_DEBUG_BUBBLES}
1205 var
1209 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1212 {$ENDIF}
1213 begin
1226 begin
1228 begin
1234 // check for level bounds
1237 (*
1238 // don't spawn bubbles outside of the liquid
1239 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1240 Continue;
1241 *)
1243 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1244 // tracer will return `false` if we started outside of the liquid
1246 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1248 ptr := mapGrid.traceOrthoRayWhileIn(liquidx, liquidTopY, x, y, x, 0, GridTagWater or GridTagAcid1 or GridTagAcid2);
1250 e_LogWritefln('traceOrthoRayWhileIn: time=%s (%s); liquidTopY=%s', [Integer(stt), ptr, liquidTopY]);
1251 //
1255 e_LogWritefln('g_Map_TraceLiquidNonPrecise: time=%s (%s); liquidTopY=%s', [Integer(stt), nptr, liquidTopY]);
1257 {$ELSE}
1260 {$ENDIF}
1284 // ////////////////////////////////////////////////////////////////////////// //
1286 label
1287 _done;
1288 var
1292 begin
1298 //writeln('spark0: pos=(', x, ',', y, '); delta=(', dx, ',', dy, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1300 // apply gravity
1302 begin
1307 // flying
1309 begin
1310 // has some horizontal velocity
1316 begin
1318 // hit the wall; falling down vertically
1322 end
1324 begin
1325 // has some vertical velocity
1327 begin
1328 // flying up
1332 begin
1333 // oops, hit a ceiling
1338 // environment didn't changed
1339 end
1340 else
1341 begin
1342 // falling down
1346 begin
1347 // hit something except a floor?
1349 // otherwise, go to sleep
1352 // environment didn't changed
1357 _done:
1358 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
1363 begin
1368 //writeln('spark1: pos=(', x, ',', y, '); delta=(', velX:6:3, ',', velY:6:3, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1372 else
1377 // ////////////////////////////////////////////////////////////////////////// //
1379 var
1384 begin
1397 begin
1399 begin
1405 // check for level bounds
1408 // in what environment we are starting in?
1411 begin
1412 // either in a wall, or in a liquid
1413 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1414 //env := TEnvType.ELiquid;
1415 continue;
1416 end
1417 else
1418 begin
1426 begin
1453 var
1460 begin
1481 begin
1483 begin
1489 // check for level bounds
1492 // in what environment we are starting in?
1495 begin
1496 // either in a wall, or in a liquid
1497 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1498 //env := TEnvType.ELiquid;
1499 continue;
1500 end
1501 else
1502 begin
1530 // ////////////////////////////////////////////////////////////////////////// //
1532 var
1534 begin
1545 begin
1551 var
1553 begin
1557 begin
1559 Exit;
1563 begin
1566 end
1567 else
1568 begin
1576 var
1578 begin
1594 // ////////////////////////////////////////////////////////////////////////// //
1596 begin
1597 //g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
1598 //SetLength(gCollideMap, gMapInfo.Height+1);
1599 //for a := 0 to High(gCollideMap) do SetLength(gCollideMap[a], gMapInfo.Width+1);
1601 {$IFDEF HEADLESS}
1603 {$ENDIF}
1608 var
1610 begin
1617 begin
1623 // why not?
1629 // ////////////////////////////////////////////////////////////////////////// //
1631 var
1635 begin
1639 begin
1646 begin
1648 begin
1650 begin
1659 // clear awake map
1663 begin
1666 begin
1671 ONCEANIM_SMOKE:
1672 begin
1681 begin
1684 end
1685 else
1693 var
1695 begin
1699 begin
1713 begin
1715 begin
1718 begin
1733 begin
1736 begin
1738 begin
1740 begin