DEADSOFTWARE

entity now can be squashed by growing mplats (imagine lowering ceiling, for example)
[d2df-sdl.git] / src / game / g_panel.pas
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+}
18 unit g_panel;
20 interface
22 uses
23 MAPDEF, BinEditor, g_textures, xdynrec;
25 type
26 TAddTextureArray = Array of
27 record
28 Texture: Cardinal;
29 Anim: Boolean;
30 end;
32 TPanel = Class (TObject)
33 private
34 const
35 private
36 mGUID: Integer; // will be assigned in "g_map.pas"
37 FTextureWidth: Word;
38 FTextureHeight: Word;
39 FAlpha: Byte;
40 FBlending: Boolean;
41 FTextureIDs: Array of
42 record
43 case Anim: Boolean of
44 False: (Tex: Cardinal);
45 True: (AnTex: TAnimation);
46 end;
48 mMovingSpeed: TDFPoint;
49 mMovingStart: TDFPoint;
50 mMovingEnd: TDFPoint;
51 mMovingActive: Boolean;
52 mMoveOnce: Boolean;
54 mSizeSpeed: TDFSize;
55 mSizeEnd: TDFSize;
57 mEndPosTrig: Integer;
58 mEndSizeTrig: Integer;
60 private
61 function getx1 (): Integer; inline;
62 function gety1 (): Integer; inline;
63 function getvisvalid (): Boolean; inline;
65 function getMovingSpeedX (): Integer; inline;
66 procedure setMovingSpeedX (v: Integer); inline;
67 function getMovingSpeedY (): Integer; inline;
68 procedure setMovingSpeedY (v: Integer); inline;
70 function getMovingStartX (): Integer; inline;
71 procedure setMovingStartX (v: Integer); inline;
72 function getMovingStartY (): Integer; inline;
73 procedure setMovingStartY (v: Integer); inline;
75 function getMovingEndX (): Integer; inline;
76 procedure setMovingEndX (v: Integer); inline;
77 function getMovingEndY (): Integer; inline;
78 procedure setMovingEndY (v: Integer); inline;
80 public
81 FCurTexture: Integer; // Íîìåð òåêóùåé òåêñòóðû
82 FCurFrame: Integer;
83 FCurFrameCount: Byte;
84 FX, FY: Integer;
85 FWidth, FHeight: Word;
86 FPanelType: Word;
87 FSaveIt: Boolean; // Ñîõðàíÿòü ïðè SaveState?
88 FEnabled: Boolean;
89 FDoor: Boolean;
90 FMoved: Boolean;
91 FLiftType: Byte;
92 FLastAnimLoop: Byte;
93 // sorry, there fields are public to allow setting 'em in g_map; this should be fixed later
94 // for now, PLEASE, don't modify 'em, or all hell will break loose
95 arrIdx: Integer; // index in one of internal arrays; sorry
96 tag: Integer; // used in coldets and such; sorry; see g_map.GridTagXXX
97 proxyId: Integer; // proxy id in map grid (DO NOT USE!)
98 mapId: AnsiString; // taken directly from map file; dunno why it is here
100 constructor Create(PanelRec: TDynRecord;
101 AddTextures: TAddTextureArray;
102 CurTex: Integer;
103 var Textures: TLevelTextureArray; aguid: Integer);
104 destructor Destroy(); override;
106 procedure Draw();
107 procedure DrawShadowVolume(lightX: Integer; lightY: Integer; radius: Integer);
108 procedure Update();
109 procedure SetFrame(Frame: Integer; Count: Byte);
110 procedure NextTexture(AnimLoop: Byte = 0);
111 procedure SetTexture(ID: Integer; AnimLoop: Byte = 0);
112 function GetTextureID(): Cardinal;
113 function GetTextureCount(): Integer;
115 procedure SaveState(var Mem: TBinMemoryWriter);
116 procedure LoadState(var Mem: TBinMemoryReader);
118 procedure positionChanged (); inline;
120 function getIsGBack (): Boolean; inline; // gRenderBackgrounds
121 function getIsGStep (): Boolean; inline; // gSteps
122 function getIsGWall (): Boolean; inline; // gWalls
123 function getIsGAcid1 (): Boolean; inline; // gAcid1
124 function getIsGAcid2 (): Boolean; inline; // gAcid2
125 function getIsGWater (): Boolean; inline; // gWater
126 function getIsGFore (): Boolean; inline; // gRenderForegrounds
127 function getIsGLift (): Boolean; inline; // gLifts
128 function getIsGBlockMon (): Boolean; inline; // gBlockMon
130 public
131 property visvalid: Boolean read getvisvalid; // panel is "visvalid" when it's width and height are positive
133 published
134 property guid: Integer read mGUID; // will be assigned in "g_map.pas"
135 property x0: Integer read FX;
136 property y0: Integer read FY;
137 property x1: Integer read getx1; // inclusive!
138 property y1: Integer read gety1; // inclusive!
139 property x: Integer read FX write FX;
140 property y: Integer read FY write FY;
141 property width: Word read FWidth write FWidth;
142 property height: Word read FHeight write FHeight;
143 property panelType: Word read FPanelType write FPanelType;
144 property saveIt: Boolean read FSaveIt write FSaveIt; // Ñîõðàíÿòü ïðè SaveState?
145 property enabled: Boolean read FEnabled write FEnabled; // Ñîõðàíÿòü ïðè SaveState?
146 property door: Boolean read FDoor write FDoor; // Ñîõðàíÿòü ïðè SaveState?
147 property moved: Boolean read FMoved write FMoved; // Ñîõðàíÿòü ïðè SaveState?
148 property liftType: Byte read FLiftType write FLiftType; // Ñîõðàíÿòü ïðè SaveState?
149 property lastAnimLoop: Byte read FLastAnimLoop write FLastAnimLoop; // Ñîõðàíÿòü ïðè SaveState?
151 property movingSpeedX: Integer read getMovingSpeedX write setMovingSpeedX;
152 property movingSpeedY: Integer read getMovingSpeedY write setMovingSpeedY;
153 property movingStartX: Integer read getMovingStartX write setMovingStartX;
154 property movingStartY: Integer read getMovingStartY write setMovingStartY;
155 property movingEndX: Integer read getMovingEndX write setMovingEndX;
156 property movingEndY: Integer read getMovingEndY write setMovingEndY;
157 property movingActive: Boolean read mMovingActive write mMovingActive;
158 property moveOnce: Boolean read mMoveOnce write mMoveOnce;
160 property isGBack: Boolean read getIsGBack;
161 property isGStep: Boolean read getIsGStep;
162 property isGWall: Boolean read getIsGWall;
163 property isGAcid1: Boolean read getIsGAcid1;
164 property isGAcid2: Boolean read getIsGAcid2;
165 property isGWater: Boolean read getIsGWater;
166 property isGFore: Boolean read getIsGFore;
167 property isGLift: Boolean read getIsGLift;
168 property isGBlockMon: Boolean read getIsGBlockMon;
170 public
171 property movingSpeed: TDFPoint read mMovingSpeed write mMovingSpeed;
172 property movingStart: TDFPoint read mMovingStart write mMovingStart;
173 property movingEnd: TDFPoint read mMovingEnd write mMovingEnd;
175 property endPosTrigId: Integer read mEndPosTrig write mEndPosTrig;
176 property endSizeTrigId: Integer read mEndSizeTrig write mEndSizeTrig;
177 end;
179 TPanelArray = Array of TPanel;
181 var
182 g_dbgpan_mplat_active: Boolean = {$IF DEFINED(D2F_DEBUG)}true{$ELSE}true{$ENDIF};
183 g_dbgpan_mplat_step: Boolean = false; // one step, and stop
186 implementation
188 uses
189 SysUtils, g_basic, g_map, g_game, g_gfx, e_graphics, g_weapons, g_triggers,
190 g_console, g_language, g_monsters, g_player, g_grid, e_log, GL, utils;
192 const
193 PANEL_SIGNATURE = $4C4E4150; // 'PANL'
195 { T P a n e l : }
197 constructor TPanel.Create(PanelRec: TDynRecord;
198 AddTextures: TAddTextureArray;
199 CurTex: Integer;
200 var Textures: TLevelTextureArray; aguid: Integer);
201 var
202 i: Integer;
203 begin
204 X := PanelRec.X;
205 Y := PanelRec.Y;
206 Width := PanelRec.Width;
207 Height := PanelRec.Height;
208 FAlpha := 0;
209 FBlending := False;
210 FCurFrame := 0;
211 FCurFrameCount := 0;
212 LastAnimLoop := 0;
213 Moved := False;
215 mapId := PanelRec.id;
216 mGUID := aguid;
218 mMovingSpeed := PanelRec.moveSpeed;
219 mMovingStart := PanelRec.moveStart;
220 mMovingEnd := PanelRec.moveEnd;
221 mMovingActive := PanelRec['move_active'].varvalue;
222 mMoveOnce := PanelRec.moveOnce;
224 mSizeSpeed := PanelRec.sizeSpeed;
225 mSizeEnd := PanelRec.sizeEnd;
227 mEndPosTrig := PanelRec.endPosTrig;
228 mEndSizeTrig := PanelRec.endSizeTrig;
230 // Òèï ïàíåëè:
231 PanelType := PanelRec.PanelType;
232 Enabled := True;
233 Door := False;
234 LiftType := 0;
235 SaveIt := False;
237 case PanelType of
238 PANEL_OPENDOOR:
239 begin
240 Enabled := False;
241 Door := True;
242 SaveIt := True;
243 end;
244 PANEL_CLOSEDOOR:
245 begin
246 Door := True;
247 SaveIt := True;
248 end;
249 PANEL_LIFTUP:
250 SaveIt := True;
251 PANEL_LIFTDOWN:
252 begin
253 LiftType := 1;
254 SaveIt := True;
255 end;
256 PANEL_LIFTLEFT:
257 begin
258 LiftType := 2;
259 SaveIt := True;
260 end;
261 PANEL_LIFTRIGHT:
262 begin
263 LiftType := 3;
264 SaveIt := True;
265 end;
266 end;
268 // Íåâèäèìàÿ:
269 if ByteBool(PanelRec.Flags and PANEL_FLAG_HIDE) then
270 begin
271 SetLength(FTextureIDs, 0);
272 FCurTexture := -1;
273 Exit;
274 end;
275 // Ïàíåëè, íå èñïîëüçóþùèå òåêñòóðû:
276 if ByteBool(PanelType and
277 (PANEL_LIFTUP or
278 PANEL_LIFTDOWN or
279 PANEL_LIFTLEFT or
280 PANEL_LIFTRIGHT or
281 PANEL_BLOCKMON)) then
282 begin
283 SetLength(FTextureIDs, 0);
284 FCurTexture := -1;
285 Exit;
286 end;
288 // Åñëè ýòî æèäêîñòü áåç òåêñòóðû - ñïåöòåêñòóðó:
289 if WordBool(PanelType and (PANEL_WATER or PANEL_ACID1 or PANEL_ACID2)) and
290 (not ByteBool(PanelRec.Flags and PANEL_FLAG_WATERTEXTURES)) then
291 begin
292 SetLength(FTextureIDs, 1);
293 FTextureIDs[0].Anim := False;
295 case PanelRec.PanelType of
296 PANEL_WATER:
297 FTextureIDs[0].Tex := LongWord(TEXTURE_SPECIAL_WATER);
298 PANEL_ACID1:
299 FTextureIDs[0].Tex := LongWord(TEXTURE_SPECIAL_ACID1);
300 PANEL_ACID2:
301 FTextureIDs[0].Tex := LongWord(TEXTURE_SPECIAL_ACID2);
302 end;
304 FCurTexture := 0;
305 Exit;
306 end;
308 SetLength(FTextureIDs, Length(AddTextures));
310 if CurTex < 0 then
311 FCurTexture := -1
312 else
313 if CurTex >= Length(FTextureIDs) then
314 FCurTexture := Length(FTextureIDs) - 1
315 else
316 FCurTexture := CurTex;
318 for i := 0 to Length(FTextureIDs)-1 do
319 begin
320 FTextureIDs[i].Anim := AddTextures[i].Anim;
321 if FTextureIDs[i].Anim then
322 begin // Àíèìèðîâàííàÿ òåêñòóðà
323 FTextureIDs[i].AnTex :=
324 TAnimation.Create(Textures[AddTextures[i].Texture].FramesID,
325 True, Textures[AddTextures[i].Texture].Speed);
326 FTextureIDs[i].AnTex.Blending := ByteBool(PanelRec.Flags and PANEL_FLAG_BLENDING);
327 FTextureIDs[i].AnTex.Alpha := PanelRec.Alpha;
328 SaveIt := True;
329 end
330 else
331 begin // Îáû÷íàÿ òåêñòóðà
332 FTextureIDs[i].Tex := Textures[AddTextures[i].Texture].TextureID;
333 end;
334 end;
336 // Òåêñòóð íåñêîëüêî - íóæíî ñîõðàíÿòü òåêóùóþ:
337 if Length(FTextureIDs) > 1 then
338 SaveIt := True;
340 // Åñëè íå ñïåöòåêñòóðà, òî çàäàåì ðàçìåðû:
341 if PanelRec.TextureNum > High(Textures) then
342 begin
343 e_WriteLog(Format('WTF?! PanelRec.TextureNum is out of limits! (%d : %d)', [PanelRec.TextureNum, High(Textures)]), MSG_FATALERROR);
344 FTextureWidth := 2;
345 FTextureHeight := 2;
346 FAlpha := 0;
347 FBlending := ByteBool(0);
348 end
349 else if not g_Map_IsSpecialTexture(Textures[PanelRec.TextureNum].TextureName) then
350 begin
351 FTextureWidth := Textures[PanelRec.TextureNum].Width;
352 FTextureHeight := Textures[PanelRec.TextureNum].Height;
353 FAlpha := PanelRec.Alpha;
354 FBlending := ByteBool(PanelRec.Flags and PANEL_FLAG_BLENDING);
355 end;
356 end;
358 destructor TPanel.Destroy();
359 var
360 i: Integer;
361 begin
362 for i := 0 to High(FTextureIDs) do
363 if FTextureIDs[i].Anim then
364 FTextureIDs[i].AnTex.Free();
365 SetLength(FTextureIDs, 0);
367 Inherited;
368 end;
370 function TPanel.getx1 (): Integer; inline; begin result := X+Width-1; end;
371 function TPanel.gety1 (): Integer; inline; begin result := Y+Height-1; end;
372 function TPanel.getvisvalid (): Boolean; inline; begin result := (Width > 0) and (Height > 0); end;
374 function TPanel.getMovingSpeedX (): Integer; inline; begin result := mMovingSpeed.X; end;
375 procedure TPanel.setMovingSpeedX (v: Integer); inline; begin mMovingSpeed.X := v; end;
376 function TPanel.getMovingSpeedY (): Integer; inline; begin result := mMovingSpeed.Y; end;
377 procedure TPanel.setMovingSpeedY (v: Integer); inline; begin mMovingSpeed.Y := v; end;
379 function TPanel.getMovingStartX (): Integer; inline; begin result := mMovingStart.X; end;
380 procedure TPanel.setMovingStartX (v: Integer); inline; begin mMovingStart.X := v; end;
381 function TPanel.getMovingStartY (): Integer; inline; begin result := mMovingStart.Y; end;
382 procedure TPanel.setMovingStartY (v: Integer); inline; begin mMovingStart.Y := v; end;
384 function TPanel.getMovingEndX (): Integer; inline; begin result := mMovingEnd.X; end;
385 procedure TPanel.setMovingEndX (v: Integer); inline; begin mMovingEnd.X := v; end;
386 function TPanel.getMovingEndY (): Integer; inline; begin result := mMovingEnd.Y; end;
387 procedure TPanel.setMovingEndY (v: Integer); inline; begin mMovingEnd.Y := v; end;
389 function TPanel.getIsGBack (): Boolean; inline; begin result := ((tag and GridTagBack) <> 0); end;
390 function TPanel.getIsGStep (): Boolean; inline; begin result := ((tag and GridTagStep) <> 0); end;
391 function TPanel.getIsGWall (): Boolean; inline; begin result := ((tag and (GridTagWall or GridTagDoor)) <> 0); end;
392 function TPanel.getIsGAcid1 (): Boolean; inline; begin result := ((tag and GridTagAcid1) <> 0); end;
393 function TPanel.getIsGAcid2 (): Boolean; inline; begin result := ((tag and GridTagAcid2) <> 0); end;
394 function TPanel.getIsGWater (): Boolean; inline; begin result := ((tag and GridTagWater) <> 0); end;
395 function TPanel.getIsGFore (): Boolean; inline; begin result := ((tag and GridTagFore) <> 0); end;
396 function TPanel.getIsGLift (): Boolean; inline; begin result := ((tag and GridTagLift) <> 0); end;
397 function TPanel.getIsGBlockMon (): Boolean; inline; begin result := ((tag and GridTagBlockMon) <> 0); end;
399 procedure TPanel.Draw();
400 var
401 xx, yy: Integer;
402 NoTextureID: DWORD;
403 NW, NH: Word;
404 begin
405 if {Enabled and} (FCurTexture >= 0) and
406 (Width > 0) and (Height > 0) and (FAlpha < 255) and
407 ((g_dbg_scale <> 1.0) or g_Collide(X, Y, Width, Height, sX, sY, sWidth, sHeight)) then
408 begin
409 if FTextureIDs[FCurTexture].Anim then
410 begin // Àíèìèðîâàííàÿ òåêñòóðà
411 if FTextureIDs[FCurTexture].AnTex = nil then
412 Exit;
414 for xx := 0 to (Width div FTextureWidth)-1 do
415 for yy := 0 to (Height div FTextureHeight)-1 do
416 FTextureIDs[FCurTexture].AnTex.Draw(
417 X + xx*FTextureWidth,
418 Y + yy*FTextureHeight, M_NONE);
419 end
420 else
421 begin // Îáû÷íàÿ òåêñòóðà
422 case FTextureIDs[FCurTexture].Tex of
423 LongWord(TEXTURE_SPECIAL_WATER):
424 e_DrawFillQuad(X, Y, X+Width-1, Y+Height-1,
425 0, 0, 255, 0, B_FILTER);
426 LongWord(TEXTURE_SPECIAL_ACID1):
427 e_DrawFillQuad(X, Y, X+Width-1, Y+Height-1,
428 0, 128, 0, 0, B_FILTER);
429 LongWord(TEXTURE_SPECIAL_ACID2):
430 e_DrawFillQuad(X, Y, X+Width-1, Y+Height-1,
431 128, 0, 0, 0, B_FILTER);
432 LongWord(TEXTURE_NONE):
433 if g_Texture_Get('NOTEXTURE', NoTextureID) then
434 begin
435 e_GetTextureSize(NoTextureID, @NW, @NH);
436 e_DrawFill(NoTextureID, X, Y, Width div NW, Height div NH,
437 0, False, False);
438 end else
439 begin
440 xx := X + (Width div 2);
441 yy := Y + (Height div 2);
442 e_DrawFillQuad(X, Y, xx, yy,
443 255, 0, 255, 0);
444 e_DrawFillQuad(xx, Y, X+Width-1, yy,
445 255, 255, 0, 0);
446 e_DrawFillQuad(X, yy, xx, Y+Height-1,
447 255, 255, 0, 0);
448 e_DrawFillQuad(xx, yy, X+Width-1, Y+Height-1,
449 255, 0, 255, 0);
450 end;
452 else
453 if not mMovingActive then
454 e_DrawFill(FTextureIDs[FCurTexture].Tex, X, Y, Width div FTextureWidth, Height div FTextureHeight, FAlpha, True, FBlending)
455 else
456 e_DrawFillX(FTextureIDs[FCurTexture].Tex, X, Y, Width, Height, FAlpha, True, FBlending, g_dbg_scale);
457 end;
458 end;
459 end;
460 end;
462 procedure TPanel.DrawShadowVolume(lightX: Integer; lightY: Integer; radius: Integer);
463 procedure extrude (x: Integer; y: Integer);
464 begin
465 glVertex2i(x+(x-lightX)*500, y+(y-lightY)*500);
466 //e_WriteLog(Format(' : (%d,%d)', [x+(x-lightX)*300, y+(y-lightY)*300]), MSG_WARNING);
467 end;
469 procedure drawLine (x0: Integer; y0: Integer; x1: Integer; y1: Integer);
470 begin
471 // does this side facing the light?
472 if ((x1-x0)*(lightY-y0)-(lightX-x0)*(y1-y0) >= 0) then exit;
473 //e_WriteLog(Format('lightpan: (%d,%d)-(%d,%d)', [x0, y0, x1, y1]), MSG_WARNING);
474 // this edge is facing the light, extrude and draw it
475 glVertex2i(x0, y0);
476 glVertex2i(x1, y1);
477 extrude(x1, y1);
478 extrude(x0, y0);
479 end;
481 begin
482 if radius < 4 then exit;
483 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
484 begin
485 if not FTextureIDs[FCurTexture].Anim then
486 begin
487 case FTextureIDs[FCurTexture].Tex of
488 LongWord(TEXTURE_SPECIAL_WATER): exit;
489 LongWord(TEXTURE_SPECIAL_ACID1): exit;
490 LongWord(TEXTURE_SPECIAL_ACID2): exit;
491 LongWord(TEXTURE_NONE): exit;
492 end;
493 end;
494 if (X+Width < lightX-radius) then exit;
495 if (Y+Height < lightY-radius) then exit;
496 if (X > lightX+radius) then exit;
497 if (Y > lightY+radius) then exit;
498 //e_DrawFill(FTextureIDs[FCurTexture].Tex, X, Y, Width div FTextureWidth, Height div FTextureHeight, FAlpha, True, FBlending);
500 glBegin(GL_QUADS);
501 drawLine(x, y, x+width, y); // top
502 drawLine(x+width, y, x+width, y+height); // right
503 drawLine(x+width, y+height, x, y+height); // bottom
504 drawLine(x, y+height, x, y); // left
505 glEnd();
506 end;
507 end;
510 procedure TPanel.positionChanged (); inline;
511 var
512 px, py, pw, ph: Integer;
513 begin
514 if (proxyId >= 0) then
515 begin
516 mapGrid.getBodyDims(proxyId, px, py, pw, ph);
517 if (px <> x) or (py <> y) or (pw <> Width) or (ph <> Height) then
518 begin
520 e_LogWritefln('panel moved: arridx=%s; guid=%s; proxyid=%s; old:(%s,%s)-(%sx%s); new:(%s,%s)-(%sx%s)',
521 [arrIdx, mGUID, proxyId, px, py, pw, ph, x, y, width, height]);
523 g_Mark(px, py, pw, ph, MARK_WALL, false);
524 if (Width < 1) or (Height < 1) then
525 begin
526 mapGrid.proxyEnabled[proxyId] := false;
527 end
528 else
529 begin
530 mapGrid.proxyEnabled[proxyId] := Enabled;
531 if (pw <> Width) or (ph <> Height) then
532 begin
533 //writeln('panel resize!');
534 mapGrid.moveResizeBody(proxyId, X, Y, Width, Height)
535 end
536 else
537 begin
538 mapGrid.moveBody(proxyId, X, Y);
539 end;
540 g_Mark(X, Y, Width, Height, MARK_WALL);
541 end;
542 end;
543 end;
544 end;
547 var
548 monCheckList: array of TMonster = nil;
549 monCheckListUsed: Integer = 0;
551 procedure TPanel.Update();
552 var
553 ox, oy: Integer;
554 nx, ny, nw, nh: Integer;
555 ex, ey, nex, ney: Integer;
556 mpw, mph: Integer;
558 // return `true` if we should move by dx,dy
559 function tryMPlatMove (px, py, pw, ph: Integer; out dx, dy: Integer; out squash: Boolean; ontop: PBoolean=nil): Boolean;
560 var
561 u0: Single;
562 tex, tey: Integer;
563 pdx, pdy: Integer;
564 trtag: Integer;
565 szdx, szdy: Integer;
566 begin
567 squash := false;
568 tex := px;
569 tey := py;
570 pdx := mMovingSpeed.X;
571 pdy := mMovingSpeed.Y;
572 // standing on the platform?
573 if (py+ph = oy) then
574 begin
575 if (ontop <> nil) then ontop^ := true;
576 // yes, move with it; but skip steps (no need to process size change here, 'cause platform top cannot be changed with it)
577 mapGrid.traceBox(tex, tey, px, py, pw, ph, pdx, pdy, nil, (GridTagWall or GridTagDoor));
578 end
579 else
580 begin
581 if (ontop <> nil) then ontop^ := false;
582 // not standing on the platform: trace platform to see if it hits the entity
583 // first, process size change (as we cannot sweeptest both move and size change)
584 // but we don't have to check for pushing if the panel is shrinking
585 szdx := nw-mpw;
586 szdy := nh-mph;
587 if (szdx > 0) or (szdy > 0) then
588 begin
589 // ignore shrinking dimension
590 if (szdx < 0) then szdx := 0;
591 if (szdy < 0) then szdy := 0;
592 // move platform by szd* back, and check for szd* movement
593 if sweepAABB(ox-szdx, oy-szdy, nw, nh, szdx, szdy, px, py, pw, ph, @u0) then
594 begin
595 // yes, platform hits the entity, push the entity in the resizing direction
596 u0 := 1.0-u0; // how much path left?
597 szdx := trunc(szdx*u0);
598 szdy := trunc(szdy*u0);
599 if (szdx <> 0) or (szdy <> 0) then
600 begin
601 // has some path to go, trace the entity
602 trtag := (GridTagWall or GridTagDoor);
603 // if we're moving down, consider steps too
604 if (szdy > 0) then trtag := trtag or GridTagStep;
605 mapGrid.traceBox(tex, tey, px, py, pw, ph, szdx, szdy, nil, trtag);
606 end;
607 end;
608 end;
609 // second, process platform movement, using te* as entity starting point
610 if sweepAABB(ox, oy, nw, nh, pdx, pdy, tex, tey, pw, ph, @u0) then
611 begin
612 //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]);
613 // yes, platform hits the entity, push the entity in the direction of the platform
614 u0 := 1.0-u0; // how much path left?
615 pdx := trunc(pdx*u0);
616 pdy := trunc(pdy*u0);
617 //e_LogWritefln(' platsweep; uleft=%s; pd=(%s,%s)', [u0, pdx, pdy]);
618 if (pdx <> 0) or (pdy <> 0) then
619 begin
620 // has some path to go, trace the entity
621 trtag := (GridTagWall or GridTagDoor);
622 // if we're moving down, consider steps too
623 if (pdy > 0) then trtag := trtag or GridTagStep;
624 mapGrid.traceBox(tex, tey, px, py, pw, ph, pdx, pdy, nil, trtag);
625 end;
626 end;
627 end;
628 // done with entity movement, new coords are in te*
629 dx := tex-px;
630 dy := tey-py;
631 result := (dx <> 0) or (dy <> 0);
632 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
633 begin
634 // check for squashing; as entity cannot be pushed into a wall, check only collision with the platform itself
635 squash := g_Collide(tex, tey, pw, ph, nx, ny, nw, nh); // squash, if still in platform
636 end;
637 end;
639 function monCollect (mon: TMonster): Boolean;
640 begin
641 result := false; // don't stop
642 if (monCheckListUsed >= Length(monCheckList)) then SetLength(monCheckList, monCheckListUsed+128);
643 monCheckList[monCheckListUsed] := mon;
644 Inc(monCheckListUsed);
645 end;
647 var
648 cx0, cy0, cx1, cy1, cw, ch: Integer;
649 f: Integer;
650 px, py, pw, ph, pdx, pdy: Integer;
651 squash: Boolean;
652 plr: TPlayer;
653 gib: PGib;
654 cor: TCorpse;
655 mon: TMonster;
656 mpfrid: LongWord;
657 ontop: Boolean;
658 actMoveTrig: Boolean;
659 actSizeTrig: Boolean;
660 begin
661 if (not Enabled) or (Width < 1) or (Height < 1) then exit;
663 if (FCurTexture >= 0) and
664 (FTextureIDs[FCurTexture].Anim) and
665 (FTextureIDs[FCurTexture].AnTex <> nil) and
666 (FAlpha < 255) then
667 begin
668 FTextureIDs[FCurTexture].AnTex.Update();
669 FCurFrame := FTextureIDs[FCurTexture].AnTex.CurrentFrame;
670 FCurFrameCount := FTextureIDs[FCurTexture].AnTex.CurrentCounter;
671 end;
673 if not g_dbgpan_mplat_active then exit;
675 if not mMovingActive then exit;
676 if mMovingSpeed.isZero and mSizeSpeed.isZero then exit;
678 //TODO: write wall size change processing
680 // moving platform?
681 begin
682 (*
683 * collect all monsters and players (aka entities) along the possible platform path
684 * if entity is standing on a platform:
685 * try to move it along the platform path, checking wall collisions
686 * if entity is NOT standing on a platform, but hit with sweeped platform aabb:
687 * try to push entity
688 * if we can't push entity all the way, squash it
689 *)
690 ox := X;
691 oy := Y;
692 mpw := Width;
693 mph := Height;
695 nw := mpw+mSizeSpeed.w;
696 nh := mph+mSizeSpeed.h;
697 nx := ox+mMovingSpeed.X;
698 ny := oy+mMovingSpeed.Y;
700 // if pannel disappeared, we don't have to do anything
701 if (nw > 0) and (nh > 0) then
702 begin
703 // old rect
704 ex := ox+mpw-1;
705 ey := ox+mph-1;
706 // new rect
707 nex := nx+nw-1;
708 ney := ny+nh-1;
709 // full rect
710 cx0 := nmin(ox, nx);
711 cy0 := nmin(oy, ny);
712 cx1 := nmax(ex, nex);
713 cy1 := nmax(ey, ney);
714 // extrude
715 cx0 -= 1;
716 cy0 -= 1;
717 cx1 += 1;
718 cy1 += 1;
719 cw := cx1-cx0+1;
720 ch := cy1-cy0+1;
722 // process "obstacle" panels
723 if ((tag and GridTagObstacle) <> 0) then
724 begin
725 // temporarily turn off this panel, so it won't interfere with collision checks
726 mapGrid.proxyEnabled[proxyId] := false;
728 // process players
729 for f := 0 to High(gPlayers) do
730 begin
731 plr := gPlayers[f];
732 if (plr = nil) or (not plr.alive) then continue;
733 plr.getMapBox(px, py, pw, ph);
734 if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue;
735 if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash) then
736 begin
737 // set new position
738 plr.moveBy(pdx, pdy); // this will call `positionChanged()` for us
739 end;
740 // squash player, if necessary
741 if not g_Game_IsClient and squash then plr.Damage(15000, 0, 0, 0, HIT_TRAP);
742 end;
744 // process gibs
745 for f := 0 to High(gGibs) do
746 begin
747 gib := @gGibs[f];
748 if not gib.alive then continue;
749 gib.getMapBox(px, py, pw, ph);
750 if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue;
751 if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash, @ontop) then
752 begin
753 // set new position
754 gib.moveBy(pdx, pdy); // this will call `positionChanged()` for us
755 end;
756 end;
758 // move and push corpses
759 for f := 0 to High(gCorpses) do
760 begin
761 cor := gCorpses[f];
762 if (cor = nil) then continue;
763 cor.getMapBox(px, py, pw, ph);
764 if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue;
765 if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash, @ontop) then
766 begin
767 // set new position
768 cor.moveBy(pdx, pdy); // this will call `positionChanged()` for us
769 end;
770 end;
772 // collect monsters
773 monCheckListUsed := 0;
774 g_Mons_ForEachAt(cx0, cy0, cw, ch, monCollect);
776 // process collected monsters
777 if (monCheckListUsed > 0) then
778 begin
779 mpfrid := g_Mons_getNewMPlatFrameId();
780 for f := 0 to monCheckListUsed do
781 begin
782 mon := monCheckList[f];
783 if (mon = nil) or (not mon.alive) or (mon.mplatCheckFrameId = mpfrid) then continue;
784 mon.mplatCheckFrameId := mpfrid;
785 mon.getMapBox(px, py, pw, ph);
786 //if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue;
787 if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash) then
788 begin
789 // set new position
790 mon.moveBy(pdx, pdy); // this will call `positionChanged()` for us
791 end;
792 // squash player, if necessary
793 if not g_Game_IsClient and squash then mon.Damage(15000, 0, 0, 0, HIT_TRAP);
794 end;
795 end;
797 // restore panel state
798 mapGrid.proxyEnabled[proxyId] := true;
799 end;
800 end;
802 // move panel
803 X := nx;
804 Y := ny;
805 FWidth := nw;
806 FHeight := nh;
807 positionChanged();
809 actMoveTrig := false;
810 actSizeTrig := false;
812 // check "size stop"
813 if not mSizeSpeed.isZero and (nw = mSizeEnd.w) and (nh = mSizeEnd.h) then
814 begin
815 mSizeSpeed.w := 0;
816 mSizeSpeed.h := 0;
817 actSizeTrig := true;
818 if (nw < 1) or (nh < 1) then mMovingActive := false; //HACK!
819 //e_LogWritefln('FUUUUUUUUUUUUUU', []);
820 end;
822 // reverse moving direction, if necessary
823 if ((mMovingSpeed.X < 0) and (nx <= mMovingStart.X)) or ((mMovingSpeed.X > 0) and (nx >= mMovingEnd.X)) then
824 begin
825 if mMoveOnce then mMovingActive := false else mMovingSpeed.X := -mMovingSpeed.X;
826 actMoveTrig := true;
827 end;
829 if ((mMovingSpeed.Y < 0) and (ny <= mMovingStart.Y)) or ((mMovingSpeed.Y > 0) and (ny >= mMovingEnd.Y)) then
830 begin
831 if mMoveOnce then mMovingActive := false else mMovingSpeed.Y := -mMovingSpeed.Y;
832 actMoveTrig := true;
833 end;
836 if actMoveTrig then g_Triggers_Press(mEndPosTrig, ACTIVATE_CUSTOM);
837 if actSizeTrig then g_Triggers_Press(mEndSizeTrig, ACTIVATE_CUSTOM);
838 end;
839 end;
842 procedure TPanel.SetFrame(Frame: Integer; Count: Byte);
844 function ClampInt(X, A, B: Integer): Integer;
845 begin
846 Result := X;
847 if X < A then Result := A else if X > B then Result := B;
848 end;
850 begin
851 if Enabled and (FCurTexture >= 0) and
852 (FTextureIDs[FCurTexture].Anim) and
853 (FTextureIDs[FCurTexture].AnTex <> nil) and
854 (Width > 0) and (Height > 0) and (FAlpha < 255) then
855 begin
856 FCurFrame := ClampInt(Frame, 0, FTextureIDs[FCurTexture].AnTex.TotalFrames);
857 FCurFrameCount := Count;
858 FTextureIDs[FCurTexture].AnTex.CurrentFrame := FCurFrame;
859 FTextureIDs[FCurTexture].AnTex.CurrentCounter := FCurFrameCount;
860 end;
861 end;
863 procedure TPanel.NextTexture(AnimLoop: Byte = 0);
864 begin
865 Assert(FCurTexture >= -1, 'FCurTexture < -1');
867 // Íåò òåêñòóð:
868 if Length(FTextureIDs) = 0 then
869 FCurTexture := -1
870 else
871 // Òîëüêî îäíà òåêñòóðà:
872 if Length(FTextureIDs) = 1 then
873 begin
874 if FCurTexture = 0 then
875 FCurTexture := -1
876 else
877 FCurTexture := 0;
878 end
879 else
880 // Áîëüøå îäíîé òåêñòóðû:
881 begin
882 // Ñëåäóþùàÿ:
883 Inc(FCurTexture);
884 // Ñëåäóþùåé íåò - âîçâðàò ê íà÷àëó:
885 if FCurTexture >= Length(FTextureIDs) then
886 FCurTexture := 0;
887 end;
889 // Ïåðåêëþ÷èëèñü íà âèäèìóþ àíèì. òåêñòóðó:
890 if (FCurTexture >= 0) and FTextureIDs[FCurTexture].Anim then
891 begin
892 if (FTextureIDs[FCurTexture].AnTex = nil) then
893 begin
894 g_FatalError(_lc[I_GAME_ERROR_SWITCH_TEXTURE]);
895 Exit;
896 end;
898 if AnimLoop = 1 then
899 FTextureIDs[FCurTexture].AnTex.Loop := True
900 else
901 if AnimLoop = 2 then
902 FTextureIDs[FCurTexture].AnTex.Loop := False;
904 FTextureIDs[FCurTexture].AnTex.Reset();
905 end;
907 LastAnimLoop := AnimLoop;
908 end;
910 procedure TPanel.SetTexture(ID: Integer; AnimLoop: Byte = 0);
911 begin
912 // Íåò òåêñòóð:
913 if Length(FTextureIDs) = 0 then
914 FCurTexture := -1
915 else
916 // Òîëüêî îäíà òåêñòóðà:
917 if Length(FTextureIDs) = 1 then
918 begin
919 if (ID = 0) or (ID = -1) then
920 FCurTexture := ID;
921 end
922 else
923 // Áîëüøå îäíîé òåêñòóðû:
924 begin
925 if (ID >= -1) and (ID <= High(FTextureIDs)) then
926 FCurTexture := ID;
927 end;
929 // Ïåðåêëþ÷èëèñü íà âèäèìóþ àíèì. òåêñòóðó:
930 if (FCurTexture >= 0) and FTextureIDs[FCurTexture].Anim then
931 begin
932 if (FTextureIDs[FCurTexture].AnTex = nil) then
933 begin
934 g_FatalError(_lc[I_GAME_ERROR_SWITCH_TEXTURE]);
935 Exit;
936 end;
938 if AnimLoop = 1 then
939 FTextureIDs[FCurTexture].AnTex.Loop := True
940 else
941 if AnimLoop = 2 then
942 FTextureIDs[FCurTexture].AnTex.Loop := False;
944 FTextureIDs[FCurTexture].AnTex.Reset();
945 end;
947 LastAnimLoop := AnimLoop;
948 end;
950 function TPanel.GetTextureID(): DWORD;
951 begin
952 Result := LongWord(TEXTURE_NONE);
954 if (FCurTexture >= 0) then
955 begin
956 if FTextureIDs[FCurTexture].Anim then
957 Result := FTextureIDs[FCurTexture].AnTex.FramesID
958 else
959 Result := FTextureIDs[FCurTexture].Tex;
960 end;
961 end;
963 function TPanel.GetTextureCount(): Integer;
964 begin
965 Result := Length(FTextureIDs);
966 if Enabled and (FCurTexture >= 0) then
967 if (FTextureIDs[FCurTexture].Anim) and
968 (FTextureIDs[FCurTexture].AnTex <> nil) and
969 (Width > 0) and (Height > 0) and (FAlpha < 255) then
970 Result := Result + 100;
971 end;
973 procedure TPanel.SaveState(Var Mem: TBinMemoryWriter);
974 var
975 sig: DWORD;
976 anim: Boolean;
977 begin
978 if (Mem = nil) then exit;
979 //if not SaveIt then exit;
981 // Ñèãíàòóðà ïàíåëè:
982 sig := PANEL_SIGNATURE; // 'PANL'
983 Mem.WriteDWORD(sig);
984 // Îòêðûòà/çàêðûòà, åñëè äâåðü:
985 Mem.WriteBoolean(FEnabled);
986 // Íàïðàâëåíèå ëèôòà, åñëè ëèôò:
987 Mem.WriteByte(FLiftType);
988 // Íîìåð òåêóùåé òåêñòóðû:
989 Mem.WriteInt(FCurTexture);
990 // Êîîðäû
991 Mem.WriteInt(FX);
992 Mem.WriteInt(FY);
993 // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà:
994 if (FCurTexture >= 0) and (FTextureIDs[FCurTexture].Anim) then
995 begin
996 Assert(FTextureIDs[FCurTexture].AnTex <> nil,
997 'TPanel.SaveState: No animation object');
998 anim := True;
999 end
1000 else
1001 anim := False;
1002 Mem.WriteBoolean(anim);
1003 // Åñëè äà - ñîõðàíÿåì àíèìàöèþ:
1004 if anim then
1005 FTextureIDs[FCurTexture].AnTex.SaveState(Mem);
1006 // moving platform state
1007 Mem.WriteInt(mMovingSpeed.X);
1008 Mem.WriteInt(mMovingSpeed.Y);
1009 Mem.WriteInt(mMovingStart.X);
1010 Mem.WriteInt(mMovingStart.Y);
1011 Mem.WriteInt(mMovingEnd.X);
1012 Mem.WriteInt(mMovingEnd.Y);
1013 Mem.WriteBoolean(mMovingActive);
1014 end;
1016 procedure TPanel.LoadState(var Mem: TBinMemoryReader);
1017 var
1018 sig: DWORD;
1019 anim: Boolean;
1020 //ox, oy: Integer;
1021 begin
1022 if (Mem = nil) then exit;
1023 //if not SaveIt then exit;
1025 // Ñèãíàòóðà ïàíåëè:
1026 Mem.ReadDWORD(sig);
1027 if sig <> PANEL_SIGNATURE then // 'PANL'
1028 begin
1029 raise EBinSizeError.Create('TPanel.LoadState: Wrong Panel Signature');
1030 end;
1031 // Îòêðûòà/çàêðûòà, åñëè äâåðü:
1032 Mem.ReadBoolean(FEnabled);
1033 // Íàïðàâëåíèå ëèôòà, åñëè ëèôò:
1034 Mem.ReadByte(FLiftType);
1035 // Íîìåð òåêóùåé òåêñòóðû:
1036 Mem.ReadInt(FCurTexture);
1037 // Êîîðäû
1038 //ox := FX;
1039 //oy := FY;
1040 Mem.ReadInt(FX);
1041 Mem.ReadInt(FY);
1042 //e_LogWritefln('panel %s(%s): old=(%s,%s); new=(%s,%s); delta=(%s,%s)', [arrIdx, proxyId, ox, oy, FX, FY, FX-ox, FY-oy]);
1043 // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà:
1044 Mem.ReadBoolean(anim);
1045 // Åñëè äà - çàãðóæàåì àíèìàöèþ:
1046 if anim then
1047 begin
1048 Assert((FCurTexture >= 0) and
1049 (FTextureIDs[FCurTexture].Anim) and
1050 (FTextureIDs[FCurTexture].AnTex <> nil),
1051 'TPanel.LoadState: No animation object');
1052 FTextureIDs[FCurTexture].AnTex.LoadState(Mem);
1053 end;
1054 // moving platform state
1055 Mem.ReadInt(mMovingSpeed.X);
1056 Mem.ReadInt(mMovingSpeed.Y);
1057 Mem.ReadInt(mMovingStart.X);
1058 Mem.ReadInt(mMovingStart.Y);
1059 Mem.ReadInt(mMovingEnd.X);
1060 Mem.ReadInt(mMovingEnd.Y);
1061 Mem.ReadBoolean(mMovingActive);
1063 positionChanged();
1064 //mapGrid.proxyEnabled[proxyId] := FEnabled; // done in g_map.pas
1065 end;
1067 end.