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
138 {$INCLUDE ../nogl/noGLuses.inc}
144 const
147 var
150 // awakeMap has one bit for each map grid cell; on g_Mark,
151 // corresponding bits will be set, and in `think()` all particles
152 // in marked cells will be awaken
158 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
160 {$ENDIF}
163 // ////////////////////////////////////////////////////////////////////////// //
165 begin
166 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
171 begin
173 begin
175 end
176 else
177 begin
180 end
181 else
182 begin
185 {$ELSE}
187 {$ENDIF}
191 // ////////////////////////////////////////////////////////////////////////// //
192 // HACK! using mapgrid
194 begin
195 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
197 begin
201 {$ENDIF}
203 begin
211 begin
219 {$IF DEFINED(D2F_DEBUG_PART_AWAKE)}
222 {$ENDIF}
223 //{$IF DEFINED(D2F_DEBUG)}
224 e_LogWritefln('particle awake map: %sx%s (for grid of size %sx%s)', [awakeMapW, awakeMapH, mapGrid.gridWidth, mapGrid.gridHeight]);
225 //{$ENDIF}
232 begin
236 begin
237 {$IF DEFINED(D2F_DEBUG)}
239 {$ENDIF}
241 end
242 else
243 begin
250 var
252 begin
256 begin
257 {$IF DEFINED(D2F_DEBUG)}
259 {$ENDIF}
267 // ////////////////////////////////////////////////////////////////////////// //
268 // st: set mark
269 // t: mark type
270 // currently unused
273 var
276 begin
282 // make some border, so we'll hit particles around the panel
293 // has something to do?
302 begin
304 begin
305 {$IF DEFINED(D2F_DEBUG)}
308 {$ENDIF}
316 // ////////////////////////////////////////////////////////////////////////// //
320 // remove velocities and acceleration
322 begin
323 // stop right there, you criminal scum!
331 // `true`: affected by air stream
333 var
336 begin
338 result := (pan <> nil) and WordBool(pan.PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT));
341 begin
343 LIFTTYPE_UP:
344 begin
350 LIFTTYPE_DOWN:
351 begin
355 LIFTTYPE_LEFT:
356 begin
360 LIFTTYPE_RIGHT:
361 begin
365 else
368 // awake
374 // switch to sleep mode
376 begin
378 begin
386 var
389 begin
391 // stuck in the wall? rescan, 'cause it can be mplat
393 begin
396 begin
397 // either in a wall, or in a liquid
399 begin
400 // we are in the wall, wtf?!
405 exit;
407 // we are in liquid, trace to liquid end
411 // are we in a liquid?
413 begin
414 // trace out of the liquid
415 //env := TEnvType.ELiquid;
417 //e_LogWritefln('tracing out of a liquid; floorY=%s; y=%s', [floorY, y]);
420 //e_LogWritefln(' traced out of a liquid; floorY=%s; y=%s', [floorY, y]);
421 end
422 else
423 begin
424 // in the air
426 //env := TEnvType.EAir;
427 pan := g_Map_traceToNearest(x, y, x, g_Map_MaxY, (GridTagObstacle or GridTagLiquid), @ex, @floorY);
429 begin
430 // wall or liquid
432 begin
433 // wall
435 end
436 else
437 begin
438 // liquid
442 end
443 else
444 begin
445 // out of the level; assume wall, but it doesn't really matter
454 var
456 begin
459 begin
467 begin
469 begin
470 //writeln('awaking particle at (', x, ',', y, ')');
472 begin
474 end
475 else
476 begin
477 // stuck to a wall, check if wall is still there
479 begin
482 begin
483 // a wall was moved out, start falling
490 end
491 else
492 begin
501 begin
504 // awake sleeping particle, if necessary
506 begin
508 {
509 case state of
510 TPartState.Sleeping, TPartState.Stuck:
511 if awmIsSet(x, y) then awake();
512 else
513 if (env = TEnvType.EWall) and awmIsSet(x, y) then awake();
514 end;
515 }
525 // ////////////////////////////////////////////////////////////////////////// //
528 begin
536 var
538 begin
542 // find next floor transition
544 // find `wallEndY`
545 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
549 begin
556 // `true`: didn't, get outa thinker
558 begin
565 begin
568 // if we're falling from ceiling, switch to normal mode
573 // switch to freefall mode
575 begin
582 begin
585 begin
588 end
589 else
590 begin
596 label
598 var
604 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
606 {$ENDIF}
607 begin
611 begin
612 // still check for air streams when sleeping (no)
613 if (state = TPartState.Sleeping) then begin {checkAirStreams();} goto _done; end; // so blood will dissolve
615 // process stuck particles
617 begin
618 // stuck to a ceiling?
620 begin
621 // yeah, stuck to a ceiling
623 // dropped from a ceiling?
625 begin
626 // yep
630 end
631 else
632 begin
633 // otherwise, try to drip
636 end
637 else
638 begin
639 // stuck to a wall
641 begin
642 // this can happen if mplat was moved out; find new `wallEndY`
645 mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep));
647 _stuckagain:
648 // floor transition?
650 begin
654 begin
655 // check if our ground wasn't moved since the last scan
658 begin
662 // otherwise, do it again
666 begin
667 // rescan, so we'll know when we'll exit the liquid
671 begin
672 // rescan, so we'll know when we'll enter something interesting
678 // wall transition?
680 begin
681 // just unstuck from the wall, switch to freefall mode
684 end
685 else
686 begin
687 // otherwise, try to drip
691 // nope, process as usual
694 // it is important to have it here
700 // gravity, if not stuck
702 begin
705 _gravityagain:
706 // floor transition?
708 begin
711 begin
712 // check if our ground wasn't moved since the last scan
714 begin
719 // otherwise, nothing to do
722 begin
723 // rescan, so we'll know when we'll exit the liquid
728 begin
729 // rescan, so we'll know when we'll enter something interesting
731 if (floorType <> TFloorType.Wall) or (floorY <> y) then applyGravity(floorType = TFloorType.LiquidIn);
734 end
735 else
736 begin
737 // looks like we're in the air
742 // trace movement
744 begin
745 // has some horizontal velocity
749 begin
750 // do not stuck inside step
752 // check for step panel below
756 begin
757 // stick to panel edges
768 begin
769 // dunno yet
772 // check environment (air/liquid)
773 if (g_Map_PanelAtPoint(x, y, GridTagLiquid) <> nil) then env := TEnvType.ELiquid else env := TEnvType.EAir;
776 begin
779 else
780 begin
781 // we stuck
782 // the only case when we can have both ceiling and wall is corner; stick to wall in this case
783 // check if we stuck to a wall
786 begin
787 // stuck to a wall
789 end
790 else
791 begin
792 // stuck to a ceiling
797 end
799 begin
800 // has only vertical velocity
802 begin
803 // flying up
807 // environment didn't changed
808 end
809 else
810 begin
812 begin
813 // falling down
818 //e_LogWritefln('floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
820 begin
821 // floor transition
824 //e_LogWritefln(' HIT FLOORY: floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]);
827 begin
828 // check if our ground wasn't moved since the last scan
830 begin
831 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
833 {$ENDIF}
835 {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)}
837 begin
838 e_LogWritefln('force rescanning vpart at (%s,%s); oldFloorY=%s; floorY=%s', [x, y, oldFloorY, floorY]);
840 {$ENDIF}
844 // environment didn't changed
849 begin
850 // we're entered the liquid
852 // rescan, so we'll know when we'll exit the liquid
856 begin
857 // we're exited the liquid
859 // rescan, so we'll know when we'll enter something interesting
862 begin
868 end
869 else
870 begin
877 else
878 begin
879 // simple blood
887 _done:
888 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
893 // blood will dissolve in other liquids
895 begin
897 begin
900 else
908 end
909 else
910 begin
911 // water will disappear in any liquid
915 else
917 // dry water
927 // ////////////////////////////////////////////////////////////////////////// //
928 procedure g_GFX_SparkVel (fX, fY: Integer; count: Word; vx, vy: Integer; devX, devY: Byte); forward;
934 begin
936 begin
941 end
942 else
943 begin
948 var
954 begin
958 begin
960 exit;
961 end
963 begin
978 begin
980 begin
986 // check for level bounds
989 // in what environment we are starting in?
992 begin
993 // either in a wall, or in a liquid
996 end
997 else
998 begin
1006 begin
1034 procedure g_GFX_Water (fX, fY: Integer; count: Word; fVelX, fVelY: Single; devX, devY, color: Byte;
1036 var
1041 begin
1058 begin
1060 begin
1062 begin
1071 end
1072 else
1073 begin
1086 // check for level bounds
1089 // this hack will allow water spawned in water to fly out
1090 // it can happen when player fell from a huge height (see "DOOM2D.WAD:\MAP03", for example)
1092 begin
1093 // in what environment we are starting in?
1095 end
1096 else
1097 begin
1103 // color
1106 begin
1112 begin
1118 begin
1124 begin
1133 begin
1142 begin
1164 procedure g_GFX_SimpleWater (fX, fY: Integer; count: Word; fVelX, fVelY: Single; defColor, cr, cg, cb: Byte);
1165 begin
1170 // ////////////////////////////////////////////////////////////////////////// //
1172 var
1174 begin
1178 begin
1181 begin
1183 end
1184 else
1185 begin
1195 else
1200 {.$DEFINE D2F_DEBUG_BUBBLES}
1202 var
1206 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1209 {$ENDIF}
1210 begin
1223 begin
1225 begin
1231 // check for level bounds
1234 (*
1235 // don't spawn bubbles outside of the liquid
1236 if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
1237 Continue;
1238 *)
1240 // trace liquid, so we'll know where it ends; do it in 8px steps for speed
1241 // tracer will return `false` if we started outside of the liquid
1243 {$IF DEFINED(D2F_DEBUG_BUBBLES)}
1245 ptr := mapGrid.traceOrthoRayWhileIn(liquidx, liquidTopY, x, y, x, 0, GridTagWater or GridTagAcid1 or GridTagAcid2);
1247 e_LogWritefln('traceOrthoRayWhileIn: time=%s (%s); liquidTopY=%s', [Integer(stt), ptr, liquidTopY]);
1248 //
1252 e_LogWritefln('g_Map_TraceLiquidNonPrecise: time=%s (%s); liquidTopY=%s', [Integer(stt), nptr, liquidTopY]);
1254 {$ELSE}
1257 {$ENDIF}
1281 // ////////////////////////////////////////////////////////////////////////// //
1283 label
1284 _done;
1285 var
1289 begin
1295 //writeln('spark0: pos=(', x, ',', y, '); delta=(', dx, ',', dy, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1297 // apply gravity
1299 begin
1304 // flying
1306 begin
1307 // has some horizontal velocity
1313 begin
1315 // hit the wall; falling down vertically
1319 end
1321 begin
1322 // has some vertical velocity
1324 begin
1325 // flying up
1329 begin
1330 // oops, hit a ceiling
1335 // environment didn't changed
1336 end
1337 else
1338 begin
1339 // falling down
1343 begin
1344 // hit something except a floor?
1346 // otherwise, go to sleep
1349 // environment didn't changed
1354 _done:
1355 if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then begin die(); end;
1360 begin
1365 //writeln('spark1: pos=(', x, ',', y, '); delta=(', velX:6:3, ',', velY:6:3, '); state=', state, '; ceilingY=', ceilingY, '; floorY=', floorY);
1369 else
1374 // ////////////////////////////////////////////////////////////////////////// //
1376 var
1381 begin
1394 begin
1396 begin
1402 // check for level bounds
1405 // in what environment we are starting in?
1408 begin
1409 // either in a wall, or in a liquid
1410 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1411 //env := TEnvType.ELiquid;
1412 continue;
1413 end
1414 else
1415 begin
1423 begin
1450 var
1457 begin
1478 begin
1480 begin
1486 // check for level bounds
1489 // in what environment we are starting in?
1492 begin
1493 // either in a wall, or in a liquid
1494 //if ((pan.tag and GridTagSolid) <> 0) then continue; // don't spawn in walls
1495 //env := TEnvType.ELiquid;
1496 continue;
1497 end
1498 else
1499 begin
1527 // ////////////////////////////////////////////////////////////////////////// //
1529 var
1531 begin
1542 begin
1548 var
1550 begin
1554 begin
1556 Exit;
1560 begin
1563 end
1564 else
1565 begin
1573 var
1575 begin
1591 // ////////////////////////////////////////////////////////////////////////// //
1593 begin
1594 //g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False);
1595 //SetLength(gCollideMap, gMapInfo.Height+1);
1596 //for a := 0 to High(gCollideMap) do SetLength(gCollideMap[a], gMapInfo.Width+1);
1598 {$IFDEF HEADLESS}
1600 {$ENDIF}
1605 var
1607 begin
1614 begin
1620 // why not?
1626 // ////////////////////////////////////////////////////////////////////////// //
1628 var
1632 begin
1636 begin
1643 begin
1645 begin
1647 begin
1656 // clear awake map
1660 begin
1663 begin
1668 ONCEANIM_SMOKE:
1669 begin
1678 begin
1681 end
1682 else