1 (* Copyright (C) DooM 2D:Forever Developers
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.
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.
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/>.
16 {$INCLUDE ../shared/a_modes.inc}
23 MAPDEF
, BinEditor
, g_textures
, xdynrec
;
26 TAddTextureArray
= Array of
32 TPanel
= Class (TObject
)
36 mGUID
: Integer; // will be assigned in "g_map.pas"
44 False: (Tex
: Cardinal);
45 True: (AnTex
: TAnimation
);
48 mMovingSpeed
: TDFPoint
;
49 mMovingStart
: TDFPoint
;
51 mMovingActive
: Boolean;
54 mOldMovingActive
: Boolean;
60 mEndSizeTrig
: Integer;
62 mNeedSend
: Boolean; // for network
65 function getx1 (): Integer; inline;
66 function gety1 (): Integer; inline;
67 function getvisvalid (): Boolean; inline;
69 function getMovingSpeedX (): Integer; inline;
70 procedure setMovingSpeedX (v
: Integer); inline;
71 function getMovingSpeedY (): Integer; inline;
72 procedure setMovingSpeedY (v
: Integer); inline;
74 function getMovingStartX (): Integer; inline;
75 procedure setMovingStartX (v
: Integer); inline;
76 function getMovingStartY (): Integer; inline;
77 procedure setMovingStartY (v
: Integer); inline;
79 function getMovingEndX (): Integer; inline;
80 procedure setMovingEndX (v
: Integer); inline;
81 function getMovingEndY (): Integer; inline;
82 procedure setMovingEndY (v
: Integer); inline;
84 function getSizeSpeedX (): Integer; inline;
85 procedure setSizeSpeedX (v
: Integer); inline;
86 function getSizeSpeedY (): Integer; inline;
87 procedure setSizeSpeedY (v
: Integer); inline;
89 function getSizeEndX (): Integer; inline;
90 procedure setSizeEndX (v
: Integer); inline;
91 function getSizeEndY (): Integer; inline;
92 procedure setSizeEndY (v
: Integer); inline;
95 FCurTexture
: Integer; // Íîìåð òåêóùåé òåêñòóðû
99 FWidth
, FHeight
: Word;
101 FSaveIt
: Boolean; // Ñîõðàíÿòü ïðè SaveState?
107 // sorry, there fields are public to allow setting 'em in g_map; this should be fixed later
108 // for now, PLEASE, don't modify 'em, or all hell will break loose
109 arrIdx
: Integer; // index in one of internal arrays; sorry
110 tag
: Integer; // used in coldets and such; sorry; see g_map.GridTagXXX
111 proxyId
: Integer; // proxy id in map grid (DO NOT USE!)
112 mapId
: AnsiString; // taken directly from map file; dunno why it is here
114 constructor Create(PanelRec
: TDynRecord
;
115 AddTextures
: TAddTextureArray
;
117 var Textures
: TLevelTextureArray
; aguid
: Integer);
118 destructor Destroy(); override;
121 procedure DrawShadowVolume(lightX
: Integer; lightY
: Integer; radius
: Integer);
123 procedure SetFrame(Frame
: Integer; Count
: Byte);
124 procedure NextTexture(AnimLoop
: Byte = 0);
125 procedure SetTexture(ID
: Integer; AnimLoop
: Byte = 0);
126 function GetTextureID(): Cardinal;
127 function GetTextureCount(): Integer;
129 procedure SaveState(var Mem
: TBinMemoryWriter
);
130 procedure LoadState(var Mem
: TBinMemoryReader
);
132 procedure positionChanged (); inline;
134 function getIsGBack (): Boolean; inline; // gRenderBackgrounds
135 function getIsGStep (): Boolean; inline; // gSteps
136 function getIsGWall (): Boolean; inline; // gWalls
137 function getIsGAcid1 (): Boolean; inline; // gAcid1
138 function getIsGAcid2 (): Boolean; inline; // gAcid2
139 function getIsGWater (): Boolean; inline; // gWater
140 function getIsGFore (): Boolean; inline; // gRenderForegrounds
141 function getIsGLift (): Boolean; inline; // gLifts
142 function getIsGBlockMon (): Boolean; inline; // gBlockMon
145 function gncNeedSend (): Boolean; inline;
146 procedure setDirty (); inline; // why `dirty`? 'cause i may introduce property `needSend` later
149 property visvalid
: Boolean read getvisvalid
; // panel is "visvalid" when it's width and height are positive
152 property guid
: Integer read mGUID
; // will be assigned in "g_map.pas"
153 property x0
: Integer read FX
;
154 property y0
: Integer read FY
;
155 property x1
: Integer read getx1
; // inclusive!
156 property y1
: Integer read gety1
; // inclusive!
157 property x
: Integer read FX write FX
;
158 property y
: Integer read FY write FY
;
159 property width
: Word read FWidth write FWidth
;
160 property height
: Word read FHeight write FHeight
;
161 property panelType
: Word read FPanelType write FPanelType
;
162 property saveIt
: Boolean read FSaveIt write FSaveIt
; // Ñîõðàíÿòü ïðè SaveState?
163 property enabled
: Boolean read FEnabled write FEnabled
; // Ñîõðàíÿòü ïðè SaveState?
164 property door
: Boolean read FDoor write FDoor
; // Ñîõðàíÿòü ïðè SaveState?
165 property moved
: Boolean read FMoved write FMoved
; // Ñîõðàíÿòü ïðè SaveState?
166 property liftType
: Byte read FLiftType write FLiftType
; // Ñîõðàíÿòü ïðè SaveState?
167 property lastAnimLoop
: Byte read FLastAnimLoop write FLastAnimLoop
; // Ñîõðàíÿòü ïðè SaveState?
169 property movingSpeedX
: Integer read getMovingSpeedX write setMovingSpeedX
;
170 property movingSpeedY
: Integer read getMovingSpeedY write setMovingSpeedY
;
171 property movingStartX
: Integer read getMovingStartX write setMovingStartX
;
172 property movingStartY
: Integer read getMovingStartY write setMovingStartY
;
173 property movingEndX
: Integer read getMovingEndX write setMovingEndX
;
174 property movingEndY
: Integer read getMovingEndY write setMovingEndY
;
175 property movingActive
: Boolean read mMovingActive write mMovingActive
;
176 property moveOnce
: Boolean read mMoveOnce write mMoveOnce
;
178 property sizeSpeedX
: Integer read getSizeSpeedX write setSizeSpeedX
;
179 property sizeSpeedY
: Integer read getSizeSpeedY write setSizeSpeedY
;
180 property sizeEndX
: Integer read getSizeEndX write setSizeEndX
;
181 property sizeEndY
: Integer read getSizeEndY write setSizeEndY
;
183 property isGBack
: Boolean read getIsGBack
;
184 property isGStep
: Boolean read getIsGStep
;
185 property isGWall
: Boolean read getIsGWall
;
186 property isGAcid1
: Boolean read getIsGAcid1
;
187 property isGAcid2
: Boolean read getIsGAcid2
;
188 property isGWater
: Boolean read getIsGWater
;
189 property isGFore
: Boolean read getIsGFore
;
190 property isGLift
: Boolean read getIsGLift
;
191 property isGBlockMon
: Boolean read getIsGBlockMon
;
194 property movingSpeed
: TDFPoint read mMovingSpeed write mMovingSpeed
;
195 property movingStart
: TDFPoint read mMovingStart write mMovingStart
;
196 property movingEnd
: TDFPoint read mMovingEnd write mMovingEnd
;
198 property sizeSpeed
: TDFSize read mSizeSpeed write mSizeSpeed
;
199 property sizeEnd
: TDFSize read mSizeEnd write mSizeEnd
;
201 property endPosTrigId
: Integer read mEndPosTrig write mEndPosTrig
;
202 property endSizeTrigId
: Integer read mEndSizeTrig write mEndSizeTrig
;
205 TPanelArray
= Array of TPanel
;
208 g_dbgpan_mplat_active
: Boolean = {$IF DEFINED(D2F_DEBUG)}true{$ELSE}true{$ENDIF};
209 g_dbgpan_mplat_step
: Boolean = false; // one step, and stop
215 SysUtils
, g_basic
, g_map
, g_game
, g_gfx
, e_graphics
, g_weapons
, g_triggers
,
216 g_console
, g_language
, g_monsters
, g_player
, g_grid
, e_log
, GL
, utils
;
219 PANEL_SIGNATURE
= $4C4E4150; // 'PANL'
223 constructor TPanel
.Create(PanelRec
: TDynRecord
;
224 AddTextures
: TAddTextureArray
;
226 var Textures
: TLevelTextureArray
; aguid
: Integer);
232 Width
:= PanelRec
.Width
;
233 Height
:= PanelRec
.Height
;
241 mapId
:= PanelRec
.id
;
244 mMovingSpeed
:= PanelRec
.moveSpeed
;
245 mMovingStart
:= PanelRec
.moveStart
;
246 mMovingEnd
:= PanelRec
.moveEnd
;
247 mMovingActive
:= PanelRec
['move_active'].varvalue
;
248 mOldMovingActive
:= mMovingActive
;
249 mMoveOnce
:= PanelRec
.moveOnce
;
251 mSizeSpeed
:= PanelRec
.sizeSpeed
;
252 mSizeEnd
:= PanelRec
.sizeEnd
;
254 mEndPosTrig
:= PanelRec
.endPosTrig
;
255 mEndSizeTrig
:= PanelRec
.endSizeTrig
;
260 PanelType
:= PanelRec
.PanelType
;
298 if ByteBool(PanelRec
.Flags
and PANEL_FLAG_HIDE
) then
300 SetLength(FTextureIDs
, 0);
304 // Ïàíåëè, íå èñïîëüçóþùèå òåêñòóðû:
305 if ByteBool(PanelType
and
310 PANEL_BLOCKMON
)) then
312 SetLength(FTextureIDs
, 0);
317 // Åñëè ýòî æèäêîñòü áåç òåêñòóðû - ñïåöòåêñòóðó:
318 if WordBool(PanelType
and (PANEL_WATER
or PANEL_ACID1
or PANEL_ACID2
)) and
319 (not ByteBool(PanelRec
.Flags
and PANEL_FLAG_WATERTEXTURES
)) then
321 SetLength(FTextureIDs
, 1);
322 FTextureIDs
[0].Anim
:= False;
324 case PanelRec
.PanelType
of
326 FTextureIDs
[0].Tex
:= LongWord(TEXTURE_SPECIAL_WATER
);
328 FTextureIDs
[0].Tex
:= LongWord(TEXTURE_SPECIAL_ACID1
);
330 FTextureIDs
[0].Tex
:= LongWord(TEXTURE_SPECIAL_ACID2
);
337 SetLength(FTextureIDs
, Length(AddTextures
));
342 if CurTex
>= Length(FTextureIDs
) then
343 FCurTexture
:= Length(FTextureIDs
) - 1
345 FCurTexture
:= CurTex
;
347 for i
:= 0 to Length(FTextureIDs
)-1 do
349 FTextureIDs
[i
].Anim
:= AddTextures
[i
].Anim
;
350 if FTextureIDs
[i
].Anim
then
351 begin // Àíèìèðîâàííàÿ òåêñòóðà
352 FTextureIDs
[i
].AnTex
:=
353 TAnimation
.Create(Textures
[AddTextures
[i
].Texture
].FramesID
,
354 True, Textures
[AddTextures
[i
].Texture
].Speed
);
355 FTextureIDs
[i
].AnTex
.Blending
:= ByteBool(PanelRec
.Flags
and PANEL_FLAG_BLENDING
);
356 FTextureIDs
[i
].AnTex
.Alpha
:= PanelRec
.Alpha
;
360 begin // Îáû÷íàÿ òåêñòóðà
361 FTextureIDs
[i
].Tex
:= Textures
[AddTextures
[i
].Texture
].TextureID
;
365 // Òåêñòóð íåñêîëüêî - íóæíî ñîõðàíÿòü òåêóùóþ:
366 if Length(FTextureIDs
) > 1 then
369 // Åñëè íå ñïåöòåêñòóðà, òî çàäàåì ðàçìåðû:
370 if PanelRec
.TextureNum
> High(Textures
) then
372 e_WriteLog(Format('WTF?! PanelRec.TextureNum is out of limits! (%d : %d)', [PanelRec
.TextureNum
, High(Textures
)]), MSG_FATALERROR
);
376 FBlending
:= ByteBool(0);
378 else if not g_Map_IsSpecialTexture(Textures
[PanelRec
.TextureNum
].TextureName
) then
380 FTextureWidth
:= Textures
[PanelRec
.TextureNum
].Width
;
381 FTextureHeight
:= Textures
[PanelRec
.TextureNum
].Height
;
382 FAlpha
:= PanelRec
.Alpha
;
383 FBlending
:= ByteBool(PanelRec
.Flags
and PANEL_FLAG_BLENDING
);
387 destructor TPanel
.Destroy();
391 for i
:= 0 to High(FTextureIDs
) do
392 if FTextureIDs
[i
].Anim
then
393 FTextureIDs
[i
].AnTex
.Free();
394 SetLength(FTextureIDs
, 0);
399 function TPanel
.getx1 (): Integer; inline; begin result
:= X
+Width
-1; end;
400 function TPanel
.gety1 (): Integer; inline; begin result
:= Y
+Height
-1; end;
401 function TPanel
.getvisvalid (): Boolean; inline; begin result
:= (Width
> 0) and (Height
> 0); end;
403 function TPanel
.getMovingSpeedX (): Integer; inline; begin result
:= mMovingSpeed
.X
; end;
404 procedure TPanel
.setMovingSpeedX (v
: Integer); inline; begin mMovingSpeed
.X
:= v
; end;
405 function TPanel
.getMovingSpeedY (): Integer; inline; begin result
:= mMovingSpeed
.Y
; end;
406 procedure TPanel
.setMovingSpeedY (v
: Integer); inline; begin mMovingSpeed
.Y
:= v
; end;
408 function TPanel
.getMovingStartX (): Integer; inline; begin result
:= mMovingStart
.X
; end;
409 procedure TPanel
.setMovingStartX (v
: Integer); inline; begin mMovingStart
.X
:= v
; end;
410 function TPanel
.getMovingStartY (): Integer; inline; begin result
:= mMovingStart
.Y
; end;
411 procedure TPanel
.setMovingStartY (v
: Integer); inline; begin mMovingStart
.Y
:= v
; end;
413 function TPanel
.getMovingEndX (): Integer; inline; begin result
:= mMovingEnd
.X
; end;
414 procedure TPanel
.setMovingEndX (v
: Integer); inline; begin mMovingEnd
.X
:= v
; end;
415 function TPanel
.getMovingEndY (): Integer; inline; begin result
:= mMovingEnd
.Y
; end;
416 procedure TPanel
.setMovingEndY (v
: Integer); inline; begin mMovingEnd
.Y
:= v
; end;
418 function TPanel
.getSizeSpeedX (): Integer; inline; begin result
:= mSizeSpeed
.w
; end;
419 procedure TPanel
.setSizeSpeedX (v
: Integer); inline; begin mSizeSpeed
.w
:= v
; end;
420 function TPanel
.getSizeSpeedY (): Integer; inline; begin result
:= mSizeSpeed
.h
; end;
421 procedure TPanel
.setSizeSpeedY (v
: Integer); inline; begin mSizeSpeed
.h
:= v
; end;
423 function TPanel
.getSizeEndX (): Integer; inline; begin result
:= mSizeEnd
.w
; end;
424 procedure TPanel
.setSizeEndX (v
: Integer); inline; begin mSizeEnd
.w
:= v
; end;
425 function TPanel
.getSizeEndY (): Integer; inline; begin result
:= mSizeEnd
.h
; end;
426 procedure TPanel
.setSizeEndY (v
: Integer); inline; begin mSizeEnd
.h
:= v
; end;
428 function TPanel
.getIsGBack (): Boolean; inline; begin result
:= ((tag
and GridTagBack
) <> 0); end;
429 function TPanel
.getIsGStep (): Boolean; inline; begin result
:= ((tag
and GridTagStep
) <> 0); end;
430 function TPanel
.getIsGWall (): Boolean; inline; begin result
:= ((tag
and (GridTagWall
or GridTagDoor
)) <> 0); end;
431 function TPanel
.getIsGAcid1 (): Boolean; inline; begin result
:= ((tag
and GridTagAcid1
) <> 0); end;
432 function TPanel
.getIsGAcid2 (): Boolean; inline; begin result
:= ((tag
and GridTagAcid2
) <> 0); end;
433 function TPanel
.getIsGWater (): Boolean; inline; begin result
:= ((tag
and GridTagWater
) <> 0); end;
434 function TPanel
.getIsGFore (): Boolean; inline; begin result
:= ((tag
and GridTagFore
) <> 0); end;
435 function TPanel
.getIsGLift (): Boolean; inline; begin result
:= ((tag
and GridTagLift
) <> 0); end;
436 function TPanel
.getIsGBlockMon (): Boolean; inline; begin result
:= ((tag
and GridTagBlockMon
) <> 0); end;
438 function TPanel
.gncNeedSend (): Boolean; inline; begin result
:= mNeedSend
; mNeedSend
:= false; end;
439 procedure TPanel
.setDirty (); inline; begin mNeedSend
:= true; end;
442 procedure TPanel
.Draw ();
448 if {Enabled and} (FCurTexture
>= 0) and
449 (Width
> 0) and (Height
> 0) and (FAlpha
< 255) and
450 ((g_dbg_scale
<> 1.0) or g_Collide(X
, Y
, Width
, Height
, sX
, sY
, sWidth
, sHeight
)) then
452 if FTextureIDs
[FCurTexture
].Anim
then
453 begin // Àíèìèðîâàííàÿ òåêñòóðà
454 if FTextureIDs
[FCurTexture
].AnTex
= nil then
457 for xx
:= 0 to (Width
div FTextureWidth
)-1 do
458 for yy
:= 0 to (Height
div FTextureHeight
)-1 do
459 FTextureIDs
[FCurTexture
].AnTex
.Draw(
460 X
+ xx
*FTextureWidth
,
461 Y
+ yy
*FTextureHeight
, M_NONE
);
464 begin // Îáû÷íàÿ òåêñòóðà
465 case FTextureIDs
[FCurTexture
].Tex
of
466 LongWord(TEXTURE_SPECIAL_WATER
):
467 e_DrawFillQuad(X
, Y
, X
+Width
-1, Y
+Height
-1,
468 0, 0, 255, 0, B_FILTER
);
469 LongWord(TEXTURE_SPECIAL_ACID1
):
470 e_DrawFillQuad(X
, Y
, X
+Width
-1, Y
+Height
-1,
471 0, 128, 0, 0, B_FILTER
);
472 LongWord(TEXTURE_SPECIAL_ACID2
):
473 e_DrawFillQuad(X
, Y
, X
+Width
-1, Y
+Height
-1,
474 128, 0, 0, 0, B_FILTER
);
475 LongWord(TEXTURE_NONE
):
476 if g_Texture_Get('NOTEXTURE', NoTextureID
) then
478 e_GetTextureSize(NoTextureID
, @NW
, @NH
);
479 e_DrawFill(NoTextureID
, X
, Y
, Width
div NW
, Height
div NH
,
483 xx
:= X
+ (Width
div 2);
484 yy
:= Y
+ (Height
div 2);
485 e_DrawFillQuad(X
, Y
, xx
, yy
,
487 e_DrawFillQuad(xx
, Y
, X
+Width
-1, yy
,
489 e_DrawFillQuad(X
, yy
, xx
, Y
+Height
-1,
491 e_DrawFillQuad(xx
, yy
, X
+Width
-1, Y
+Height
-1,
496 if not mMovingActive
then
497 e_DrawFill(FTextureIDs
[FCurTexture
].Tex
, X
, Y
, Width
div FTextureWidth
, Height
div FTextureHeight
, FAlpha
, True, FBlending
)
499 e_DrawFillX(FTextureIDs
[FCurTexture
].Tex
, X
, Y
, Width
, Height
, FAlpha
, True, FBlending
, g_dbg_scale
);
505 procedure TPanel
.DrawShadowVolume(lightX
: Integer; lightY
: Integer; radius
: Integer);
506 procedure extrude (x
: Integer; y
: Integer);
508 glVertex2i(x
+(x
-lightX
)*500, y
+(y
-lightY
)*500);
509 //e_WriteLog(Format(' : (%d,%d)', [x+(x-lightX)*300, y+(y-lightY)*300]), MSG_WARNING);
512 procedure drawLine (x0
: Integer; y0
: Integer; x1
: Integer; y1
: Integer);
514 // does this side facing the light?
515 if ((x1
-x0
)*(lightY
-y0
)-(lightX
-x0
)*(y1
-y0
) >= 0) then exit
;
516 //e_WriteLog(Format('lightpan: (%d,%d)-(%d,%d)', [x0, y0, x1, y1]), MSG_WARNING);
517 // this edge is facing the light, extrude and draw it
525 if radius
< 4 then exit
;
526 if Enabled
and (FCurTexture
>= 0) and (Width
> 0) and (Height
> 0) and (FAlpha
< 255) and
527 ((g_dbg_scale
<> 1.0) or g_Collide(X
, Y
, Width
, Height
, sX
, sY
, sWidth
, sHeight
)) then
529 if not FTextureIDs
[FCurTexture
].Anim
then
531 case FTextureIDs
[FCurTexture
].Tex
of
532 LongWord(TEXTURE_SPECIAL_WATER
): exit
;
533 LongWord(TEXTURE_SPECIAL_ACID1
): exit
;
534 LongWord(TEXTURE_SPECIAL_ACID2
): exit
;
535 LongWord(TEXTURE_NONE
): exit
;
538 if (X
+Width
< lightX
-radius
) then exit
;
539 if (Y
+Height
< lightY
-radius
) then exit
;
540 if (X
> lightX
+radius
) then exit
;
541 if (Y
> lightY
+radius
) then exit
;
542 //e_DrawFill(FTextureIDs[FCurTexture].Tex, X, Y, Width div FTextureWidth, Height div FTextureHeight, FAlpha, True, FBlending);
545 drawLine(x
, y
, x
+width
, y
); // top
546 drawLine(x
+width
, y
, x
+width
, y
+height
); // right
547 drawLine(x
+width
, y
+height
, x
, y
+height
); // bottom
548 drawLine(x
, y
+height
, x
, y
); // left
554 procedure TPanel
.positionChanged (); inline;
556 px
, py
, pw
, ph
: Integer;
558 if (proxyId
>= 0) then
560 mapGrid
.getBodyDims(proxyId
, px
, py
, pw
, ph
);
561 if (px
<> x
) or (py
<> y
) or (pw
<> Width
) or (ph
<> Height
) then
564 e_LogWritefln('panel moved: arridx=%s; guid=%s; proxyid=%s; old:(%s,%s)-(%sx%s); new:(%s,%s)-(%sx%s)',
565 [arrIdx, mGUID, proxyId, px, py, pw, ph, x, y, width, height]);
567 g_Mark(px
, py
, pw
, ph
, MARK_WALL
, false);
568 if (Width
< 1) or (Height
< 1) then
570 mapGrid
.proxyEnabled
[proxyId
] := false;
574 mapGrid
.proxyEnabled
[proxyId
] := Enabled
;
575 if (pw
<> Width
) or (ph
<> Height
) then
577 //writeln('panel resize!');
578 mapGrid
.moveResizeBody(proxyId
, X
, Y
, Width
, Height
)
582 mapGrid
.moveBody(proxyId
, X
, Y
);
584 g_Mark(X
, Y
, Width
, Height
, MARK_WALL
);
592 monCheckList
: array of TMonster
= nil;
593 monCheckListUsed
: Integer = 0;
595 procedure TPanel
.Update();
598 nx
, ny
, nw
, nh
: Integer;
599 ex
, ey
, nex
, ney
: Integer;
602 // return `true` if we should move by dx,dy
603 function tryMPlatMove (px
, py
, pw
, ph
: Integer; out dx
, dy
: Integer; out squash
: Boolean; ontop
: PBoolean=nil): Boolean;
614 pdx
:= mMovingSpeed
.X
;
615 pdy
:= mMovingSpeed
.Y
;
616 // standing on the platform?
619 if (ontop
<> nil) then ontop
^ := true;
620 // yes, move with it; but skip steps (no need to process size change here, 'cause platform top cannot be changed with it)
621 mapGrid
.traceBox(tex
, tey
, px
, py
, pw
, ph
, pdx
, pdy
, nil, (GridTagWall
or GridTagDoor
));
625 if (ontop
<> nil) then ontop
^ := false;
626 // not standing on the platform: trace platform to see if it hits the entity
627 // first, process size change (as we cannot sweeptest both move and size change)
628 // but we don't have to check for pushing if the panel is shrinking
631 if (szdx
> 0) or (szdy
> 0) then
633 // ignore shrinking dimension
634 if (szdx
< 0) then szdx
:= 0;
635 if (szdy
< 0) then szdy
:= 0;
636 // move platform by szd* back, and check for szd* movement
637 if sweepAABB(ox
-szdx
, oy
-szdy
, nw
, nh
, szdx
, szdy
, px
, py
, pw
, ph
, @u0
) then
639 // yes, platform hits the entity, push the entity in the resizing direction
640 u0
:= 1.0-u0
; // how much path left?
641 szdx
:= trunc(szdx
*u0
);
642 szdy
:= trunc(szdy
*u0
);
643 if (szdx
<> 0) or (szdy
<> 0) then
645 // has some path to go, trace the entity
646 trtag
:= (GridTagWall
or GridTagDoor
);
647 // if we're moving down, consider steps too
648 if (szdy
> 0) then trtag
:= trtag
or GridTagStep
;
649 mapGrid
.traceBox(tex
, tey
, px
, py
, pw
, ph
, szdx
, szdy
, nil, trtag
);
653 // second, process platform movement, using te* as entity starting point
654 if sweepAABB(ox
, oy
, nw
, nh
, pdx
, pdy
, tex
, tey
, pw
, ph
, @u0
) then
656 //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, mpw, mph, pdx, pdy, px-1, py-1, pw+2, ph+2]);
657 // yes, platform hits the entity, push the entity in the direction of the platform
658 u0
:= 1.0-u0
; // how much path left?
659 pdx
:= trunc(pdx
*u0
);
660 pdy
:= trunc(pdy
*u0
);
661 //e_LogWritefln(' platsweep; uleft=%s; pd=(%s,%s)', [u0, pdx, pdy]);
662 if (pdx
<> 0) or (pdy
<> 0) then
664 // has some path to go, trace the entity
665 trtag
:= (GridTagWall
or GridTagDoor
);
666 // if we're moving down, consider steps too
667 if (pdy
> 0) then trtag
:= trtag
or GridTagStep
;
668 mapGrid
.traceBox(tex
, tey
, px
, py
, pw
, ph
, pdx
, pdy
, nil, trtag
);
672 // done with entity movement, new coords are in te*
675 result
:= (dx
<> 0) or (dy
<> 0);
676 if ((tag
and (GridTagWall
or GridTagDoor
)) <> 0) then
678 // check for squashing; as entity cannot be pushed into a wall, check only collision with the platform itself
679 squash
:= g_Collide(tex
, tey
, pw
, ph
, nx
, ny
, nw
, nh
); // squash, if still in platform
683 function monCollect (mon
: TMonster
): Boolean;
685 result
:= false; // don't stop
686 if (monCheckListUsed
>= Length(monCheckList
)) then SetLength(monCheckList
, monCheckListUsed
+128);
687 monCheckList
[monCheckListUsed
] := mon
;
688 Inc(monCheckListUsed
);
692 cx0
, cy0
, cx1
, cy1
, cw
, ch
: Integer;
694 px
, py
, pw
, ph
, pdx
, pdy
: Integer;
702 actMoveTrig
: Boolean;
703 actSizeTrig
: Boolean;
705 if (not Enabled
) or (Width
< 1) or (Height
< 1) then exit
;
707 if (FCurTexture
>= 0) and
708 (FTextureIDs
[FCurTexture
].Anim
) and
709 (FTextureIDs
[FCurTexture
].AnTex
<> nil) and
712 FTextureIDs
[FCurTexture
].AnTex
.Update();
713 FCurFrame
:= FTextureIDs
[FCurTexture
].AnTex
.CurrentFrame
;
714 FCurFrameCount
:= FTextureIDs
[FCurTexture
].AnTex
.CurrentCounter
;
717 if not g_dbgpan_mplat_active
then exit
;
719 if (mOldMovingActive
<> mMovingActive
) then mNeedSend
:= true;
720 mOldMovingActive
:= mMovingActive
;
722 if not mMovingActive
then exit
;
723 if mMovingSpeed
.isZero
and mSizeSpeed
.isZero
then exit
;
725 //TODO: write wall size change processing
730 * collect all monsters and players (aka entities) along the possible platform path
731 * if entity is standing on a platform:
732 * try to move it along the platform path, checking wall collisions
733 * if entity is NOT standing on a platform, but hit with sweeped platform aabb:
735 * if we can't push entity all the way, squash it
742 nw
:= mpw
+mSizeSpeed
.w
;
743 nh
:= mph
+mSizeSpeed
.h
;
744 nx
:= ox
+mMovingSpeed
.X
;
745 ny
:= oy
+mMovingSpeed
.Y
;
747 // force network updates only if some sudden change happened
748 // set the flag here, so we can sync affected monsters
749 if not mSizeSpeed
.isZero
and (nw
= mSizeEnd
.w
) and (nh
= mSizeEnd
.h
) then
753 else if ((mMovingSpeed
.X
< 0) and (nx
<= mMovingStart
.X
)) or ((mMovingSpeed
.X
> 0) and (nx
>= mMovingEnd
.X
)) then
757 else if ((mMovingSpeed
.Y
< 0) and (ny
<= mMovingStart
.Y
)) or ((mMovingSpeed
.Y
> 0) and (ny
>= mMovingEnd
.Y
)) then
762 // if pannel disappeared, we don't have to do anything
763 if (nw
> 0) and (nh
> 0) then
774 cx1
:= nmax(ex
, nex
);
775 cy1
:= nmax(ey
, ney
);
784 // process "obstacle" panels
785 if ((tag
and GridTagObstacle
) <> 0) then
787 // temporarily turn off this panel, so it won't interfere with collision checks
788 mapGrid
.proxyEnabled
[proxyId
] := false;
791 for f
:= 0 to High(gPlayers
) do
794 if (plr
= nil) or (not plr
.alive
) then continue
;
795 plr
.getMapBox(px
, py
, pw
, ph
);
796 if not g_Collide(px
, py
, pw
, ph
, cx0
, cy0
, cw
, ch
) then continue
;
797 if tryMPlatMove(px
, py
, pw
, ph
, pdx
, pdy
, squash
) then
800 plr
.moveBy(pdx
, pdy
); // this will call `positionChanged()` for us
802 // squash player, if necessary
803 if not g_Game_IsClient
and squash
then plr
.Damage(15000, 0, 0, 0, HIT_TRAP
);
807 for f
:= 0 to High(gGibs
) do
810 if not gib
.alive
then continue
;
811 gib
.getMapBox(px
, py
, pw
, ph
);
812 if not g_Collide(px
, py
, pw
, ph
, cx0
, cy0
, cw
, ch
) then continue
;
813 if tryMPlatMove(px
, py
, pw
, ph
, pdx
, pdy
, squash
, @ontop
) then
816 gib
.moveBy(pdx
, pdy
); // this will call `positionChanged()` for us
820 // move and push corpses
821 for f
:= 0 to High(gCorpses
) do
824 if (cor
= nil) then continue
;
825 cor
.getMapBox(px
, py
, pw
, ph
);
826 if not g_Collide(px
, py
, pw
, ph
, cx0
, cy0
, cw
, ch
) then continue
;
827 if tryMPlatMove(px
, py
, pw
, ph
, pdx
, pdy
, squash
, @ontop
) then
830 cor
.moveBy(pdx
, pdy
); // this will call `positionChanged()` for us
835 monCheckListUsed
:= 0;
836 g_Mons_ForEachAt(cx0
, cy0
, cw
, ch
, monCollect
);
838 // process collected monsters
839 if (monCheckListUsed
> 0) then
841 mpfrid
:= g_Mons_getNewMPlatFrameId();
842 for f
:= 0 to monCheckListUsed
do
844 mon
:= monCheckList
[f
];
845 if (mon
= nil) or (not mon
.alive
) or (mon
.mplatCheckFrameId
= mpfrid
) then continue
;
846 mon
.mplatCheckFrameId
:= mpfrid
;
847 mon
.getMapBox(px
, py
, pw
, ph
);
848 //if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue;
849 if tryMPlatMove(px
, py
, pw
, ph
, pdx
, pdy
, squash
) then
852 mon
.moveBy(pdx
, pdy
); // this will call `positionChanged()` for us
853 //???FIXME: do we really need to send monsters over the net?
854 // i don't think so, as dead reckoning should take care of 'em
855 // ok, send new monster position only if platform is going to change it's direction
856 if mNeedSend
then mon
.setDirty();
858 // squash monster, if necessary
859 if not g_Game_IsClient
and squash
then mon
.Damage(15000, 0, 0, 0, HIT_TRAP
);
863 // restore panel state
864 mapGrid
.proxyEnabled
[proxyId
] := true;
875 actMoveTrig
:= false;
876 actSizeTrig
:= false;
878 // `mNeedSend` was set above
881 if not mSizeSpeed
.isZero
and (nw
= mSizeEnd
.w
) and (nh
= mSizeEnd
.h
) then
886 if (nw
< 1) or (nh
< 1) then mMovingActive
:= false; //HACK!
889 // reverse moving direction, if necessary
890 if ((mMovingSpeed
.X
< 0) and (nx
<= mMovingStart
.X
)) or ((mMovingSpeed
.X
> 0) and (nx
>= mMovingEnd
.X
)) then
892 if mMoveOnce
then mMovingActive
:= false else mMovingSpeed
.X
:= -mMovingSpeed
.X
;
896 if ((mMovingSpeed
.Y
< 0) and (ny
<= mMovingStart
.Y
)) or ((mMovingSpeed
.Y
> 0) and (ny
>= mMovingEnd
.Y
)) then
898 if mMoveOnce
then mMovingActive
:= false else mMovingSpeed
.Y
:= -mMovingSpeed
.Y
;
902 if (mOldMovingActive
<> mMovingActive
) then mNeedSend
:= true;
903 mOldMovingActive
:= mMovingActive
;
905 if g_Game_IsServer
and g_Game_IsNet
then
907 if actMoveTrig
then g_Triggers_Press(mEndPosTrig
, ACTIVATE_CUSTOM
);
908 if actSizeTrig
then g_Triggers_Press(mEndSizeTrig
, ACTIVATE_CUSTOM
);
911 // some triggers may activate this, don't delay sending
912 //TODO: when triggers will be able to control speed and size, check that here too
913 if (mOldMovingActive
<> mMovingActive
) then mNeedSend
:= true;
914 mOldMovingActive
:= mMovingActive
;
919 procedure TPanel
.SetFrame(Frame
: Integer; Count
: Byte);
921 function ClampInt(X
, A
, B
: Integer): Integer;
924 if X
< A
then Result
:= A
else if X
> B
then Result
:= B
;
928 if Enabled
and (FCurTexture
>= 0) and
929 (FTextureIDs
[FCurTexture
].Anim
) and
930 (FTextureIDs
[FCurTexture
].AnTex
<> nil) and
931 (Width
> 0) and (Height
> 0) and (FAlpha
< 255) then
933 FCurFrame
:= ClampInt(Frame
, 0, FTextureIDs
[FCurTexture
].AnTex
.TotalFrames
);
934 FCurFrameCount
:= Count
;
935 FTextureIDs
[FCurTexture
].AnTex
.CurrentFrame
:= FCurFrame
;
936 FTextureIDs
[FCurTexture
].AnTex
.CurrentCounter
:= FCurFrameCount
;
940 procedure TPanel
.NextTexture(AnimLoop
: Byte = 0);
942 Assert(FCurTexture
>= -1, 'FCurTexture < -1');
945 if Length(FTextureIDs
) = 0 then
948 // Òîëüêî îäíà òåêñòóðà:
949 if Length(FTextureIDs
) = 1 then
951 if FCurTexture
= 0 then
957 // Áîëüøå îäíîé òåêñòóðû:
961 // Ñëåäóþùåé íåò - âîçâðàò ê íà÷àëó:
962 if FCurTexture
>= Length(FTextureIDs
) then
966 // Ïåðåêëþ÷èëèñü íà âèäèìóþ àíèì. òåêñòóðó:
967 if (FCurTexture
>= 0) and FTextureIDs
[FCurTexture
].Anim
then
969 if (FTextureIDs
[FCurTexture
].AnTex
= nil) then
971 g_FatalError(_lc
[I_GAME_ERROR_SWITCH_TEXTURE
]);
976 FTextureIDs
[FCurTexture
].AnTex
.Loop
:= True
979 FTextureIDs
[FCurTexture
].AnTex
.Loop
:= False;
981 FTextureIDs
[FCurTexture
].AnTex
.Reset();
984 LastAnimLoop
:= AnimLoop
;
987 procedure TPanel
.SetTexture(ID
: Integer; AnimLoop
: Byte = 0);
990 if Length(FTextureIDs
) = 0 then
993 // Òîëüêî îäíà òåêñòóðà:
994 if Length(FTextureIDs
) = 1 then
996 if (ID
= 0) or (ID
= -1) then
1000 // Áîëüøå îäíîé òåêñòóðû:
1002 if (ID
>= -1) and (ID
<= High(FTextureIDs
)) then
1006 // Ïåðåêëþ÷èëèñü íà âèäèìóþ àíèì. òåêñòóðó:
1007 if (FCurTexture
>= 0) and FTextureIDs
[FCurTexture
].Anim
then
1009 if (FTextureIDs
[FCurTexture
].AnTex
= nil) then
1011 g_FatalError(_lc
[I_GAME_ERROR_SWITCH_TEXTURE
]);
1015 if AnimLoop
= 1 then
1016 FTextureIDs
[FCurTexture
].AnTex
.Loop
:= True
1018 if AnimLoop
= 2 then
1019 FTextureIDs
[FCurTexture
].AnTex
.Loop
:= False;
1021 FTextureIDs
[FCurTexture
].AnTex
.Reset();
1024 LastAnimLoop
:= AnimLoop
;
1027 function TPanel
.GetTextureID(): DWORD
;
1029 Result
:= LongWord(TEXTURE_NONE
);
1031 if (FCurTexture
>= 0) then
1033 if FTextureIDs
[FCurTexture
].Anim
then
1034 Result
:= FTextureIDs
[FCurTexture
].AnTex
.FramesID
1036 Result
:= FTextureIDs
[FCurTexture
].Tex
;
1040 function TPanel
.GetTextureCount(): Integer;
1042 Result
:= Length(FTextureIDs
);
1043 if Enabled
and (FCurTexture
>= 0) then
1044 if (FTextureIDs
[FCurTexture
].Anim
) and
1045 (FTextureIDs
[FCurTexture
].AnTex
<> nil) and
1046 (Width
> 0) and (Height
> 0) and (FAlpha
< 255) then
1047 Result
:= Result
+ 100;
1051 PAN_SAVE_VERSION
= 1;
1053 procedure TPanel
.SaveState(Var Mem
: TBinMemoryWriter
);
1059 if (Mem
= nil) then exit
;
1060 //if not SaveIt then exit;
1062 // Ñèãíàòóðà ïàíåëè:
1063 sig
:= PANEL_SIGNATURE
; // 'PANL'
1064 Mem
.WriteDWORD(sig
);
1065 ver
:= PAN_SAVE_VERSION
;
1067 // Îòêðûòà/çàêðûòà, åñëè äâåðü:
1068 Mem
.WriteBoolean(FEnabled
);
1069 // Íàïðàâëåíèå ëèôòà, åñëè ëèôò:
1070 Mem
.WriteByte(FLiftType
);
1071 // Íîìåð òåêóùåé òåêñòóðû:
1072 Mem
.WriteInt(FCurTexture
);
1076 Mem
.WriteWord(FWidth
);
1077 Mem
.WriteWord(FHeight
);
1078 // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà:
1079 if (FCurTexture
>= 0) and (FTextureIDs
[FCurTexture
].Anim
) then
1081 Assert(FTextureIDs
[FCurTexture
].AnTex
<> nil,
1082 'TPanel.SaveState: No animation object');
1087 Mem
.WriteBoolean(anim
);
1088 // Åñëè äà - ñîõðàíÿåì àíèìàöèþ:
1090 FTextureIDs
[FCurTexture
].AnTex
.SaveState(Mem
);
1092 // moving platform state
1093 Mem
.WriteInt(mMovingSpeed
.X
);
1094 Mem
.WriteInt(mMovingSpeed
.Y
);
1095 Mem
.WriteInt(mMovingStart
.X
);
1096 Mem
.WriteInt(mMovingStart
.Y
);
1097 Mem
.WriteInt(mMovingEnd
.X
);
1098 Mem
.WriteInt(mMovingEnd
.Y
);
1100 Mem
.WriteInt(mSizeSpeed
.w
);
1101 Mem
.WriteInt(mSizeSpeed
.h
);
1102 Mem
.WriteInt(mSizeEnd
.w
);
1103 Mem
.WriteInt(mSizeEnd
.h
);
1104 Mem
.WriteBoolean(mMovingActive
);
1105 Mem
.WriteBoolean(mMoveOnce
);
1107 Mem
.WriteInt(mEndPosTrig
);
1108 Mem
.WriteInt(mEndSizeTrig
);
1111 procedure TPanel
.LoadState(var Mem
: TBinMemoryReader
);
1118 if (Mem
= nil) then exit
;
1119 //if not SaveIt then exit;
1121 // Ñèãíàòóðà ïàíåëè:
1123 if (sig
<> PANEL_SIGNATURE
) then raise EBinSizeError
.Create('TPanel.LoadState: wrong panel signature'); // 'PANL'
1125 if (ver
<> PAN_SAVE_VERSION
) then raise EBinSizeError
.Create('TPanel.LoadState: invalid panel version');
1126 // Îòêðûòà/çàêðûòà, åñëè äâåðü:
1127 Mem
.ReadBoolean(FEnabled
);
1128 // Íàïðàâëåíèå ëèôòà, åñëè ëèôò:
1129 Mem
.ReadByte(FLiftType
);
1130 // Íîìåð òåêóùåé òåêñòóðû:
1131 Mem
.ReadInt(FCurTexture
);
1137 Mem
.ReadWord(FWidth
);
1138 Mem
.ReadWord(FHeight
);
1139 //e_LogWritefln('panel %s(%s): old=(%s,%s); new=(%s,%s); delta=(%s,%s)', [arrIdx, proxyId, ox, oy, FX, FY, FX-ox, FY-oy]);
1140 // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà:
1141 Mem
.ReadBoolean(anim
);
1142 // Åñëè äà - çàãðóæàåì àíèìàöèþ:
1145 Assert((FCurTexture
>= 0) and
1146 (FTextureIDs
[FCurTexture
].Anim
) and
1147 (FTextureIDs
[FCurTexture
].AnTex
<> nil),
1148 'TPanel.LoadState: No animation object');
1149 FTextureIDs
[FCurTexture
].AnTex
.LoadState(Mem
);
1152 // moving platform state
1153 Mem
.ReadInt(mMovingSpeed
.X
);
1154 Mem
.ReadInt(mMovingSpeed
.Y
);
1155 Mem
.ReadInt(mMovingStart
.X
);
1156 Mem
.ReadInt(mMovingStart
.Y
);
1157 Mem
.ReadInt(mMovingEnd
.X
);
1158 Mem
.ReadInt(mMovingEnd
.Y
);
1159 Mem
.ReadInt(mSizeSpeed
.w
);
1160 Mem
.ReadInt(mSizeSpeed
.h
);
1161 Mem
.ReadInt(mSizeEnd
.w
);
1162 Mem
.ReadInt(mSizeEnd
.h
);
1163 Mem
.ReadBoolean(mMovingActive
);
1164 Mem
.ReadBoolean(mMoveOnce
);
1166 Mem
.ReadInt(mEndPosTrig
);
1167 Mem
.ReadInt(mEndSizeTrig
);
1170 //mapGrid.proxyEnabled[proxyId] := FEnabled; // done in g_map.pas