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, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 {$M+}
20 interface
22 uses
25 type
27 record
33 private
34 const
35 private
42 record
53 private
73 public
86 // sorry, there fields are public to allow setting 'em in g_map; this should be fixed later
87 // for now, PLEASE, don't modify 'em, or all hell will break loose
123 public
124 property visvalid: Boolean read getvisvalid; // panel is "visvalid" when it's width and height are positive
126 published
162 public
170 var
175 implementation
177 uses
181 const
184 { T P a n e l : }
190 var
192 begin
212 // Òèï ïàíåëè:
220 PANEL_OPENDOOR:
221 begin
226 PANEL_CLOSEDOOR:
227 begin
231 PANEL_LIFTUP:
233 PANEL_LIFTDOWN:
234 begin
238 PANEL_LIFTLEFT:
239 begin
243 PANEL_LIFTRIGHT:
244 begin
250 // Íåâèäèìàÿ:
252 begin
255 Exit;
257 // Ïàíåëè, íå èñïîëüçóþùèå òåêñòóðû:
260 PANEL_LIFTDOWN or
261 PANEL_LIFTLEFT or
262 PANEL_LIFTRIGHT or
264 begin
267 Exit;
270 // Åñëè ýòî æèäêîñòü áåç òåêñòóðû - ñïåöòåêñòóðó:
273 begin
278 PANEL_WATER:
280 PANEL_ACID1:
282 PANEL_ACID2:
287 Exit;
294 else
297 else
301 begin
311 end
312 else
318 // Òåêñòóð íåñêîëüêî - íóæíî ñîõðàíÿòü òåêóùóþ:
322 // Åñëè íå ñïåöòåêñòóðà, òî çàäàåì ðàçìåðû:
324 begin
325 e_WriteLog(Format('WTF?! PanelRec.TextureNum is out of limits! (%d : %d)', [PanelRec.TextureNum, High(Textures)]), MSG_FATALERROR);
330 end
332 begin
341 var
343 begin
354 function TPanel.getvisvalid (): Boolean; inline; begin result := (Width > 0) and (Height > 0); end;
371 function TPanel.getIsGBack (): Boolean; inline; begin result := ((tag and GridTagBack) <> 0); end;
372 function TPanel.getIsGStep (): Boolean; inline; begin result := ((tag and GridTagStep) <> 0); end;
373 function TPanel.getIsGWall (): Boolean; inline; begin result := ((tag and (GridTagWall or GridTagDoor)) <> 0); end;
374 function TPanel.getIsGAcid1 (): Boolean; inline; begin result := ((tag and GridTagAcid1) <> 0); end;
375 function TPanel.getIsGAcid2 (): Boolean; inline; begin result := ((tag and GridTagAcid2) <> 0); end;
376 function TPanel.getIsGWater (): Boolean; inline; begin result := ((tag and GridTagWater) <> 0); end;
377 function TPanel.getIsGFore (): Boolean; inline; begin result := ((tag and GridTagFore) <> 0); end;
378 function TPanel.getIsGLift (): Boolean; inline; begin result := ((tag and GridTagLift) <> 0); end;
379 function TPanel.getIsGBlockMon (): Boolean; inline; begin result := ((tag and GridTagBlockMon) <> 0); end;
382 var
386 begin
390 begin
394 Exit;
401 end
402 else
416 begin
420 end else
421 begin
434 else
446 begin
448 //e_WriteLog(Format(' : (%d,%d)', [x+(x-lightX)*300, y+(y-lightY)*300]), MSG_WARNING);
452 begin
453 // does this side facing the light?
455 //e_WriteLog(Format('lightpan: (%d,%d)-(%d,%d)', [x0, y0, x1, y1]), MSG_WARNING);
456 // this edge is facing the light, extrude and draw it
463 begin
465 if Enabled and (FCurTexture >= 0) and (Width > 0) and (Height > 0) and (FAlpha < 255) and g_Collide(X, Y, Width, Height, sX, sY, sWidth, sHeight) then
466 begin
468 begin
480 //e_DrawFill(FTextureIDs[FCurTexture].Tex, X, Y, Width div FTextureWidth, Height div FTextureHeight, FAlpha, True, FBlending);
493 var
495 begin
497 begin
500 begin
501 {
502 e_LogWritefln('panel moved: arridx=%s; guid=%s; proxyid=%s; old:(%s,%s)-(%sx%s); new:(%s,%s)-(%sx%s)',
503 [arrIdx, mGUID, proxyId, px, py, pw, ph, x, y, width, height]);
504 }
514 {
515 var
516 monMoveList: array of TMonster = nil;
517 monMoveListUsed: Integer = 0;
518 }
521 var
527 // return `true` if we should move by dx,dy
528 function tryMPlatMove (px, py, pw, ph: Integer; out dx, dy: Integer; out squash: Boolean): Boolean;
529 var
533 begin
539 // standing on the platform?
541 begin
542 // yes, move with it
544 //e_LogWritefln('entity on the platform; tracing=(%s,%s); endpoint=(%s,%s); mustbe=(%s,%s)', [px, py, tex, tey, px+pdx, py+pdy]);
545 // still in platform?
549 end
550 else
551 begin
552 // not standing on the platform: trace platform to see if it hits the entity
553 // hitedge (for `it`): 0: top; 1: right; 2: bottom; 3: left
554 {
555 if g_Collide(px, py, pw, ph, ox, oy, Width, Height) then
556 begin
557 e_LogWritefln('entity is embedded: plr=(%s,%s)-(%s,%s); mpl=(%s,%s)-(%s,%s)', [px, py, px+pw-1, py+ph-1, ox, oy, ox+Width-1, oy+Height-1]);
558 end;
559 }
561 begin
562 //e_LogWritefln('T: platsweep; u0=%s; u1=%s; hedge=%s; sweepAABB(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)', [u0, u1, hedge, ox, oy, Width, Height, pdx, pdy, px-1, py-1, pw+2, ph+2]);
563 // yes, platform hits the entity, push the entity in the direction of the platform
567 //e_LogWritefln(' platsweep; uleft=%s; pd=(%s,%s)', [u0, pdx, pdy]);
569 begin
570 // has some path to go, trace the entity
572 //e_LogWritefln(' tracebox: te=(%s,%s)', [tex, tey]);
573 end
574 else
575 begin
576 // no movement
580 // free to push along the whole path, or path was corrected
581 // still in platform?
585 end
586 else
587 begin
588 // no collistion, but may be embedded
589 //e_LogWritefln('F: platsweep; u0=%s; u1=%s; sweepAABB(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)', [u0, u1, ox, oy, Width, Height, pdx, pdy, px-1, py-1, pw+2, ph+2]);
596 (*
597 function monCollect (mon: TMonster): Boolean;
598 begin
599 result := false; // don't stop
600 monMoveList[monMoveListUsed] := mon;
601 Inc(monMoveListUsed);
602 end;
604 function doPush (px, py, pw, ph: Integer; out dx, dy: Integer): Boolean;
605 begin
606 result := g_Collide(px, py, pw, ph, nx, ny, Width, Height);
607 if result then
608 begin
609 // need to push
610 if (mMovingSpeed.X < 0) then dx := nx-(px+pw)
611 else if (mMovingSpeed.X > 0) then dx := (nx+Width)-px
612 else dx := 0;
613 if (mMovingSpeed.Y < 0) then dy := ny-(py+ph)
614 else if (mMovingSpeed.Y > 0) then dy := (ny+Height)-py
615 else dy := 0;
616 end
617 else
618 begin
619 dx := 0;
620 dy := 0;
621 end;
622 end;
624 function monMove (mon: TMonster): Boolean;
625 begin
626 result := false; // don't stop
627 //mon.GameX := mon.GameX+mMovingSpeed.X;
628 //mon.GameY := mon.GameY+mMovingSpeed.Y;
629 mon.setPosition(mon.GameX+mMovingSpeed.X, mon.GameY+mMovingSpeed.Y, false); // we can't call `positionChanged()` in grid callback
630 if (monMoveListUsed >= Length(monMoveList)) then SetLength(monMoveList, monMoveListUsed+64);
631 monMoveList[monMoveListUsed] := mon;
632 Inc(monMoveListUsed);
633 end;
635 function monPush (mon: TMonster): Boolean;
636 var
637 px, py, pw, ph, dx, dy: Integer;
638 begin
639 result := false; // don't stop
640 mon.getMapBox(px, py, pw, ph);
641 if doPush(px, py, pw, ph, dx, dy) then
642 begin
643 //mon.GameX := mon.GameX+dx;
644 //mon.GameY := mon.GameY+dy;
645 mon.setPosition(mon.GameX+dx, mon.GameY+dy, false); // we can't call `positionChanged()` in grid callback
646 if (monMoveListUsed >= Length(monMoveList)) then SetLength(monMoveList, monMoveListUsed+64);
647 monMoveList[monMoveListUsed] := mon;
648 Inc(monMoveListUsed);
649 end;
650 end;
652 procedure plrMove (plr: TPlayer);
653 var
654 px, py, pw, ph, dx, dy: Integer;
655 begin
656 // dead players leaves separate body entities, so don't move 'em
657 if (plr = nil) or not plr.alive then exit;
658 plr.getMapBox(px, py, pw, ph);
659 if (py+ph <> oy) then
660 begin
661 // push player
662 if doPush(px, py, pw, ph, dx, dy) then
663 begin
664 plr.GameX := plr.GameX+dx;
665 plr.GameY := plr.GameY+dy;
666 plr.positionChanged();
667 // check if we're squashed
668 if plr.alive then
669 begin
670 plr.getMapBox(px, py, pw, ph);
671 if g_Map_CollidePanel(px, py, pw, ph, (PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR)) then
672 begin
673 plr.Damage(15000, 0, 0, 0, HIT_TRAP);
674 end;
675 end;
676 end;
677 exit;
678 end;
679 if (px+pw <= ox) then exit;
680 if (px >= ox+Width) then exit;
681 plr.GameX := plr.GameX+mMovingSpeed.X;
682 plr.GameY := plr.GameY+mMovingSpeed.Y;
683 plr.positionChanged();
684 end;
686 procedure gibMove (gib: PGib);
687 var
688 px, py, pw, ph, dx, dy: Integer;
689 begin
690 if (gib = nil) or not gib.alive then exit;
691 gib.getMapBox(px, py, pw, ph);
692 {
693 writeln('gib: p=(', px, ',', py, '); obj=(', gib.Obj.X, ',', gib.Obj.Y, ')');
694 px := gib.Obj.X;
695 py := gib.Obj.Y;
696 }
697 if (py+ph <> oy) then
698 begin
699 // push gib
700 if doPush(px, py, pw, ph, dx, dy) then
701 begin
702 gib.Obj.X += dx;
703 gib.Obj.Y += dy;
704 gib.positionChanged();
705 end;
706 exit;
707 end;
708 if (px+pw <= ox) then exit;
709 if (px >= ox+Width) then exit;
710 gib.Obj.X += mMovingSpeed.X;
711 gib.Obj.Y += mMovingSpeed.Y;
712 gib.positionChanged();
713 end;
715 procedure corpseMove (cor: TCorpse);
716 var
717 px, py, pw, ph, dx, dy: Integer;
718 begin
719 if (cor = nil) then exit;
720 cor.getMapBox(px, py, pw, ph);
721 if (py+ph <> oy) then
722 begin
723 // push gib
724 if doPush(px, py, pw, ph, dx, dy) then
725 begin
726 cor.moveBy(dx, dy); // will call `positionChanged()` for us
727 end;
728 exit;
729 end;
730 if (px+pw <= ox) then exit;
731 if (px >= ox+Width) then exit;
732 cor.moveBy(mMovingSpeed.X, mMovingSpeed.Y); // will call `positionChanged()` for us
733 end;
734 *)
736 var
741 {
742 mon: TMonster;
743 sdx, sdy, he: Integer;
744 u0, u1: Single;
745 }
746 begin
753 begin
759 // moving platform?
761 begin
762 (*
763 * collect all monsters and players (aka entities) along the possible platform path
764 * if entity is standing on a platform:
765 * try to move it along the platform path, checking wall collisions
766 * if entity is NOT standing on a platform, but hit with sweeped platform aabb:
767 * try to push entity
768 * if we can't push entity all the way, squash it
769 *)
770 // old rect
775 // new rect
780 // full rect
786 // temporarily turn off this panel, so it won't interfere with collision checks
790 begin
795 begin
796 // set new position
798 // squash player, if necessary
803 // restore panel state
805 // and really move it
810 // reverse moving direction, if necessary
816 (*
817 // collect monsters
818 monMoveListUsed := 0;
819 g_Mons_ForEachAt(cx0, cy0, cx1-cx0+1, cy1-cy0+1, monCollect);
820 // process collected monsters
823 // move monsters on lifts
824 g_Mons_ForEachAt(ox, oy-1, Width, 1, monMove);
825 X := nx;
826 Y := ny;
827 // fix grid
828 positionChanged();
829 // push monsters
830 g_Mons_ForEachAt(nx, ny, Width, Height, monPush);
831 // move and push players
832 for f := 0 to High(gPlayers) do
833 begin
834 gPlayers[f].getMapBox(px, py, pw, ph);
835 //if sweepAABB(ox, oy, Width, Height, mMovingSpeed.X, mMovingSpeed.Y, px, py, pw, ph, @u0, @u1) then
836 //if sweepAABB(ox, oy, Width, Height, mMovingSpeed.X, mMovingSpeed.Y, px, py, pw, ph, @u0, @u1) then
837 sdx := -mMovingSpeed.X;
838 sdy := -mMovingSpeed.Y;
839 if sweepAABB(ox, oy, Width, Height, mMovingSpeed.X, mMovingSpeed.Y, px, py, pw, ph, @u0, @he, @u1) then
840 begin
841 e_LogWritefln('player #%s sweep with platform %s: u0=%s; u1=%s; phe=%s', [f, mGUID, u0, u1, he]);
842 if (u1 > 0.0) then
843 begin
844 e_LogWritefln(' collide: %s', [g_Collide(px, py, pw, ph, nx, ny, Width, Height)]);
845 //gPlayers[f].GameX := gPlayers[f].GameX+trunc(sdx*(1.0-u0));
846 //gPlayers[f].GameY := gPlayers[f].GameY+trunc(mMovingSpeed.Y*(1.0-u0));
847 //gPlayers[f].positionChanged();
848 end;
849 end;
850 //plrMove(gPlayers[f]);
851 end;
852 // move and push gibs
853 for f := 0 to High(gGibs) do gibMove(@gGibs[f]);
854 // move and push corpses
855 for f := 0 to High(gCorpses) do corpseMove(gCorpses[f]);
856 // notify moved monsters about their movement
857 for f := 0 to monMoveListUsed-1 do
858 begin
859 monMoveList[f].positionChanged();
860 end;
861 for f := 0 to monMoveListUsed-1 do
862 begin
863 mon := monMoveList[f];
864 // check if it is squashed
865 if mon.alive then
866 begin
867 mon.getMapBox(px, py, pw, ph);
868 if g_Map_CollidePanel(px, py, pw, ph, (PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR)) then
869 begin
870 mon.Damage(15000, 0, 0, 0, HIT_TRAP);
871 end;
872 end;
873 end;
874 *)
881 begin
886 begin
891 begin
900 begin
903 // Íåò òåêñòóð:
906 else
907 // Òîëüêî îäíà òåêñòóðà:
909 begin
912 else
914 end
915 else
916 // Áîëüøå îäíîé òåêñòóðû:
917 begin
918 // Ñëåäóþùàÿ:
920 // Ñëåäóþùåé íåò - âîçâðàò ê íà÷àëó:
925 // Ïåðåêëþ÷èëèñü íà âèäèìóþ àíèì. òåêñòóðó:
927 begin
929 begin
931 Exit;
936 else
947 begin
948 // Íåò òåêñòóð:
951 else
952 // Òîëüêî îäíà òåêñòóðà:
954 begin
957 end
958 else
959 // Áîëüøå îäíîé òåêñòóðû:
960 begin
965 // Ïåðåêëþ÷èëèñü íà âèäèìóþ àíèì. òåêñòóðó:
967 begin
969 begin
971 Exit;
976 else
987 begin
991 begin
994 else
1000 begin
1010 var
1013 begin
1015 //if not SaveIt then exit;
1017 // Ñèãíàòóðà ïàíåëè:
1020 // Îòêðûòà/çàêðûòà, åñëè äâåðü:
1022 // Íàïðàâëåíèå ëèôòà, åñëè ëèôò:
1024 // Íîìåð òåêóùåé òåêñòóðû:
1026 // Êîîðäû
1029 // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà:
1031 begin
1035 end
1036 else
1039 // Åñëè äà - ñîõðàíÿåì àíèìàöèþ:
1042 // moving platform state
1053 var
1056 //ox, oy: Integer;
1057 begin
1059 //if not SaveIt then exit;
1061 // Ñèãíàòóðà ïàíåëè:
1064 begin
1067 // Îòêðûòà/çàêðûòà, åñëè äâåðü:
1069 // Íàïðàâëåíèå ëèôòà, åñëè ëèôò:
1071 // Íîìåð òåêóùåé òåêñòóðû:
1073 // Êîîðäû
1074 //ox := FX;
1075 //oy := FY;
1078 //e_LogWritefln('panel %s(%s): old=(%s,%s); new=(%s,%s); delta=(%s,%s)', [arrIdx, proxyId, ox, oy, FX, FY, FX-ox, FY-oy]);
1079 // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà:
1081 // Åñëè äà - çàãðóæàåì àíèìàöèþ:
1083 begin
1090 // moving platform state
1100 //mapGrid.proxyEnabled[proxyId] := FEnabled; // done in g_map.pas