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);
66 var
71 //WARNING: only for Holmes!
78 // Wall: floorY is just before floor
79 // LiquidIn: floorY is liquid *start* (i.e. just in a liquid)
80 // LiquidOut: floorY is liquid *end* (i.e. just out of a liquid)
83 // note: this MUST be record, so we can keep it in
84 // dynamic array and has sequential memory access pattern
104 //k8: sorry, i have to emulate virtual methods this way, 'cause i haet `Object`
125 implementation
127 uses
133 const
136 var
139 // awakeMap has one bit for each map grid cell; on g_Mark,
140 // corresponding bits will be set, and in `think()` all particles
141 // in marked cells will be awaken
147 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
149 {$ENDIF}
152 // ////////////////////////////////////////////////////////////////////////// //
154 begin
155 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
160 begin
162 begin
164 end
165 else
166 begin
169 end
170 else
171 begin
174 {$ELSE}
176 {$ENDIF}
180 // ////////////////////////////////////////////////////////////////////////// //
181 // HACK! using mapgrid
183 begin
184 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
186 begin
190 {$ENDIF}
192 begin
200 begin
208 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
211 {$ENDIF}
212 //{$IF DEFINED(D2F_DEBUG)}
213 e_LogWritefln('particle awake map: %sx%s (for grid of size %sx%s)', [awakeMapW, awakeMapH, mapGrid.gridWidth, mapGrid.gridHeight]);
214 //{$ENDIF}
221 begin
225 begin
226 {$IF DEFINED(D2F_DEBUG)}
228 {$ENDIF}
230 end
231 else
232 begin
239 var
241 begin
245 begin
246 {$IF DEFINED(D2F_DEBUG)}
248 {$ENDIF}
256 // ////////////////////////////////////////////////////////////////////////// //
257 // st: set mark
258 // t: mark type
259 // currently unused
262 var
265 begin
271 // make some border, so we'll hit particles around the panel
282 // has something to do?
291 begin
293 begin
294 {$IF DEFINED(D2F_DEBUG)}
297 {$ENDIF}
305 // ////////////////////////////////////////////////////////////////////////// //
309 // remove velocities and acceleration
311 begin
312 // stop right there, you criminal scum!
320 // `true`: affected by air stream
322 var
325 begin
327 result := (pan <> nil) and WordBool(pan.PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT));
330 begin
332 LIFTTYPE_UP:
333 begin
339 LIFTTYPE_DOWN:
340 begin
344 LIFTTYPE_LEFT:
345 begin
349 LIFTTYPE_RIGHT:
350 begin
354 else
357 // awake
363 // switch to sleep mode
365 begin
367 begin
375 var
378 begin
380 // stuck in the wall? rescan, 'cause it can be mplat
382 begin
385 begin
386 // either in a wall, or in a liquid
388 begin
389 // we are in the wall, wtf?!
394 exit;
396 // we are in liquid, trace to liquid end
400 // are we in a liquid?
402 begin
403 // trace out of the liquid
404 //env := TEnvType.ELiquid;
406 //e_LogWritefln('tracing out of a liquid; floorY=%s; y=%s', [floorY, y]);
409 //e_LogWritefln(' traced out of a liquid; floorY=%s; y=%s', [floorY, y]);
410 end
411 else
412 begin
413 // in the air
415 //env := TEnvType.EAir;
416 pan := g_Map_traceToNearest(x, y, x, g_Map_MaxY, (GridTagObstacle or GridTagLiquid), @ex, @floorY);
418 begin
419 // wall or liquid
421 begin
422 // wall
424 end
425 else
426 begin
427 // liquid
431 end
432 else
433 begin
434 // out of the level; assume wall, but it doesn't really matter
443 var
445 begin
448 begin
456 begin
458 begin
459 //writeln('awaking particle at (', x, ',', y, ')');
461 begin
463 end
464 else
465 begin
466 // stuck to a wall, check if wall is still there
468 begin
471 begin
472 // a wall was moved out, start falling
479 end
480 else
481 begin
490 begin
493 // awake sleeping particle, if necessary
495 begin
497 {
498 case state of
499 TPartState.Sleeping, TPartState.Stuck:
500 if awmIsSet(x, y) then awake();
501 else
502 if (env = TEnvType.EWall) and awmIsSet(x, y) then awake();
503 end;
504 }
514 // ////////////////////////////////////////////////////////////////////////// //
517 begin
525 var
527 begin
531 // find next floor transition
533 // find `wallEndY`
534 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
538 begin
545 // `true`: didn't, get outa thinker
547 begin
554 begin
557 // if we're falling from ceiling, switch to normal mode
562 // switch to freefall mode
564 begin
571 begin
574 begin
577 end
578 else
579 begin
585 label
587 var
593 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
595 {$ENDIF}
596 begin
600 begin
601 // still check for air streams when sleeping (no)
602 if (state = TPartState.Sleeping) then begin {checkAirStreams();} goto _done; end; // so blood will dissolve
604 // process stuck particles
606 begin
607 // stuck to a ceiling?
609 begin
610 // yeah, stuck to a ceiling
612 // dropped from a ceiling?
614 begin
615 // yep
619 end
620 else
621 begin
622 // otherwise, try to drip
625 end
626 else
627 begin
628 // stuck to a wall
630 begin
631 // this can happen if mplat was moved out; find new `wallEndY`
634 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
636 _stuckagain:
637 // floor transition?
639 begin
643 begin
644 // check if our ground wasn't moved since the last scan
647 begin
651 // otherwise, do it again
655 begin
656 // rescan, so we'll know when we'll exit the liquid
660 begin
661 // rescan, so we'll know when we'll enter something interesting
667 // wall transition?
669 begin
670 // just unstuck from the wall, switch to freefall mode
673 end
674 else
675 begin
676 // otherwise, try to drip
680 // nope, process as usual
683 // it is important to have it here
689 // gravity, if not stuck
691 begin
694 _gravityagain:
695 // floor transition?
697 begin
700 begin
701 // check if our ground wasn't moved since the last scan
703 begin
708 // otherwise, nothing to do
711 begin
712 // rescan, so we'll know when we'll exit the liquid
717 begin
718 // rescan, so we'll know when we'll enter something interesting
720 if (floorType <> TFloorType.Wall) or (floorY <> y) then applyGravity(floorType = TFloorType.LiquidIn);
723 end
724 else
725 begin
726 // looks like we're in the air
731 // trace movement
733 begin
734 // has some horizontal velocity
738 begin
739 // do not stuck inside step
741 // check for step panel below
745 begin
746 // stick to panel edges
757 begin
758 // dunno yet
761 // check environment (air/liquid)
762 if (g_Map_PanelAtPoint(x, y, GridTagLiquid) <> nil) then env := TEnvType.ELiquid else env := TEnvType.EAir;
765 begin
768 else
769 begin
770 // we stuck
771 // the only case when we can have both ceiling and wall is corner; stick to wall in this case
772 // check if we stuck to a wall
775 begin
776 // stuck to a wall
778 end
779 else
780 begin
781 // stuck to a ceiling
786 end
788 begin
789 // has only vertical velocity
791 begin
792 // flying up
796 // environment didn't changed
797 end
798 else
799 begin
801 begin
802 // falling down
807 //e_LogWritefln('floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
809 begin
810 // floor transition
813 //e_LogWritefln(' HIT FLOORY: floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
816 begin
817 // check if our ground wasn't moved since the last scan
819 begin
820 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
822 {$ENDIF}
824 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
826 begin
827 e_LogWritefln('force rescanning vpart at (%s,%s); oldFloorY=%s; floorY=%s', [x, y, oldFloorY, floorY]);
829 {$ENDIF}
833 // environment didn't changed
838 begin
839 // we're entered the liquid
841 // rescan, so we'll know when we'll exit the liquid
845 begin
846 // we're exited the liquid
848 // rescan, so we'll know when we'll enter something interesting
851 begin
857 end
858 else
859 begin
866 else
867 begin
868 // simple blood
876 _done:
877 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
882 // blood will dissolve in other liquids
884 begin
886 begin
889 else
897 end
898 else
899 begin
900 // water will disappear in any liquid
904 else
906 // dry water
916 // ////////////////////////////////////////////////////////////////////////// //
917 procedure g_GFX_SparkVel (fX, fY: Integer; count: Word; vx, vy: Integer; devX, devY: Byte); forward;
923 begin
925 begin
930 end
931 else
932 begin
937 var
943 begin
947 begin
949 exit;
950 end
952 begin
967 begin
969 begin
975 // check for level bounds
978 // in what environment we are starting in?
981 begin
982 // either in a wall, or in a liquid
985 end
986 else
987 begin
995 begin
1023 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, color: Byte;
1025 var
1030 begin
1047 begin
1049 begin
1051 begin
1060 end
1061 else
1062 begin
1075 // check for level bounds
1078 // this hack will allow water spawned in water to fly out
1079 // it can happen when player fell from a huge height (see "DOOM2D.WAD:\MAP03", for example)
1081 begin
1082 // in what environment we are starting in?
1084 end
1085 else
1086 begin
1092 // color
1095 begin
1101 begin
1107 begin
1113 begin
1122 begin
1131 begin
1153 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
1154 begin
1159 // ////////////////////////////////////////////////////////////////////////// //
1161 var
1163 begin
1167 begin
1170 begin
1172 end
1173 else
1174 begin
1184 else
1189 {.$DEFINE D2F_DEBUG_BUBBLES}
1191 var
1195 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1198 {$ENDIF}
1199 begin
1212 begin
1214 begin
1220 // check for level bounds
1223 (*
1224 // don't spawn bubbles outside of the liquid
1225 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1226 Continue;
1227 *)
1229 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1230 // tracer will return `false` if we started outside of the liquid
1232 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1234 ptr := mapGrid.traceOrthoRayWhileIn(liquidx, liquidTopY, x, y, x, 0, GridTagWater or GridTagAcid1 or GridTagAcid2);
1236 e_LogWritefln('traceOrthoRayWhileIn: time=%s (%s); liquidTopY=%s', [Integer(stt), ptr, liquidTopY]);
1237 //
1241 e_LogWritefln('g_Map_TraceLiquidNonPrecise: time=%s (%s); liquidTopY=%s', [Integer(stt), nptr, liquidTopY]);
1243 {$ELSE}
1246 {$ENDIF}
1270 // ////////////////////////////////////////////////////////////////////////// //
1272 label
1273 _done;
1274 var
1278 begin
1284 //writeln('spark0: pos=(', x, ',', y, '); delta=(', dx, ',', dy, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1286 // apply gravity
1288 begin
1293 // flying
1295 begin
1296 // has some horizontal velocity
1302 begin
1304 // hit the wall; falling down vertically
1308 end
1310 begin
1311 // has some vertical velocity
1313 begin
1314 // flying up
1318 begin
1319 // oops, hit a ceiling
1324 // environment didn't changed
1325 end
1326 else
1327 begin
1328 // falling down
1332 begin
1333 // hit something except a floor?
1335 // otherwise, go to sleep
1338 // environment didn't changed
1343 _done:
1344 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
1349 begin
1354 //writeln('spark1: pos=(', x, ',', y, '); delta=(', velX:6:3, ',', velY:6:3, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1358 else
1363 // ////////////////////////////////////////////////////////////////////////// //
1365 var
1370 begin
1383 begin
1385 begin
1391 // check for level bounds
1394 // in what environment we are starting in?
1397 begin
1398 // either in a wall, or in a liquid
1399 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1400 //env := TEnvType.ELiquid;
1401 continue;
1402 end
1403 else
1404 begin
1412 begin
1439 var
1446 begin
1467 begin
1469 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
1535 // ////////////////////////////////////////////////////////////////////////// //
1537 begin
1538 //g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
1539 //SetLength(gCollideMap, gMapInfo.Height+1);
1540 //for a := 0 to High(gCollideMap) do SetLength(gCollideMap[a], gMapInfo.Width+1);
1542 {$IFDEF HEADLESS}
1544 {$ENDIF}
1549 var
1551 begin
1558 // why not?
1564 // ////////////////////////////////////////////////////////////////////////// //
1566 var
1570 begin
1574 begin
1581 begin
1583 begin
1585 begin
1594 // clear awake map