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, version 3 of the License ONLY.
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.
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/>.
15 {$INCLUDE ../shared/a_modes.inc}
23 MAPDEF
, g_textures
, xdynrec
;
26 TAddTextureArray
= array of record
27 Texture
: Cardinal; // Textures[Texture]
30 ATextureID
= array of record
31 Texture
: Cardinal; // Textures[Texture]
34 True: (AnTex
: TAnimationState
);
38 TPanel
= Class (TObject
)
42 mGUID
: Integer; // will be assigned in "g_map.pas"
45 FTextureIDs
: ATextureID
;
46 mMovingSpeed
: TDFPoint
;
47 mMovingStart
: TDFPoint
;
49 mMovingActive
: Boolean;
52 mOldMovingActive
: Boolean;
58 mEndSizeTrig
: Integer;
60 mNeedSend
: Boolean; // for network
63 function getx1 (): Integer; inline;
64 function gety1 (): Integer; inline;
65 function getvisvalid (): Boolean; inline;
67 function getMovingSpeedX (): Integer; inline;
68 procedure setMovingSpeedX (v
: Integer); inline;
69 function getMovingSpeedY (): Integer; inline;
70 procedure setMovingSpeedY (v
: Integer); inline;
72 function getMovingStartX (): Integer; inline;
73 procedure setMovingStartX (v
: Integer); inline;
74 function getMovingStartY (): Integer; inline;
75 procedure setMovingStartY (v
: Integer); inline;
77 function getMovingEndX (): Integer; inline;
78 procedure setMovingEndX (v
: Integer); inline;
79 function getMovingEndY (): Integer; inline;
80 procedure setMovingEndY (v
: Integer); inline;
82 function getSizeSpeedX (): Integer; inline;
83 procedure setSizeSpeedX (v
: Integer); inline;
84 function getSizeSpeedY (): Integer; inline;
85 procedure setSizeSpeedY (v
: Integer); inline;
87 function getSizeEndX (): Integer; inline;
88 procedure setSizeEndX (v
: Integer); inline;
89 function getSizeEndY (): Integer; inline;
90 procedure setSizeEndY (v
: Integer); inline;
93 FCurTexture
: Integer; // Íîìåð òåêóùåé òåêñòóðû
97 FWidth
, FHeight
: Word;
103 // sorry, there fields are public to allow setting 'em in g_map; this should be fixed later
104 // for now, PLEASE, don't modify 'em, or all hell will break loose
105 arrIdx
: Integer; // index in one of internal arrays; sorry
106 tag
: Integer; // used in coldets and such; sorry; see g_map.GridTagXXX
107 proxyId
: Integer; // proxy id in map grid (DO NOT USE!)
108 mapId
: AnsiString; // taken directly from map file; dunno why it is here
109 hasTexTrigger
: Boolean; // HACK: true when there's a trigger than can change my texture
111 constructor Create(PanelRec
: TDynRecord
;
112 AddTextures
: TAddTextureArray
;
114 var Textures
: TLevelTextureArray
; aguid
: Integer);
115 destructor Destroy(); override;
118 procedure SetFrame(Frame
: Integer; Count
: Byte);
119 procedure NextTexture(AnimLoop
: Byte = 0);
120 procedure SetTexture(ID
: Integer; AnimLoop
: Byte = 0);
121 function GetTextureID(): Cardinal;
122 function GetTextureCount(): Integer;
123 function CanChangeTexture(): Boolean;
125 procedure SaveState (st
: TStream
);
126 procedure LoadState (st
: TStream
);
128 procedure positionChanged (); inline;
130 function getIsGBack (): Boolean; inline; // gRenderBackgrounds
131 function getIsGStep (): Boolean; inline; // gSteps
132 function getIsGWall (): Boolean; inline; // gWalls
133 function getIsGAcid1 (): Boolean; inline; // gAcid1
134 function getIsGAcid2 (): Boolean; inline; // gAcid2
135 function getIsGWater (): Boolean; inline; // gWater
136 function getIsGFore (): Boolean; inline; // gRenderForegrounds
137 function getIsGLift (): Boolean; inline; // gLifts
138 function getIsGBlockMon (): Boolean; inline; // gBlockMon
141 function gncNeedSend (): Boolean; inline;
142 procedure setDirty (); inline; // why `dirty`? 'cause i may introduce property `needSend` later
145 property visvalid
: Boolean read getvisvalid
; // panel is "visvalid" when it's width and height are positive
148 property guid
: Integer read mGUID
; // will be assigned in "g_map.pas"
149 property x0
: Integer read FX
;
150 property y0
: Integer read FY
;
151 property x1
: Integer read getx1
; // inclusive!
152 property y1
: Integer read gety1
; // inclusive!
153 property x
: Integer read FX write FX
;
154 property y
: Integer read FY write FY
;
155 property width
: Word read FWidth write FWidth
;
156 property height
: Word read FHeight write FHeight
;
157 property panelType
: Word read FPanelType write FPanelType
;
158 property enabled
: Boolean read FEnabled write FEnabled
;
159 property door
: Boolean read FDoor write FDoor
;
160 property liftType
: Byte read FLiftType write FLiftType
;
161 property lastAnimLoop
: Byte read FLastAnimLoop write FLastAnimLoop
;
163 property movingSpeedX
: Integer read getMovingSpeedX write setMovingSpeedX
;
164 property movingSpeedY
: Integer read getMovingSpeedY write setMovingSpeedY
;
165 property movingStartX
: Integer read getMovingStartX write setMovingStartX
;
166 property movingStartY
: Integer read getMovingStartY write setMovingStartY
;
167 property movingEndX
: Integer read getMovingEndX write setMovingEndX
;
168 property movingEndY
: Integer read getMovingEndY write setMovingEndY
;
169 property movingActive
: Boolean read mMovingActive write mMovingActive
;
170 property moveOnce
: Boolean read mMoveOnce write mMoveOnce
;
172 property sizeSpeedX
: Integer read getSizeSpeedX write setSizeSpeedX
;
173 property sizeSpeedY
: Integer read getSizeSpeedY write setSizeSpeedY
;
174 property sizeEndX
: Integer read getSizeEndX write setSizeEndX
;
175 property sizeEndY
: Integer read getSizeEndY write setSizeEndY
;
177 property isGBack
: Boolean read getIsGBack
;
178 property isGStep
: Boolean read getIsGStep
;
179 property isGWall
: Boolean read getIsGWall
;
180 property isGAcid1
: Boolean read getIsGAcid1
;
181 property isGAcid2
: Boolean read getIsGAcid2
;
182 property isGWater
: Boolean read getIsGWater
;
183 property isGFore
: Boolean read getIsGFore
;
184 property isGLift
: Boolean read getIsGLift
;
185 property isGBlockMon
: Boolean read getIsGBlockMon
;
188 property Alpha
: Byte read FAlpha
;
189 property Blending
: Boolean read FBlending
;
190 property TextureIDs
: ATextureID read FTextureIDs
;
193 property movingSpeed
: TDFPoint read mMovingSpeed write mMovingSpeed
;
194 property movingStart
: TDFPoint read mMovingStart write mMovingStart
;
195 property movingEnd
: TDFPoint read mMovingEnd write mMovingEnd
;
197 property sizeSpeed
: TDFSize read mSizeSpeed write mSizeSpeed
;
198 property sizeEnd
: TDFSize read mSizeEnd write mSizeEnd
;
200 property endPosTrigId
: Integer read mEndPosTrig write mEndPosTrig
;
201 property endSizeTrigId
: Integer read mEndSizeTrig write mEndSizeTrig
;
204 TPanelArray
= Array of TPanel
;
213 g_dbgpan_mplat_active
: Boolean = {$IF DEFINED(D2F_DEBUG)}true{$ELSE}true{$ENDIF};
214 g_dbgpan_mplat_step
: Boolean = false; // one step, and stop
220 g_basic
, g_map
, g_game
, g_gfx
, g_weapons
, g_triggers
,
221 g_console
, g_language
, g_monsters
, g_player
, g_grid
, e_log
, geom
, utils
, xstreams
;
224 PANEL_SIGNATURE
= $4C4E4150; // 'PANL'
228 constructor TPanel
.Create(PanelRec
: TDynRecord
;
229 AddTextures
: TAddTextureArray
;
231 var Textures
: TLevelTextureArray
; aguid
: Integer);
238 Width
:= PanelRec
.Width
;
239 Height
:= PanelRec
.Height
;
246 mapId
:= PanelRec
.id
;
249 mMovingSpeed
:= PanelRec
.moveSpeed
;
250 mMovingStart
:= PanelRec
.moveStart
;
251 mMovingEnd
:= PanelRec
.moveEnd
;
252 mMovingActive
:= PanelRec
['move_active'].value
;
253 mOldMovingActive
:= mMovingActive
;
254 mMoveOnce
:= PanelRec
.moveOnce
;
256 mSizeSpeed
:= PanelRec
.sizeSpeed
;
257 mSizeEnd
:= PanelRec
.sizeEnd
;
259 mEndPosTrig
:= PanelRec
.endPosTrig
;
260 mEndSizeTrig
:= PanelRec
.endSizeTrig
;
265 PanelType
:= PanelRec
.PanelType
;
268 LiftType
:= LIFTTYPE_UP
;
269 hasTexTrigger
:= False;
272 PANEL_OPENDOOR
: begin Enabled
:= False; Door
:= True; end;
273 PANEL_CLOSEDOOR
: Door
:= True;
274 PANEL_LIFTUP
: LiftType
:= LIFTTYPE_UP
; //???
275 PANEL_LIFTDOWN
: LiftType
:= LIFTTYPE_DOWN
;
276 PANEL_LIFTLEFT
: LiftType
:= LIFTTYPE_LEFT
;
277 PANEL_LIFTRIGHT
: LiftType
:= LIFTTYPE_RIGHT
;
281 if ByteBool(PanelRec
.Flags
and PANEL_FLAG_HIDE
) then
283 SetLength(FTextureIDs
, 0);
287 // Ïàíåëè, íå èñïîëüçóþùèå òåêñòóðû:
288 if ByteBool(PanelType
and
293 PANEL_BLOCKMON
)) then
295 SetLength(FTextureIDs
, 0);
300 // Åñëè ýòî æèäêîñòü áåç òåêñòóðû - ñïåöòåêñòóðó:
301 if WordBool(PanelType
and (PANEL_WATER
or PANEL_ACID1
or PANEL_ACID2
)) and
302 (not ByteBool(PanelRec
.Flags
and PANEL_FLAG_WATERTEXTURES
)) then
304 SetLength(FTextureIDs
, 1);
305 FTextureIDs
[0].Anim
:= False;
309 case PanelRec.PanelType of
311 FTextureIDs[0].Tex := LongWord(TEXTURE_SPECIAL_WATER);
313 FTextureIDs[0].Tex := LongWord(TEXTURE_SPECIAL_ACID1);
315 FTextureIDs[0].Tex := LongWord(TEXTURE_SPECIAL_ACID2);
323 SetLength(FTextureIDs
, Length(AddTextures
));
328 if CurTex
>= Length(FTextureIDs
) then
329 FCurTexture
:= Length(FTextureIDs
) - 1
331 FCurTexture
:= CurTex
;
333 for i
:= 0 to Length(FTextureIDs
)-1 do
335 FTextureIDs
[i
].Texture
:= AddTextures
[i
].Texture
;
336 FTextureIDs
[i
].Anim
:= Textures
[AddTextures
[i
].Texture
].FramesCount
> 0;
337 if FTextureIDs
[i
].Anim
then
338 begin // Àíèìèðîâàííàÿ òåêñòóðà
339 FTextureIDs
[i
].AnTex
:= TAnimationState
.Create(True, Textures
[AddTextures
[i
].Texture
].Speed
, Textures
[AddTextures
[i
].Texture
].FramesCount
);
340 FTextureIDs
[i
].AnTex
.Blending
:= ByteBool(PanelRec
.Flags
and PANEL_FLAG_BLENDING
);
341 FTextureIDs
[i
].AnTex
.Alpha
:= PanelRec
.Alpha
;
345 // Òåêñòóð íåñêîëüêî - íóæíî ñîõðàíÿòü òåêóùóþ:
346 //if Length(FTextureIDs) > 1 then SaveIt := True;
348 if (PanelRec
.TextureRec
= nil) then tnum
:= -1 else tnum
:= PanelRec
.tagInt
;
349 if (tnum
< 0) then tnum
:= Length(Textures
);
351 // Åñëè íå ñïåöòåêñòóðà, òî çàäàåì ðàçìåðû:
352 if ({PanelRec.TextureNum}tnum
> High(Textures
)) then
354 e_WriteLog(Format('WTF?! tnum is out of limits! (%d : %d)', [tnum
, High(Textures
)]), TMsgType
.Warning
);
356 FBlending
:= ByteBool(0);
358 else if not g_Map_IsSpecialTexture(Textures
[{PanelRec.TextureNum}tnum
].TextureName
) then
360 FAlpha
:= PanelRec
.Alpha
;
361 FBlending
:= ByteBool(PanelRec
.Flags
and PANEL_FLAG_BLENDING
);
365 destructor TPanel
.Destroy();
369 for i
:= 0 to High(FTextureIDs
) do
370 if FTextureIDs
[i
].Anim
then
371 FTextureIDs
[i
].AnTex
.Free();
372 SetLength(FTextureIDs
, 0);
377 function TPanel
.getx1 (): Integer; inline; begin result
:= X
+Width
-1; end;
378 function TPanel
.gety1 (): Integer; inline; begin result
:= Y
+Height
-1; end;
379 function TPanel
.getvisvalid (): Boolean; inline; begin result
:= (Width
> 0) and (Height
> 0); end;
381 function TPanel
.getMovingSpeedX (): Integer; inline; begin result
:= mMovingSpeed
.X
; end;
382 procedure TPanel
.setMovingSpeedX (v
: Integer); inline; begin mMovingSpeed
.X
:= v
; end;
383 function TPanel
.getMovingSpeedY (): Integer; inline; begin result
:= mMovingSpeed
.Y
; end;
384 procedure TPanel
.setMovingSpeedY (v
: Integer); inline; begin mMovingSpeed
.Y
:= v
; end;
386 function TPanel
.getMovingStartX (): Integer; inline; begin result
:= mMovingStart
.X
; end;
387 procedure TPanel
.setMovingStartX (v
: Integer); inline; begin mMovingStart
.X
:= v
; end;
388 function TPanel
.getMovingStartY (): Integer; inline; begin result
:= mMovingStart
.Y
; end;
389 procedure TPanel
.setMovingStartY (v
: Integer); inline; begin mMovingStart
.Y
:= v
; end;
391 function TPanel
.getMovingEndX (): Integer; inline; begin result
:= mMovingEnd
.X
; end;
392 procedure TPanel
.setMovingEndX (v
: Integer); inline; begin mMovingEnd
.X
:= v
; end;
393 function TPanel
.getMovingEndY (): Integer; inline; begin result
:= mMovingEnd
.Y
; end;
394 procedure TPanel
.setMovingEndY (v
: Integer); inline; begin mMovingEnd
.Y
:= v
; end;
396 function TPanel
.getSizeSpeedX (): Integer; inline; begin result
:= mSizeSpeed
.w
; end;
397 procedure TPanel
.setSizeSpeedX (v
: Integer); inline; begin mSizeSpeed
.w
:= v
; end;
398 function TPanel
.getSizeSpeedY (): Integer; inline; begin result
:= mSizeSpeed
.h
; end;
399 procedure TPanel
.setSizeSpeedY (v
: Integer); inline; begin mSizeSpeed
.h
:= v
; end;
401 function TPanel
.getSizeEndX (): Integer; inline; begin result
:= mSizeEnd
.w
; end;
402 procedure TPanel
.setSizeEndX (v
: Integer); inline; begin mSizeEnd
.w
:= v
; end;
403 function TPanel
.getSizeEndY (): Integer; inline; begin result
:= mSizeEnd
.h
; end;
404 procedure TPanel
.setSizeEndY (v
: Integer); inline; begin mSizeEnd
.h
:= v
; end;
406 function TPanel
.getIsGBack (): Boolean; inline; begin result
:= ((tag
and GridTagBack
) <> 0); end;
407 function TPanel
.getIsGStep (): Boolean; inline; begin result
:= ((tag
and GridTagStep
) <> 0); end;
408 function TPanel
.getIsGWall (): Boolean; inline; begin result
:= ((tag
and (GridTagWall
or GridTagDoor
)) <> 0); end;
409 function TPanel
.getIsGAcid1 (): Boolean; inline; begin result
:= ((tag
and GridTagAcid1
) <> 0); end;
410 function TPanel
.getIsGAcid2 (): Boolean; inline; begin result
:= ((tag
and GridTagAcid2
) <> 0); end;
411 function TPanel
.getIsGWater (): Boolean; inline; begin result
:= ((tag
and GridTagWater
) <> 0); end;
412 function TPanel
.getIsGFore (): Boolean; inline; begin result
:= ((tag
and GridTagFore
) <> 0); end;
413 function TPanel
.getIsGLift (): Boolean; inline; begin result
:= ((tag
and GridTagLift
) <> 0); end;
414 function TPanel
.getIsGBlockMon (): Boolean; inline; begin result
:= ((tag
and GridTagBlockMon
) <> 0); end;
416 function TPanel
.gncNeedSend (): Boolean; inline; begin result
:= mNeedSend
; mNeedSend
:= false; end;
417 procedure TPanel
.setDirty (); inline; begin mNeedSend
:= true; end;
419 procedure TPanel
.positionChanged (); inline;
421 px
, py
, pw
, ph
: Integer;
423 if (proxyId
>= 0) then
425 mapGrid
.getBodyDims(proxyId
, px
, py
, pw
, ph
);
426 if (px
<> x
) or (py
<> y
) or (pw
<> Width
) or (ph
<> Height
) then
429 e_LogWritefln('panel moved: arridx=%s; guid=%s; proxyid=%s; old:(%s,%s)-(%sx%s); new:(%s,%s)-(%sx%s)',
430 [arrIdx, mGUID, proxyId, px, py, pw, ph, x, y, width, height]);
432 g_Mark(px
, py
, pw
, ph
, MARK_WALL
, false);
433 if (Width
< 1) or (Height
< 1) then
435 mapGrid
.proxyEnabled
[proxyId
] := false;
439 mapGrid
.proxyEnabled
[proxyId
] := Enabled
;
440 if (pw
<> Width
) or (ph
<> Height
) then
442 //writeln('panel resize!');
443 mapGrid
.moveResizeBody(proxyId
, X
, Y
, Width
, Height
)
447 mapGrid
.moveBody(proxyId
, X
, Y
);
449 g_Mark(X
, Y
, Width
, Height
, MARK_WALL
);
457 monCheckList
: array of TMonster
= nil;
458 monCheckListUsed
: Integer = 0;
460 procedure TPanel
.Update();
463 nx
, ny
, nw
, nh
: Integer;
464 ex
, ey
, nex
, ney
: Integer;
467 // return `true` if we should move by dx,dy
468 function tryMPlatMove (px
, py
, pw
, ph
: Integer; out dx
, dy
: Integer; out squash
: Boolean; ontop
: PBoolean=nil): Boolean;
479 pdx
:= mMovingSpeed
.X
;
480 pdy
:= mMovingSpeed
.Y
;
481 // standing on the platform?
484 if (ontop
<> nil) then ontop
^ := true;
485 // yes, move with it; but skip steps (no need to process size change here, 'cause platform top cannot be changed with it)
486 mapGrid
.traceBox(tex
, tey
, px
, py
, pw
, ph
, pdx
, pdy
, (GridTagWall
or GridTagDoor
));
490 if (ontop
<> nil) then ontop
^ := false;
491 // not standing on the platform: trace platform to see if it hits the entity
492 // first, process size change (as we cannot sweeptest both move and size change)
493 // but we don't have to check for pushing if the panel is shrinking
496 if (szdx
> 0) or (szdy
> 0) then
498 // ignore shrinking dimension
499 if (szdx
< 0) then szdx
:= 0;
500 if (szdy
< 0) then szdy
:= 0;
501 // move platform by szd* back, and check for szd* movement
502 if sweepAABB(ox
-szdx
, oy
-szdy
, nw
, nh
, szdx
, szdy
, px
, py
, pw
, ph
, @u0
) then
504 // yes, platform hits the entity, push the entity in the resizing direction
505 u0
:= 1.0-u0
; // how much path left?
506 szdx
:= trunc(szdx
*u0
);
507 szdy
:= trunc(szdy
*u0
);
508 if (szdx
<> 0) or (szdy
<> 0) then
510 // has some path to go, trace the entity
511 trtag
:= (GridTagWall
or GridTagDoor
);
512 // if we're moving down, consider steps too
513 if (szdy
> 0) then trtag
:= trtag
or GridTagStep
;
514 mapGrid
.traceBox(tex
, tey
, px
, py
, pw
, ph
, szdx
, szdy
, trtag
);
518 // second, process platform movement, using te* as entity starting point
519 if sweepAABB(ox
, oy
, nw
, nh
, pdx
, pdy
, tex
, tey
, pw
, ph
, @u0
) then
521 //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]);
522 // yes, platform hits the entity, push the entity in the direction of the platform
523 u0
:= 1.0-u0
; // how much path left?
524 pdx
:= trunc(pdx
*u0
);
525 pdy
:= trunc(pdy
*u0
);
526 //e_LogWritefln(' platsweep; uleft=%s; pd=(%s,%s)', [u0, pdx, pdy]);
527 if (pdx
<> 0) or (pdy
<> 0) then
529 // has some path to go, trace the entity
530 trtag
:= (GridTagWall
or GridTagDoor
);
531 // if we're moving down, consider steps too
532 if (pdy
> 0) then trtag
:= trtag
or GridTagStep
;
533 mapGrid
.traceBox(tex
, tey
, px
, py
, pw
, ph
, pdx
, pdy
, trtag
);
537 // done with entity movement, new coords are in te*
540 result
:= (dx
<> 0) or (dy
<> 0);
541 if ((tag
and (GridTagWall
or GridTagDoor
)) <> 0) then
543 // check for squashing; as entity cannot be pushed into a wall, check only collision with the platform itself
544 squash
:= g_Collide(tex
, tey
, pw
, ph
, nx
, ny
, nw
, nh
); // squash, if still in platform
548 function monCollect (mon
: TMonster
): Boolean;
550 result
:= false; // don't stop
551 if (monCheckListUsed
>= Length(monCheckList
)) then SetLength(monCheckList
, monCheckListUsed
+128);
552 monCheckList
[monCheckListUsed
] := mon
;
553 Inc(monCheckListUsed
);
557 cx0
, cy0
, cx1
, cy1
, cw
, ch
: Integer;
559 px
, py
, pw
, ph
, pdx
, pdy
: Integer;
567 actMoveTrig
: Boolean;
568 actSizeTrig
: Boolean;
570 if (not Enabled
) or (Width
< 1) or (Height
< 1) then exit
;
572 if (FCurTexture
>= 0) and
573 (FTextureIDs
[FCurTexture
].Anim
) and
574 (FTextureIDs
[FCurTexture
].AnTex
<> nil) and
577 FTextureIDs
[FCurTexture
].AnTex
.Update();
578 FCurFrame
:= FTextureIDs
[FCurTexture
].AnTex
.CurrentFrame
;
579 FCurFrameCount
:= FTextureIDs
[FCurTexture
].AnTex
.CurrentCounter
;
582 if not g_dbgpan_mplat_active
then exit
;
584 if (mOldMovingActive
<> mMovingActive
) then mNeedSend
:= true;
585 mOldMovingActive
:= mMovingActive
;
587 if not mMovingActive
then exit
;
588 if mMovingSpeed
.isZero
and mSizeSpeed
.isZero
then exit
;
590 //TODO: write wall size change processing
595 * collect all monsters and players (aka entities) along the possible platform path
596 * if entity is standing on a platform:
597 * try to move it along the platform path, checking wall collisions
598 * if entity is NOT standing on a platform, but hit with sweeped platform aabb:
600 * if we can't push entity all the way, squash it
607 nw
:= mpw
+mSizeSpeed
.w
;
608 nh
:= mph
+mSizeSpeed
.h
;
609 nx
:= ox
+mMovingSpeed
.X
;
610 ny
:= oy
+mMovingSpeed
.Y
;
612 // force network updates only if some sudden change happened
613 // set the flag here, so we can sync affected monsters
614 if not mSizeSpeed
.isZero
and (nw
= mSizeEnd
.w
) and (nh
= mSizeEnd
.h
) then
618 else if ((mMovingSpeed
.X
< 0) and (nx
<= mMovingStart
.X
)) or ((mMovingSpeed
.X
> 0) and (nx
>= mMovingEnd
.X
)) then
622 else if ((mMovingSpeed
.Y
< 0) and (ny
<= mMovingStart
.Y
)) or ((mMovingSpeed
.Y
> 0) and (ny
>= mMovingEnd
.Y
)) then
627 // if pannel disappeared, we don't have to do anything
628 if (nw
> 0) and (nh
> 0) then
639 cx1
:= nmax(ex
, nex
);
640 cy1
:= nmax(ey
, ney
);
649 // process "obstacle" panels
650 if ((tag
and GridTagObstacle
) <> 0) then
652 // temporarily turn off this panel, so it won't interfere with collision checks
653 mapGrid
.proxyEnabled
[proxyId
] := false;
656 for f
:= 0 to High(gPlayers
) do
659 if (plr
= nil) or (not plr
.alive
) then continue
;
660 plr
.getMapBox(px
, py
, pw
, ph
);
661 if not g_Collide(px
, py
, pw
, ph
, cx0
, cy0
, cw
, ch
) then continue
;
662 if tryMPlatMove(px
, py
, pw
, ph
, pdx
, pdy
, squash
) then
665 plr
.moveBy(pdx
, pdy
); // this will call `positionChanged()` for us
667 // squash player, if necessary
668 if not g_Game_IsClient
and squash
then plr
.Damage(15000, 0, 0, 0, HIT_TRAP
);
672 for f
:= 0 to High(gGibs
) do
675 if not gib
.alive
then continue
;
676 gib
.getMapBox(px
, py
, pw
, ph
);
677 if not g_Collide(px
, py
, pw
, ph
, cx0
, cy0
, cw
, ch
) then continue
;
678 if tryMPlatMove(px
, py
, pw
, ph
, pdx
, pdy
, squash
, @ontop
) then
681 gib
.moveBy(pdx
, pdy
); // this will call `positionChanged()` for us
685 // move and push corpses
686 for f
:= 0 to High(gCorpses
) do
689 if (cor
= nil) then continue
;
690 cor
.getMapBox(px
, py
, pw
, ph
);
691 if not g_Collide(px
, py
, pw
, ph
, cx0
, cy0
, cw
, ch
) then continue
;
692 if tryMPlatMove(px
, py
, pw
, ph
, pdx
, pdy
, squash
, @ontop
) then
695 cor
.moveBy(pdx
, pdy
); // this will call `positionChanged()` for us
700 monCheckListUsed
:= 0;
701 g_Mons_ForEachAt(cx0
, cy0
, cw
, ch
, monCollect
);
703 // process collected monsters
704 if (monCheckListUsed
> 0) then
706 mpfrid
:= g_Mons_getNewMPlatFrameId();
707 for f
:= 0 to monCheckListUsed
do
709 mon
:= monCheckList
[f
];
710 if (mon
= nil) or (not mon
.alive
) or (mon
.mplatCheckFrameId
= mpfrid
) then continue
;
711 mon
.mplatCheckFrameId
:= mpfrid
;
712 mon
.getMapBox(px
, py
, pw
, ph
);
713 //if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue;
714 if tryMPlatMove(px
, py
, pw
, ph
, pdx
, pdy
, squash
) then
717 mon
.moveBy(pdx
, pdy
); // this will call `positionChanged()` for us
718 //???FIXME: do we really need to send monsters over the net?
719 // i don't think so, as dead reckoning should take care of 'em
720 // ok, send new monster position only if platform is going to change it's direction
721 if mNeedSend
then mon
.setDirty();
723 // squash monster, if necessary
724 if not g_Game_IsClient
and squash
then mon
.Damage(15000, 0, 0, 0, HIT_TRAP
);
728 // restore panel state
729 mapGrid
.proxyEnabled
[proxyId
] := true;
740 actMoveTrig
:= false;
741 actSizeTrig
:= false;
743 // `mNeedSend` was set above
746 if not mSizeSpeed
.isZero
and (nw
= mSizeEnd
.w
) and (nh
= mSizeEnd
.h
) then
751 if (nw
< 1) or (nh
< 1) then mMovingActive
:= false; //HACK!
754 // reverse moving direction, if necessary
755 if ((mMovingSpeed
.X
< 0) and (nx
<= mMovingStart
.X
)) or ((mMovingSpeed
.X
> 0) and (nx
>= mMovingEnd
.X
)) then
757 if mMoveOnce
then mMovingActive
:= false else mMovingSpeed
.X
:= -mMovingSpeed
.X
;
761 if ((mMovingSpeed
.Y
< 0) and (ny
<= mMovingStart
.Y
)) or ((mMovingSpeed
.Y
> 0) and (ny
>= mMovingEnd
.Y
)) then
763 if mMoveOnce
then mMovingActive
:= false else mMovingSpeed
.Y
:= -mMovingSpeed
.Y
;
767 if (mOldMovingActive
<> mMovingActive
) then mNeedSend
:= true;
768 mOldMovingActive
:= mMovingActive
;
770 if not g_Game_IsClient
then
772 if actMoveTrig
then g_Triggers_Press(mEndPosTrig
, ACTIVATE_CUSTOM
);
773 if actSizeTrig
then g_Triggers_Press(mEndSizeTrig
, ACTIVATE_CUSTOM
);
776 // some triggers may activate this, don't delay sending
777 //TODO: when triggers will be able to control speed and size, check that here too
778 if (mOldMovingActive
<> mMovingActive
) then mNeedSend
:= true;
779 mOldMovingActive
:= mMovingActive
;
784 procedure TPanel
.SetFrame(Frame
: Integer; Count
: Byte);
786 function ClampInt(X
, A
, B
: Integer): Integer;
789 if X
< A
then Result
:= A
else if X
> B
then Result
:= B
;
793 if Enabled
and (FCurTexture
>= 0) and
794 (FTextureIDs
[FCurTexture
].Anim
) and
795 (FTextureIDs
[FCurTexture
].AnTex
<> nil) and
796 (Width
> 0) and (Height
> 0) and (FAlpha
< 255) then
798 FCurFrame
:= ClampInt(Frame
, 0, FTextureIDs
[FCurTexture
].AnTex
.TotalFrames
- 1);
799 FCurFrameCount
:= Count
;
800 FTextureIDs
[FCurTexture
].AnTex
.CurrentFrame
:= FCurFrame
;
801 FTextureIDs
[FCurTexture
].AnTex
.CurrentCounter
:= FCurFrameCount
;
805 procedure TPanel
.NextTexture(AnimLoop
: Byte = 0);
807 Assert(FCurTexture
>= -1, 'FCurTexture < -1');
810 if Length(FTextureIDs
) = 0 then
813 // Òîëüêî îäíà òåêñòóðà:
814 if Length(FTextureIDs
) = 1 then
816 if FCurTexture
= 0 then
822 // Áîëüøå îäíîé òåêñòóðû:
826 // Ñëåäóþùåé íåò - âîçâðàò ê íà÷àëó:
827 if FCurTexture
>= Length(FTextureIDs
) then
831 // Ïåðåêëþ÷èëèñü íà âèäèìóþ àíèì. òåêñòóðó:
832 if (FCurTexture
>= 0) and FTextureIDs
[FCurTexture
].Anim
then
834 if (FTextureIDs
[FCurTexture
].AnTex
= nil) then
836 g_FatalError(_lc
[I_GAME_ERROR_SWITCH_TEXTURE
]);
841 FTextureIDs
[FCurTexture
].AnTex
.Loop
:= True
844 FTextureIDs
[FCurTexture
].AnTex
.Loop
:= False;
846 FTextureIDs
[FCurTexture
].AnTex
.Reset();
849 LastAnimLoop
:= AnimLoop
;
852 procedure TPanel
.SetTexture(ID
: Integer; AnimLoop
: Byte = 0);
854 if (ID
>= -1) and (ID
< Length(FTextureIDs
)) then
857 // Ïåðåêëþ÷èëèñü íà âèäèìóþ àíèì. òåêñòóðó:
858 if (FCurTexture
>= 0) and FTextureIDs
[FCurTexture
].Anim
then
860 if (FTextureIDs
[FCurTexture
].AnTex
= nil) then
862 g_FatalError(_lc
[I_GAME_ERROR_SWITCH_TEXTURE
]);
867 FTextureIDs
[FCurTexture
].AnTex
.Loop
:= True
870 FTextureIDs
[FCurTexture
].AnTex
.Loop
:= False;
872 FTextureIDs
[FCurTexture
].AnTex
.Reset();
875 LastAnimLoop
:= AnimLoop
;
878 function TPanel
.GetTextureID(): DWORD
;
880 Result
:= LongWord(TEXTURE_NONE
);
881 // if (FCurTexture >= 0) then
883 // if FTextureIDs[FCurTexture].Anim then
884 // Result := Textures[FTextureIDs[FCurTexture].Texture].FramesID
886 // Result := Textures[FTextureIDs[FCurTexture].Texture].TextureID
889 if FTextureIDs[FCurTexture].Anim then
890 Result := FTextureIDs[FCurTexture].AnTex.FramesID
892 Result := FTextureIDs[FCurTexture].Tex;
897 function TPanel
.GetTextureCount(): Integer;
899 Result
:= Length(FTextureIDs
);
900 if Enabled
and (FCurTexture
>= 0) then
901 if (FTextureIDs
[FCurTexture
].Anim
) and
902 (FTextureIDs
[FCurTexture
].AnTex
<> nil) and
903 (Width
> 0) and (Height
> 0) and (FAlpha
< 255) then
904 Result
:= Result
+ 100;
907 function TPanel
.CanChangeTexture(): Boolean;
909 Result
:= (GetTextureCount() > 1) or hasTexTrigger
;
913 PAN_SAVE_VERSION
= 1;
915 procedure TPanel
.SaveState (st
: TStream
);
919 if (st
= nil) then exit
;
922 utils
.writeSign(st
, 'PANL');
923 utils
.writeInt(st
, Byte(PAN_SAVE_VERSION
));
924 // Îòêðûòà/çàêðûòà, åñëè äâåðü
925 utils
.writeBool(st
, FEnabled
);
926 // Íàïðàâëåíèå ëèôòà, åñëè ëèôò
927 utils
.writeInt(st
, Byte(FLiftType
));
928 // Íîìåð òåêóùåé òåêñòóðû
929 utils
.writeInt(st
, Integer(FCurTexture
));
930 // Êîîðäèíàòû è ðàçìåð
931 utils
.writeInt(st
, Integer(FX
));
932 utils
.writeInt(st
, Integer(FY
));
933 utils
.writeInt(st
, Word(FWidth
));
934 utils
.writeInt(st
, Word(FHeight
));
935 // Àíèìèðîâàíà ëè òåêóùàÿ òåêñòóðà
936 if (FCurTexture
>= 0) and (FTextureIDs
[FCurTexture
].Anim
) then
938 assert(FTextureIDs
[FCurTexture
].AnTex
<> nil, 'TPanel.SaveState: No animation object');
945 utils
.writeBool(st
, anim
);
946 // Åñëè äà - ñîõðàíÿåì àíèìàöèþ
947 if anim
then FTextureIDs
[FCurTexture
].AnTex
.SaveState(st
);
949 // moving platform state
950 utils
.writeInt(st
, Integer(mMovingSpeed
.X
));
951 utils
.writeInt(st
, Integer(mMovingSpeed
.Y
));
952 utils
.writeInt(st
, Integer(mMovingStart
.X
));
953 utils
.writeInt(st
, Integer(mMovingStart
.Y
));
954 utils
.writeInt(st
, Integer(mMovingEnd
.X
));
955 utils
.writeInt(st
, Integer(mMovingEnd
.Y
));
957 utils
.writeInt(st
, Integer(mSizeSpeed
.w
));
958 utils
.writeInt(st
, Integer(mSizeSpeed
.h
));
959 utils
.writeInt(st
, Integer(mSizeEnd
.w
));
960 utils
.writeInt(st
, Integer(mSizeEnd
.h
));
962 utils
.writeBool(st
, mMovingActive
);
963 utils
.writeBool(st
, mMoveOnce
);
965 utils
.writeInt(st
, Integer(mEndPosTrig
));
966 utils
.writeInt(st
, Integer(mEndSizeTrig
));
970 procedure TPanel
.LoadState (st
: TStream
);
972 if (st
= nil) then exit
;
975 if not utils
.checkSign(st
, 'PANL') then raise XStreamError
.create('wrong panel signature');
976 if (utils
.readByte(st
) <> PAN_SAVE_VERSION
) then raise XStreamError
.create('wrong panel version');
977 // Îòêðûòà/çàêðûòà, åñëè äâåðü
978 FEnabled
:= utils
.readBool(st
);
979 // Íàïðàâëåíèå ëèôòà, åñëè ëèôò
980 FLiftType
:= utils
.readByte(st
);
981 // Íîìåð òåêóùåé òåêñòóðû
982 FCurTexture
:= utils
.readLongInt(st
);
983 // Êîîðäèíàòû è ðàçìåð
984 FX
:= utils
.readLongInt(st
);
985 FY
:= utils
.readLongInt(st
);
986 FWidth
:= utils
.readWord(st
);
987 FHeight
:= utils
.readWord(st
);
988 // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà
989 if utils
.readBool(st
) then
991 // Åñëè äà - çàãðóæàåì àíèìàöèþ
992 Assert((FCurTexture
>= 0) and
993 (FTextureIDs
[FCurTexture
].Anim
) and
994 (FTextureIDs
[FCurTexture
].AnTex
<> nil),
995 'TPanel.LoadState: No animation object');
996 FTextureIDs
[FCurTexture
].AnTex
.LoadState(st
);
999 // moving platform state
1000 mMovingSpeed
.X
:= utils
.readLongInt(st
);
1001 mMovingSpeed
.Y
:= utils
.readLongInt(st
);
1002 mMovingStart
.X
:= utils
.readLongInt(st
);
1003 mMovingStart
.Y
:= utils
.readLongInt(st
);
1004 mMovingEnd
.X
:= utils
.readLongInt(st
);
1005 mMovingEnd
.Y
:= utils
.readLongInt(st
);
1007 mSizeSpeed
.w
:= utils
.readLongInt(st
);
1008 mSizeSpeed
.h
:= utils
.readLongInt(st
);
1009 mSizeEnd
.w
:= utils
.readLongInt(st
);
1010 mSizeEnd
.h
:= utils
.readLongInt(st
);
1012 mMovingActive
:= utils
.readBool(st
);
1013 mMoveOnce
:= utils
.readBool(st
);
1015 mEndPosTrig
:= utils
.readLongInt(st
);
1016 mEndSizeTrig
:= utils
.readLongInt(st
);
1019 //mapGrid.proxyEnabled[proxyId] := FEnabled; // done in g_map.pas