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
24 const
74 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, color: Byte;
76 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
88 var
93 //WARNING: only for Holmes!
100 // Wall: floorY is just before floor
101 // LiquidIn: floorY is liquid *start* (i.e. just in a liquid)
102 // LiquidOut: floorY is liquid *end* (i.e. just out of a liquid)
105 // note: this MUST be record, so we can keep it in
106 // dynamic array and has sequential memory access pattern
126 //k8: sorry, i have to emulate virtual methods this way, 'cause i haet `Object`
147 implementation
149 uses
150 {$IFDEF ENABLE_RENDER}
151 r_render,
152 {$ENDIF}
155 ;
158 const
161 var
164 // awakeMap has one bit for each map grid cell; on g_Mark,
165 // corresponding bits will be set, and in `think()` all particles
166 // in marked cells will be awaken
172 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
174 {$ENDIF}
177 begin
178 {$IFDEF ENABLE_RENDER}
180 {$ENDIF}
183 // ////////////////////////////////////////////////////////////////////////// //
185 begin
186 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
191 begin
193 begin
195 end
196 else
197 begin
200 end
201 else
202 begin
205 {$ELSE}
207 {$ENDIF}
211 // ////////////////////////////////////////////////////////////////////////// //
212 // HACK! using mapgrid
214 begin
215 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
217 begin
221 {$ENDIF}
223 begin
231 begin
239 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
242 {$ENDIF}
243 //{$IF DEFINED(D2F_DEBUG)}
244 e_LogWritefln('particle awake map: %sx%s (for grid of size %sx%s)', [awakeMapW, awakeMapH, mapGrid.gridWidth, mapGrid.gridHeight]);
245 //{$ENDIF}
252 begin
256 begin
257 {$IF DEFINED(D2F_DEBUG)}
259 {$ENDIF}
261 end
262 else
263 begin
270 var
272 begin
276 begin
277 {$IF DEFINED(D2F_DEBUG)}
279 {$ENDIF}
287 // ////////////////////////////////////////////////////////////////////////// //
288 // st: set mark
289 // t: mark type
290 // currently unused
293 var
296 begin
302 // make some border, so we'll hit particles around the panel
313 // has something to do?
322 begin
324 begin
325 {$IF DEFINED(D2F_DEBUG)}
328 {$ENDIF}
336 // ////////////////////////////////////////////////////////////////////////// //
340 // remove velocities and acceleration
342 begin
343 // stop right there, you criminal scum!
351 // `true`: affected by air stream
353 var
356 begin
358 result := (pan <> nil) and WordBool(pan.PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT));
361 begin
363 LIFTTYPE_UP:
364 begin
370 LIFTTYPE_DOWN:
371 begin
375 LIFTTYPE_LEFT:
376 begin
380 LIFTTYPE_RIGHT:
381 begin
385 else
388 // awake
394 // switch to sleep mode
396 begin
398 begin
406 var
409 begin
411 // stuck in the wall? rescan, 'cause it can be mplat
413 begin
416 begin
417 // either in a wall, or in a liquid
419 begin
420 // we are in the wall, wtf?!
425 exit;
427 // we are in liquid, trace to liquid end
431 // are we in a liquid?
433 begin
434 // trace out of the liquid
435 //env := TEnvType.ELiquid;
437 //e_LogWritefln('tracing out of a liquid; floorY=%s; y=%s', [floorY, y]);
440 //e_LogWritefln(' traced out of a liquid; floorY=%s; y=%s', [floorY, y]);
441 end
442 else
443 begin
444 // in the air
446 //env := TEnvType.EAir;
447 pan := g_Map_traceToNearest(x, y, x, g_Map_MaxY, (GridTagObstacle or GridTagLiquid), @ex, @floorY);
449 begin
450 // wall or liquid
452 begin
453 // wall
455 end
456 else
457 begin
458 // liquid
462 end
463 else
464 begin
465 // out of the level; assume wall, but it doesn't really matter
474 var
476 begin
479 begin
487 begin
489 begin
490 //writeln('awaking particle at (', x, ',', y, ')');
492 begin
494 end
495 else
496 begin
497 // stuck to a wall, check if wall is still there
499 begin
502 begin
503 // a wall was moved out, start falling
510 end
511 else
512 begin
521 begin
524 // awake sleeping particle, if necessary
526 begin
528 {
529 case state of
530 TPartState.Sleeping, TPartState.Stuck:
531 if awmIsSet(x, y) then awake();
532 else
533 if (env = TEnvType.EWall) and awmIsSet(x, y) then awake();
534 end;
535 }
545 // ////////////////////////////////////////////////////////////////////////// //
548 begin
556 var
558 begin
562 // find next floor transition
564 // find `wallEndY`
565 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
569 begin
576 // `true`: didn't, get outa thinker
578 begin
585 begin
588 // if we're falling from ceiling, switch to normal mode
593 // switch to freefall mode
595 begin
602 begin
605 begin
608 end
609 else
610 begin
616 label
618 var
624 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
626 {$ENDIF}
627 begin
631 begin
632 // still check for air streams when sleeping (no)
633 if (state = TPartState.Sleeping) then begin {checkAirStreams();} goto _done; end; // so blood will dissolve
635 // process stuck particles
637 begin
638 // stuck to a ceiling?
640 begin
641 // yeah, stuck to a ceiling
643 // dropped from a ceiling?
645 begin
646 // yep
650 end
651 else
652 begin
653 // otherwise, try to drip
656 end
657 else
658 begin
659 // stuck to a wall
661 begin
662 // this can happen if mplat was moved out; find new `wallEndY`
665 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
667 _stuckagain:
668 // floor transition?
670 begin
674 begin
675 // check if our ground wasn't moved since the last scan
678 begin
682 // otherwise, do it again
686 begin
687 // rescan, so we'll know when we'll exit the liquid
691 begin
692 // rescan, so we'll know when we'll enter something interesting
698 // wall transition?
700 begin
701 // just unstuck from the wall, switch to freefall mode
704 end
705 else
706 begin
707 // otherwise, try to drip
711 // nope, process as usual
714 // it is important to have it here
720 // gravity, if not stuck
722 begin
725 _gravityagain:
726 // floor transition?
728 begin
731 begin
732 // check if our ground wasn't moved since the last scan
734 begin
739 // otherwise, nothing to do
742 begin
743 // rescan, so we'll know when we'll exit the liquid
748 begin
749 // rescan, so we'll know when we'll enter something interesting
751 if (floorType <> TFloorType.Wall) or (floorY <> y) then applyGravity(floorType = TFloorType.LiquidIn);
754 end
755 else
756 begin
757 // looks like we're in the air
762 // trace movement
764 begin
765 // has some horizontal velocity
769 begin
770 // do not stuck inside step
772 // check for step panel below
776 begin
777 // stick to panel edges
788 begin
789 // dunno yet
792 // check environment (air/liquid)
793 if (g_Map_PanelAtPoint(x, y, GridTagLiquid) <> nil) then env := TEnvType.ELiquid else env := TEnvType.EAir;
796 begin
799 else
800 begin
801 // we stuck
802 // the only case when we can have both ceiling and wall is corner; stick to wall in this case
803 // check if we stuck to a wall
806 begin
807 // stuck to a wall
809 end
810 else
811 begin
812 // stuck to a ceiling
817 end
819 begin
820 // has only vertical velocity
822 begin
823 // flying up
827 // environment didn't changed
828 end
829 else
830 begin
832 begin
833 // falling down
838 //e_LogWritefln('floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
840 begin
841 // floor transition
844 //e_LogWritefln(' HIT FLOORY: floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
847 begin
848 // check if our ground wasn't moved since the last scan
850 begin
851 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
853 {$ENDIF}
855 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
857 begin
858 e_LogWritefln('force rescanning vpart at (%s,%s); oldFloorY=%s; floorY=%s', [x, y, oldFloorY, floorY]);
860 {$ENDIF}
864 // environment didn't changed
869 begin
870 // we're entered the liquid
872 // rescan, so we'll know when we'll exit the liquid
876 begin
877 // we're exited the liquid
879 // rescan, so we'll know when we'll enter something interesting
882 begin
888 end
889 else
890 begin
897 else
898 begin
899 // simple blood
907 _done:
908 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
913 // blood will dissolve in other liquids
915 begin
917 begin
920 else
928 end
929 else
930 begin
931 // water will disappear in any liquid
935 else
937 // dry water
947 // ////////////////////////////////////////////////////////////////////////// //
948 procedure g_GFX_SparkVel (fX, fY: Integer; count: Word; vx, vy: Integer; devX, devY: Byte); forward;
954 begin
956 begin
961 end
962 else
963 begin
968 var
974 begin
978 begin
980 exit;
981 end
983 begin
998 begin
1000 begin
1006 // check for level bounds
1009 // in what environment we are starting in?
1012 begin
1013 // either in a wall, or in a liquid
1016 end
1017 else
1018 begin
1026 begin
1054 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, color: Byte;
1056 var
1061 begin
1078 begin
1080 begin
1082 begin
1091 end
1092 else
1093 begin
1106 // check for level bounds
1109 // this hack will allow water spawned in water to fly out
1110 // it can happen when player fell from a huge height (see "DOOM2D.WAD:\MAP03", for example)
1112 begin
1113 // in what environment we are starting in?
1115 end
1116 else
1117 begin
1123 // color
1126 begin
1132 begin
1138 begin
1144 begin
1153 begin
1162 begin
1184 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
1185 begin
1190 // ////////////////////////////////////////////////////////////////////////// //
1192 var
1194 begin
1198 begin
1201 begin
1203 end
1204 else
1205 begin
1215 else
1220 {.$DEFINE D2F_DEBUG_BUBBLES}
1222 var
1226 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1229 {$ENDIF}
1230 begin
1243 begin
1245 begin
1251 // check for level bounds
1254 (*
1255 // don't spawn bubbles outside of the liquid
1256 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1257 Continue;
1258 *)
1260 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1261 // tracer will return `false` if we started outside of the liquid
1263 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1265 ptr := mapGrid.traceOrthoRayWhileIn(liquidx, liquidTopY, x, y, x, 0, GridTagWater or GridTagAcid1 or GridTagAcid2);
1267 e_LogWritefln('traceOrthoRayWhileIn: time=%s (%s); liquidTopY=%s', [Integer(stt), ptr, liquidTopY]);
1268 //
1272 e_LogWritefln('g_Map_TraceLiquidNonPrecise: time=%s (%s); liquidTopY=%s', [Integer(stt), nptr, liquidTopY]);
1274 {$ELSE}
1277 {$ENDIF}
1301 // ////////////////////////////////////////////////////////////////////////// //
1303 label
1304 _done;
1305 var
1309 begin
1315 //writeln('spark0: pos=(', x, ',', y, '); delta=(', dx, ',', dy, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1317 // apply gravity
1319 begin
1324 // flying
1326 begin
1327 // has some horizontal velocity
1333 begin
1335 // hit the wall; falling down vertically
1339 end
1341 begin
1342 // has some vertical velocity
1344 begin
1345 // flying up
1349 begin
1350 // oops, hit a ceiling
1355 // environment didn't changed
1356 end
1357 else
1358 begin
1359 // falling down
1363 begin
1364 // hit something except a floor?
1366 // otherwise, go to sleep
1369 // environment didn't changed
1374 _done:
1375 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
1380 begin
1385 //writeln('spark1: pos=(', x, ',', y, '); delta=(', velX:6:3, ',', velY:6:3, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1389 else
1394 // ////////////////////////////////////////////////////////////////////////// //
1396 var
1401 begin
1414 begin
1416 begin
1422 // check for level bounds
1425 // in what environment we are starting in?
1428 begin
1429 // either in a wall, or in a liquid
1430 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1431 //env := TEnvType.ELiquid;
1432 continue;
1433 end
1434 else
1435 begin
1443 begin
1470 var
1477 begin
1498 begin
1500 begin
1506 // check for level bounds
1509 // in what environment we are starting in?
1512 begin
1513 // either in a wall, or in a liquid
1514 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1515 //env := TEnvType.ELiquid;
1516 continue;
1517 end
1518 else
1519 begin
1547 // ////////////////////////////////////////////////////////////////////////// //
1549 var
1551 begin
1562 begin
1566 // ////////////////////////////////////////////////////////////////////////// //
1568 begin
1569 //g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
1570 //SetLength(gCollideMap, gMapInfo.Height+1);
1571 //for a := 0 to High(gCollideMap) do SetLength(gCollideMap[a], gMapInfo.Width+1);
1573 {$IFDEF HEADLESS}
1575 {$ENDIF}
1580 var
1582 begin
1589 // why not?
1595 // ////////////////////////////////////////////////////////////////////////// //
1597 var
1601 begin
1605 begin
1612 begin
1614 begin
1616 begin
1625 // clear awake map