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
75 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, color: Byte;
77 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
89 var
94 //WARNING: only for Holmes!
101 // Wall: floorY is just before floor
102 // LiquidIn: floorY is liquid *start* (i.e. just in a liquid)
103 // LiquidOut: floorY is liquid *end* (i.e. just out of a liquid)
106 // note: this MUST be record, so we can keep it in
107 // dynamic array and has sequential memory access pattern
127 //k8: sorry, i have to emulate virtual methods this way, 'cause i haet `Object`
148 implementation
150 uses
151 {$IFNDEF HEADLESS}
152 r_render,
153 {$ENDIF}
159 const
162 var
165 // awakeMap has one bit for each map grid cell; on g_Mark,
166 // corresponding bits will be set, and in `think()` all particles
167 // in marked cells will be awaken
173 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
175 {$ENDIF}
178 begin
179 {$IFNDEF HEADLESS}
181 {$ENDIF}
184 // ////////////////////////////////////////////////////////////////////////// //
186 begin
187 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
192 begin
194 begin
196 end
197 else
198 begin
201 end
202 else
203 begin
206 {$ELSE}
208 {$ENDIF}
212 // ////////////////////////////////////////////////////////////////////////// //
213 // HACK! using mapgrid
215 begin
216 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
218 begin
222 {$ENDIF}
224 begin
232 begin
240 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
243 {$ENDIF}
244 //{$IF DEFINED(D2F_DEBUG)}
245 e_LogWritefln('particle awake map: %sx%s (for grid of size %sx%s)', [awakeMapW, awakeMapH, mapGrid.gridWidth, mapGrid.gridHeight]);
246 //{$ENDIF}
253 begin
257 begin
258 {$IF DEFINED(D2F_DEBUG)}
260 {$ENDIF}
262 end
263 else
264 begin
271 var
273 begin
277 begin
278 {$IF DEFINED(D2F_DEBUG)}
280 {$ENDIF}
288 // ////////////////////////////////////////////////////////////////////////// //
289 // st: set mark
290 // t: mark type
291 // currently unused
294 var
297 begin
303 // make some border, so we'll hit particles around the panel
314 // has something to do?
323 begin
325 begin
326 {$IF DEFINED(D2F_DEBUG)}
329 {$ENDIF}
337 // ////////////////////////////////////////////////////////////////////////// //
341 // remove velocities and acceleration
343 begin
344 // stop right there, you criminal scum!
352 // `true`: affected by air stream
354 var
357 begin
359 result := (pan <> nil) and WordBool(pan.PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT));
362 begin
364 LIFTTYPE_UP:
365 begin
371 LIFTTYPE_DOWN:
372 begin
376 LIFTTYPE_LEFT:
377 begin
381 LIFTTYPE_RIGHT:
382 begin
386 else
389 // awake
395 // switch to sleep mode
397 begin
399 begin
407 var
410 begin
412 // stuck in the wall? rescan, 'cause it can be mplat
414 begin
417 begin
418 // either in a wall, or in a liquid
420 begin
421 // we are in the wall, wtf?!
426 exit;
428 // we are in liquid, trace to liquid end
432 // are we in a liquid?
434 begin
435 // trace out of the liquid
436 //env := TEnvType.ELiquid;
438 //e_LogWritefln('tracing out of a liquid; floorY=%s; y=%s', [floorY, y]);
441 //e_LogWritefln(' traced out of a liquid; floorY=%s; y=%s', [floorY, y]);
442 end
443 else
444 begin
445 // in the air
447 //env := TEnvType.EAir;
448 pan := g_Map_traceToNearest(x, y, x, g_Map_MaxY, (GridTagObstacle or GridTagLiquid), @ex, @floorY);
450 begin
451 // wall or liquid
453 begin
454 // wall
456 end
457 else
458 begin
459 // liquid
463 end
464 else
465 begin
466 // out of the level; assume wall, but it doesn't really matter
475 var
477 begin
480 begin
488 begin
490 begin
491 //writeln('awaking particle at (', x, ',', y, ')');
493 begin
495 end
496 else
497 begin
498 // stuck to a wall, check if wall is still there
500 begin
503 begin
504 // a wall was moved out, start falling
511 end
512 else
513 begin
522 begin
525 // awake sleeping particle, if necessary
527 begin
529 {
530 case state of
531 TPartState.Sleeping, TPartState.Stuck:
532 if awmIsSet(x, y) then awake();
533 else
534 if (env = TEnvType.EWall) and awmIsSet(x, y) then awake();
535 end;
536 }
546 // ////////////////////////////////////////////////////////////////////////// //
549 begin
557 var
559 begin
563 // find next floor transition
565 // find `wallEndY`
566 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
570 begin
577 // `true`: didn't, get outa thinker
579 begin
586 begin
589 // if we're falling from ceiling, switch to normal mode
594 // switch to freefall mode
596 begin
603 begin
606 begin
609 end
610 else
611 begin
617 label
619 var
625 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
627 {$ENDIF}
628 begin
632 begin
633 // still check for air streams when sleeping (no)
634 if (state = TPartState.Sleeping) then begin {checkAirStreams();} goto _done; end; // so blood will dissolve
636 // process stuck particles
638 begin
639 // stuck to a ceiling?
641 begin
642 // yeah, stuck to a ceiling
644 // dropped from a ceiling?
646 begin
647 // yep
651 end
652 else
653 begin
654 // otherwise, try to drip
657 end
658 else
659 begin
660 // stuck to a wall
662 begin
663 // this can happen if mplat was moved out; find new `wallEndY`
666 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
668 _stuckagain:
669 // floor transition?
671 begin
675 begin
676 // check if our ground wasn't moved since the last scan
679 begin
683 // otherwise, do it again
687 begin
688 // rescan, so we'll know when we'll exit the liquid
692 begin
693 // rescan, so we'll know when we'll enter something interesting
699 // wall transition?
701 begin
702 // just unstuck from the wall, switch to freefall mode
705 end
706 else
707 begin
708 // otherwise, try to drip
712 // nope, process as usual
715 // it is important to have it here
721 // gravity, if not stuck
723 begin
726 _gravityagain:
727 // floor transition?
729 begin
732 begin
733 // check if our ground wasn't moved since the last scan
735 begin
740 // otherwise, nothing to do
743 begin
744 // rescan, so we'll know when we'll exit the liquid
749 begin
750 // rescan, so we'll know when we'll enter something interesting
752 if (floorType <> TFloorType.Wall) or (floorY <> y) then applyGravity(floorType = TFloorType.LiquidIn);
755 end
756 else
757 begin
758 // looks like we're in the air
763 // trace movement
765 begin
766 // has some horizontal velocity
770 begin
771 // do not stuck inside step
773 // check for step panel below
777 begin
778 // stick to panel edges
789 begin
790 // dunno yet
793 // check environment (air/liquid)
794 if (g_Map_PanelAtPoint(x, y, GridTagLiquid) <> nil) then env := TEnvType.ELiquid else env := TEnvType.EAir;
797 begin
800 else
801 begin
802 // we stuck
803 // the only case when we can have both ceiling and wall is corner; stick to wall in this case
804 // check if we stuck to a wall
807 begin
808 // stuck to a wall
810 end
811 else
812 begin
813 // stuck to a ceiling
818 end
820 begin
821 // has only vertical velocity
823 begin
824 // flying up
828 // environment didn't changed
829 end
830 else
831 begin
833 begin
834 // falling down
839 //e_LogWritefln('floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
841 begin
842 // floor transition
845 //e_LogWritefln(' HIT FLOORY: floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
848 begin
849 // check if our ground wasn't moved since the last scan
851 begin
852 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
854 {$ENDIF}
856 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
858 begin
859 e_LogWritefln('force rescanning vpart at (%s,%s); oldFloorY=%s; floorY=%s', [x, y, oldFloorY, floorY]);
861 {$ENDIF}
865 // environment didn't changed
870 begin
871 // we're entered the liquid
873 // rescan, so we'll know when we'll exit the liquid
877 begin
878 // we're exited the liquid
880 // rescan, so we'll know when we'll enter something interesting
883 begin
889 end
890 else
891 begin
898 else
899 begin
900 // simple blood
908 _done:
909 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
914 // blood will dissolve in other liquids
916 begin
918 begin
921 else
929 end
930 else
931 begin
932 // water will disappear in any liquid
936 else
938 // dry water
948 // ////////////////////////////////////////////////////////////////////////// //
949 procedure g_GFX_SparkVel (fX, fY: Integer; count: Word; vx, vy: Integer; devX, devY: Byte); forward;
955 begin
957 begin
962 end
963 else
964 begin
969 var
975 begin
979 begin
981 exit;
982 end
984 begin
999 begin
1001 begin
1007 // check for level bounds
1010 // in what environment we are starting in?
1013 begin
1014 // either in a wall, or in a liquid
1017 end
1018 else
1019 begin
1027 begin
1055 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, color: Byte;
1057 var
1062 begin
1079 begin
1081 begin
1083 begin
1092 end
1093 else
1094 begin
1107 // check for level bounds
1110 // this hack will allow water spawned in water to fly out
1111 // it can happen when player fell from a huge height (see "DOOM2D.WAD:\MAP03", for example)
1113 begin
1114 // in what environment we are starting in?
1116 end
1117 else
1118 begin
1124 // color
1127 begin
1133 begin
1139 begin
1145 begin
1154 begin
1163 begin
1185 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
1186 begin
1191 // ////////////////////////////////////////////////////////////////////////// //
1193 var
1195 begin
1199 begin
1202 begin
1204 end
1205 else
1206 begin
1216 else
1221 {.$DEFINE D2F_DEBUG_BUBBLES}
1223 var
1227 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1230 {$ENDIF}
1231 begin
1244 begin
1246 begin
1252 // check for level bounds
1255 (*
1256 // don't spawn bubbles outside of the liquid
1257 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1258 Continue;
1259 *)
1261 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1262 // tracer will return `false` if we started outside of the liquid
1264 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1266 ptr := mapGrid.traceOrthoRayWhileIn(liquidx, liquidTopY, x, y, x, 0, GridTagWater or GridTagAcid1 or GridTagAcid2);
1268 e_LogWritefln('traceOrthoRayWhileIn: time=%s (%s); liquidTopY=%s', [Integer(stt), ptr, liquidTopY]);
1269 //
1273 e_LogWritefln('g_Map_TraceLiquidNonPrecise: time=%s (%s); liquidTopY=%s', [Integer(stt), nptr, liquidTopY]);
1275 {$ELSE}
1278 {$ENDIF}
1302 // ////////////////////////////////////////////////////////////////////////// //
1304 label
1305 _done;
1306 var
1310 begin
1316 //writeln('spark0: pos=(', x, ',', y, '); delta=(', dx, ',', dy, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1318 // apply gravity
1320 begin
1325 // flying
1327 begin
1328 // has some horizontal velocity
1334 begin
1336 // hit the wall; falling down vertically
1340 end
1342 begin
1343 // has some vertical velocity
1345 begin
1346 // flying up
1350 begin
1351 // oops, hit a ceiling
1356 // environment didn't changed
1357 end
1358 else
1359 begin
1360 // falling down
1364 begin
1365 // hit something except a floor?
1367 // otherwise, go to sleep
1370 // environment didn't changed
1375 _done:
1376 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
1381 begin
1386 //writeln('spark1: pos=(', x, ',', y, '); delta=(', velX:6:3, ',', velY:6:3, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1390 else
1395 // ////////////////////////////////////////////////////////////////////////// //
1397 var
1402 begin
1415 begin
1417 begin
1423 // check for level bounds
1426 // in what environment we are starting in?
1429 begin
1430 // either in a wall, or in a liquid
1431 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1432 //env := TEnvType.ELiquid;
1433 continue;
1434 end
1435 else
1436 begin
1444 begin
1471 var
1478 begin
1499 begin
1501 begin
1507 // check for level bounds
1510 // in what environment we are starting in?
1513 begin
1514 // either in a wall, or in a liquid
1515 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1516 //env := TEnvType.ELiquid;
1517 continue;
1518 end
1519 else
1520 begin
1548 // ////////////////////////////////////////////////////////////////////////// //
1550 var
1552 begin
1563 begin
1567 // ////////////////////////////////////////////////////////////////////////// //
1569 begin
1570 //g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
1571 //SetLength(gCollideMap, gMapInfo.Height+1);
1572 //for a := 0 to High(gCollideMap) do SetLength(gCollideMap[a], gMapInfo.Width+1);
1574 {$IFDEF HEADLESS}
1576 {$ENDIF}
1581 var
1583 begin
1590 // why not?
1596 // ////////////////////////////////////////////////////////////////////////// //
1598 var
1602 begin
1606 begin
1613 begin
1615 begin
1617 begin
1626 // clear awake map