DEADSOFTWARE

some new code for moving platforms: only for players yet
[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;
53 private
54 function getx1 (): Integer; inline;
55 function gety1 (): Integer; inline;
56 function getvisvalid (): Boolean; inline;
58 function getMovingSpeedX (): Integer; inline;
59 procedure setMovingSpeedX (v: Integer); inline;
60 function getMovingSpeedY (): Integer; inline;
61 procedure setMovingSpeedY (v: Integer); inline;
63 function getMovingStartX (): Integer; inline;
64 procedure setMovingStartX (v: Integer); inline;
65 function getMovingStartY (): Integer; inline;
66 procedure setMovingStartY (v: Integer); inline;
68 function getMovingEndX (): Integer; inline;
69 procedure setMovingEndX (v: Integer); inline;
70 function getMovingEndY (): Integer; inline;
71 procedure setMovingEndY (v: Integer); inline;
73 public
74 FCurTexture: Integer; // Íîìåð òåêóùåé òåêñòóðû
75 FCurFrame: Integer;
76 FCurFrameCount: Byte;
77 FX, FY: Integer;
78 FWidth, FHeight: Word;
79 FPanelType: Word;
80 FSaveIt: Boolean; // Ñîõðàíÿòü ïðè SaveState?
81 FEnabled: Boolean;
82 FDoor: Boolean;
83 FMoved: Boolean;
84 FLiftType: Byte;
85 FLastAnimLoop: Byte;
86 // sorry, there fields are public to allow setting 'em in g_map; this should be fixed later
87 // for now, PLEASE, don't modify 'em, or all hell will break loose
88 arrIdx: Integer; // index in one of internal arrays; sorry
89 tag: Integer; // used in coldets and such; sorry; see g_map.GridTagXXX
90 proxyId: Integer; // proxy id in map grid (DO NOT USE!)
91 mapId: AnsiString; // taken directly from map file; dunno why it is here
93 constructor Create(PanelRec: TDynRecord;
94 AddTextures: TAddTextureArray;
95 CurTex: Integer;
96 var Textures: TLevelTextureArray; aguid: Integer);
97 destructor Destroy(); override;
99 procedure Draw();
100 procedure DrawShadowVolume(lightX: Integer; lightY: Integer; radius: Integer);
101 procedure Update();
102 procedure SetFrame(Frame: Integer; Count: Byte);
103 procedure NextTexture(AnimLoop: Byte = 0);
104 procedure SetTexture(ID: Integer; AnimLoop: Byte = 0);
105 function GetTextureID(): Cardinal;
106 function GetTextureCount(): Integer;
108 procedure SaveState(var Mem: TBinMemoryWriter);
109 procedure LoadState(var Mem: TBinMemoryReader);
111 procedure positionChanged (); inline;
113 function getIsGBack (): Boolean; inline; // gRenderBackgrounds
114 function getIsGStep (): Boolean; inline; // gSteps
115 function getIsGWall (): Boolean; inline; // gWalls
116 function getIsGAcid1 (): Boolean; inline; // gAcid1
117 function getIsGAcid2 (): Boolean; inline; // gAcid2
118 function getIsGWater (): Boolean; inline; // gWater
119 function getIsGFore (): Boolean; inline; // gRenderForegrounds
120 function getIsGLift (): Boolean; inline; // gLifts
121 function getIsGBlockMon (): Boolean; inline; // gBlockMon
123 public
124 property visvalid: Boolean read getvisvalid; // panel is "visvalid" when it's width and height are positive
126 published
127 property guid: Integer read mGUID; // will be assigned in "g_map.pas"
128 property x0: Integer read FX;
129 property y0: Integer read FY;
130 property x1: Integer read getx1; // inclusive!
131 property y1: Integer read gety1; // inclusive!
132 property x: Integer read FX write FX;
133 property y: Integer read FY write FY;
134 property width: Word read FWidth write FWidth;
135 property height: Word read FHeight write FHeight;
136 property panelType: Word read FPanelType write FPanelType;
137 property saveIt: Boolean read FSaveIt write FSaveIt; // Ñîõðàíÿòü ïðè SaveState?
138 property enabled: Boolean read FEnabled write FEnabled; // Ñîõðàíÿòü ïðè SaveState?
139 property door: Boolean read FDoor write FDoor; // Ñîõðàíÿòü ïðè SaveState?
140 property moved: Boolean read FMoved write FMoved; // Ñîõðàíÿòü ïðè SaveState?
141 property liftType: Byte read FLiftType write FLiftType; // Ñîõðàíÿòü ïðè SaveState?
142 property lastAnimLoop: Byte read FLastAnimLoop write FLastAnimLoop; // Ñîõðàíÿòü ïðè SaveState?
144 property movingSpeedX: Integer read getMovingSpeedX write setMovingSpeedX;
145 property movingSpeedY: Integer read getMovingSpeedY write setMovingSpeedY;
146 property movingStartX: Integer read getMovingStartX write setMovingStartX;
147 property movingStartY: Integer read getMovingStartY write setMovingStartY;
148 property movingEndX: Integer read getMovingEndX write setMovingEndX;
149 property movingEndY: Integer read getMovingEndY write setMovingEndY;
150 property movingActive: Boolean read mMovingActive write mMovingActive;
152 property isGBack: Boolean read getIsGBack;
153 property isGStep: Boolean read getIsGStep;
154 property isGWall: Boolean read getIsGWall;
155 property isGAcid1: Boolean read getIsGAcid1;
156 property isGAcid2: Boolean read getIsGAcid2;
157 property isGWater: Boolean read getIsGWater;
158 property isGFore: Boolean read getIsGFore;
159 property isGLift: Boolean read getIsGLift;
160 property isGBlockMon: Boolean read getIsGBlockMon;
162 public
163 property movingSpeed: TDFPoint read mMovingSpeed write mMovingSpeed;
164 property movingStart: TDFPoint read mMovingStart write mMovingStart;
165 property movingEnd: TDFPoint read mMovingEnd write mMovingEnd;
166 end;
168 TPanelArray = Array of TPanel;
170 var
171 g_dbgpan_mplat_active: Boolean = {$IF DEFINED(D2F_DEBUG)}false{$ELSE}true{$ENDIF};
172 g_dbgpan_mplat_step: Boolean = false; // one step, and stop
175 implementation
177 uses
178 SysUtils, g_basic, g_map, g_game, g_gfx, e_graphics, g_weapons,
179 g_console, g_language, g_monsters, g_player, g_grid, e_log, GL, utils;
181 const
182 PANEL_SIGNATURE = $4C4E4150; // 'PANL'
184 { T P a n e l : }
186 constructor TPanel.Create(PanelRec: TDynRecord;
187 AddTextures: TAddTextureArray;
188 CurTex: Integer;
189 var Textures: TLevelTextureArray; aguid: Integer);
190 var
191 i: Integer;
192 begin
193 X := PanelRec.X;
194 Y := PanelRec.Y;
195 Width := PanelRec.Width;
196 Height := PanelRec.Height;
197 FAlpha := 0;
198 FBlending := False;
199 FCurFrame := 0;
200 FCurFrameCount := 0;
201 LastAnimLoop := 0;
202 Moved := False;
204 mapId := PanelRec.id;
205 mGUID := aguid;
207 mMovingSpeed := PanelRec.moveSpeed;
208 mMovingStart := PanelRec.moveStart;
209 mMovingEnd := PanelRec.moveEnd;
210 mMovingActive := PanelRec['move_active'].varvalue;
212 // Òèï ïàíåëè:
213 PanelType := PanelRec.PanelType;
214 Enabled := True;
215 Door := False;
216 LiftType := 0;
217 SaveIt := False;
219 case PanelType of
220 PANEL_OPENDOOR:
221 begin
222 Enabled := False;
223 Door := True;
224 SaveIt := True;
225 end;
226 PANEL_CLOSEDOOR:
227 begin
228 Door := True;
229 SaveIt := True;
230 end;
231 PANEL_LIFTUP:
232 SaveIt := True;
233 PANEL_LIFTDOWN:
234 begin
235 LiftType := 1;
236 SaveIt := True;
237 end;
238 PANEL_LIFTLEFT:
239 begin
240 LiftType := 2;
241 SaveIt := True;
242 end;
243 PANEL_LIFTRIGHT:
244 begin
245 LiftType := 3;
246 SaveIt := True;
247 end;
248 end;
250 // Íåâèäèìàÿ:
251 if ByteBool(PanelRec.Flags and PANEL_FLAG_HIDE) then
252 begin
253 SetLength(FTextureIDs, 0);
254 FCurTexture := -1;
255 Exit;
256 end;
257 // Ïàíåëè, íå èñïîëüçóþùèå òåêñòóðû:
258 if ByteBool(PanelType and
259 (PANEL_LIFTUP or
260 PANEL_LIFTDOWN or
261 PANEL_LIFTLEFT or
262 PANEL_LIFTRIGHT or
263 PANEL_BLOCKMON)) then
264 begin
265 SetLength(FTextureIDs, 0);
266 FCurTexture := -1;
267 Exit;
268 end;
270 // Åñëè ýòî æèäêîñòü áåç òåêñòóðû - ñïåöòåêñòóðó:
271 if WordBool(PanelType and (PANEL_WATER or PANEL_ACID1 or PANEL_ACID2)) and
272 (not ByteBool(PanelRec.Flags and PANEL_FLAG_WATERTEXTURES)) then
273 begin
274 SetLength(FTextureIDs, 1);
275 FTextureIDs[0].Anim := False;
277 case PanelRec.PanelType of
278 PANEL_WATER:
279 FTextureIDs[0].Tex := LongWord(TEXTURE_SPECIAL_WATER);
280 PANEL_ACID1:
281 FTextureIDs[0].Tex := LongWord(TEXTURE_SPECIAL_ACID1);
282 PANEL_ACID2:
283 FTextureIDs[0].Tex := LongWord(TEXTURE_SPECIAL_ACID2);
284 end;
286 FCurTexture := 0;
287 Exit;
288 end;
290 SetLength(FTextureIDs, Length(AddTextures));
292 if CurTex < 0 then
293 FCurTexture := -1
294 else
295 if CurTex >= Length(FTextureIDs) then
296 FCurTexture := Length(FTextureIDs) - 1
297 else
298 FCurTexture := CurTex;
300 for i := 0 to Length(FTextureIDs)-1 do
301 begin
302 FTextureIDs[i].Anim := AddTextures[i].Anim;
303 if FTextureIDs[i].Anim then
304 begin // Àíèìèðîâàííàÿ òåêñòóðà
305 FTextureIDs[i].AnTex :=
306 TAnimation.Create(Textures[AddTextures[i].Texture].FramesID,
307 True, Textures[AddTextures[i].Texture].Speed);
308 FTextureIDs[i].AnTex.Blending := ByteBool(PanelRec.Flags and PANEL_FLAG_BLENDING);
309 FTextureIDs[i].AnTex.Alpha := PanelRec.Alpha;
310 SaveIt := True;
311 end
312 else
313 begin // Îáû÷íàÿ òåêñòóðà
314 FTextureIDs[i].Tex := Textures[AddTextures[i].Texture].TextureID;
315 end;
316 end;
318 // Òåêñòóð íåñêîëüêî - íóæíî ñîõðàíÿòü òåêóùóþ:
319 if Length(FTextureIDs) > 1 then
320 SaveIt := True;
322 // Åñëè íå ñïåöòåêñòóðà, òî çàäàåì ðàçìåðû:
323 if PanelRec.TextureNum > High(Textures) then
324 begin
325 e_WriteLog(Format('WTF?! PanelRec.TextureNum is out of limits! (%d : %d)', [PanelRec.TextureNum, High(Textures)]), MSG_FATALERROR);
326 FTextureWidth := 2;
327 FTextureHeight := 2;
328 FAlpha := 0;
329 FBlending := ByteBool(0);
330 end
331 else if not g_Map_IsSpecialTexture(Textures[PanelRec.TextureNum].TextureName) then
332 begin
333 FTextureWidth := Textures[PanelRec.TextureNum].Width;
334 FTextureHeight := Textures[PanelRec.TextureNum].Height;
335 FAlpha := PanelRec.Alpha;
336 FBlending := ByteBool(PanelRec.Flags and PANEL_FLAG_BLENDING);
337 end;
338 end;
340 destructor TPanel.Destroy();
341 var
342 i: Integer;
343 begin
344 for i := 0 to High(FTextureIDs) do
345 if FTextureIDs[i].Anim then
346 FTextureIDs[i].AnTex.Free();
347 SetLength(FTextureIDs, 0);
349 Inherited;
350 end;
352 function TPanel.getx1 (): Integer; inline; begin result := X+Width-1; end;
353 function TPanel.gety1 (): Integer; inline; begin result := Y+Height-1; end;
354 function TPanel.getvisvalid (): Boolean; inline; begin result := (Width > 0) and (Height > 0); end;
356 function TPanel.getMovingSpeedX (): Integer; inline; begin result := mMovingSpeed.X; end;
357 procedure TPanel.setMovingSpeedX (v: Integer); inline; begin mMovingSpeed.X := v; end;
358 function TPanel.getMovingSpeedY (): Integer; inline; begin result := mMovingSpeed.Y; end;
359 procedure TPanel.setMovingSpeedY (v: Integer); inline; begin mMovingSpeed.Y := v; end;
361 function TPanel.getMovingStartX (): Integer; inline; begin result := mMovingStart.X; end;
362 procedure TPanel.setMovingStartX (v: Integer); inline; begin mMovingStart.X := v; end;
363 function TPanel.getMovingStartY (): Integer; inline; begin result := mMovingStart.Y; end;
364 procedure TPanel.setMovingStartY (v: Integer); inline; begin mMovingStart.Y := v; end;
366 function TPanel.getMovingEndX (): Integer; inline; begin result := mMovingEnd.X; end;
367 procedure TPanel.setMovingEndX (v: Integer); inline; begin mMovingEnd.X := v; end;
368 function TPanel.getMovingEndY (): Integer; inline; begin result := mMovingEnd.Y; end;
369 procedure TPanel.setMovingEndY (v: Integer); inline; begin mMovingEnd.Y := v; end;
371 function TPanel.getIsGBack (): Boolean; inline; begin result := ((tag and GridTagBack) <> 0); end;
372 function TPanel.getIsGStep (): Boolean; inline; begin result := ((tag and GridTagStep) <> 0); end;
373 function TPanel.getIsGWall (): Boolean; inline; begin result := ((tag and (GridTagWall or GridTagDoor)) <> 0); end;
374 function TPanel.getIsGAcid1 (): Boolean; inline; begin result := ((tag and GridTagAcid1) <> 0); end;
375 function TPanel.getIsGAcid2 (): Boolean; inline; begin result := ((tag and GridTagAcid2) <> 0); end;
376 function TPanel.getIsGWater (): Boolean; inline; begin result := ((tag and GridTagWater) <> 0); end;
377 function TPanel.getIsGFore (): Boolean; inline; begin result := ((tag and GridTagFore) <> 0); end;
378 function TPanel.getIsGLift (): Boolean; inline; begin result := ((tag and GridTagLift) <> 0); end;
379 function TPanel.getIsGBlockMon (): Boolean; inline; begin result := ((tag and GridTagBlockMon) <> 0); end;
381 procedure TPanel.Draw();
382 var
383 xx, yy: Integer;
384 NoTextureID: DWORD;
385 NW, NH: Word;
386 begin
387 if {Enabled and} (FCurTexture >= 0) and
388 (Width > 0) and (Height > 0) and (FAlpha < 255) and
389 ((g_dbg_scale <> 1.0) or g_Collide(X, Y, Width, Height, sX, sY, sWidth, sHeight)) then
390 begin
391 if FTextureIDs[FCurTexture].Anim then
392 begin // Àíèìèðîâàííàÿ òåêñòóðà
393 if FTextureIDs[FCurTexture].AnTex = nil then
394 Exit;
396 for xx := 0 to (Width div FTextureWidth)-1 do
397 for yy := 0 to (Height div FTextureHeight)-1 do
398 FTextureIDs[FCurTexture].AnTex.Draw(
399 X + xx*FTextureWidth,
400 Y + yy*FTextureHeight, M_NONE);
401 end
402 else
403 begin // Îáû÷íàÿ òåêñòóðà
404 case FTextureIDs[FCurTexture].Tex of
405 LongWord(TEXTURE_SPECIAL_WATER):
406 e_DrawFillQuad(X, Y, X+Width-1, Y+Height-1,
407 0, 0, 255, 0, B_FILTER);
408 LongWord(TEXTURE_SPECIAL_ACID1):
409 e_DrawFillQuad(X, Y, X+Width-1, Y+Height-1,
410 0, 128, 0, 0, B_FILTER);
411 LongWord(TEXTURE_SPECIAL_ACID2):
412 e_DrawFillQuad(X, Y, X+Width-1, Y+Height-1,
413 128, 0, 0, 0, B_FILTER);
414 LongWord(TEXTURE_NONE):
415 if g_Texture_Get('NOTEXTURE', NoTextureID) then
416 begin
417 e_GetTextureSize(NoTextureID, @NW, @NH);
418 e_DrawFill(NoTextureID, X, Y, Width div NW, Height div NH,
419 0, False, False);
420 end else
421 begin
422 xx := X + (Width div 2);
423 yy := Y + (Height div 2);
424 e_DrawFillQuad(X, Y, xx, yy,
425 255, 0, 255, 0);
426 e_DrawFillQuad(xx, Y, X+Width-1, yy,
427 255, 255, 0, 0);
428 e_DrawFillQuad(X, yy, xx, Y+Height-1,
429 255, 255, 0, 0);
430 e_DrawFillQuad(xx, yy, X+Width-1, Y+Height-1,
431 255, 0, 255, 0);
432 end;
434 else
435 e_DrawFill(FTextureIDs[FCurTexture].Tex, X, Y,
436 Width div FTextureWidth,
437 Height div FTextureHeight,
438 FAlpha, True, FBlending);
439 end;
440 end;
441 end;
442 end;
444 procedure TPanel.DrawShadowVolume(lightX: Integer; lightY: Integer; radius: Integer);
445 procedure extrude (x: Integer; y: Integer);
446 begin
447 glVertex2i(x+(x-lightX)*500, y+(y-lightY)*500);
448 //e_WriteLog(Format(' : (%d,%d)', [x+(x-lightX)*300, y+(y-lightY)*300]), MSG_WARNING);
449 end;
451 procedure drawLine (x0: Integer; y0: Integer; x1: Integer; y1: Integer);
452 begin
453 // does this side facing the light?
454 if ((x1-x0)*(lightY-y0)-(lightX-x0)*(y1-y0) >= 0) then exit;
455 //e_WriteLog(Format('lightpan: (%d,%d)-(%d,%d)', [x0, y0, x1, y1]), MSG_WARNING);
456 // this edge is facing the light, extrude and draw it
457 glVertex2i(x0, y0);
458 glVertex2i(x1, y1);
459 extrude(x1, y1);
460 extrude(x0, y0);
461 end;
463 begin
464 if radius < 4 then exit;
465 if Enabled and (FCurTexture >= 0) and (Width > 0) and (Height > 0) and (FAlpha < 255) and g_Collide(X, Y, Width, Height, sX, sY, sWidth, sHeight) then
466 begin
467 if not FTextureIDs[FCurTexture].Anim then
468 begin
469 case FTextureIDs[FCurTexture].Tex of
470 LongWord(TEXTURE_SPECIAL_WATER): exit;
471 LongWord(TEXTURE_SPECIAL_ACID1): exit;
472 LongWord(TEXTURE_SPECIAL_ACID2): exit;
473 LongWord(TEXTURE_NONE): exit;
474 end;
475 end;
476 if (X+Width < lightX-radius) then exit;
477 if (Y+Height < lightY-radius) then exit;
478 if (X > lightX+radius) then exit;
479 if (Y > lightY+radius) then exit;
480 //e_DrawFill(FTextureIDs[FCurTexture].Tex, X, Y, Width div FTextureWidth, Height div FTextureHeight, FAlpha, True, FBlending);
482 glBegin(GL_QUADS);
483 drawLine(x, y, x+width, y); // top
484 drawLine(x+width, y, x+width, y+height); // right
485 drawLine(x+width, y+height, x, y+height); // bottom
486 drawLine(x, y+height, x, y); // left
487 glEnd();
488 end;
489 end;
492 procedure TPanel.positionChanged (); inline;
493 var
494 px, py, pw, ph: Integer;
495 begin
496 if (proxyId >= 0) then
497 begin
498 mapGrid.getBodyDims(proxyId, px, py, pw, ph);
499 if (px <> x) or (py <> y) or (pw <> Width) or (ph <> Height) then
500 begin
502 e_LogWritefln('panel moved: arridx=%s; guid=%s; proxyid=%s; old:(%s,%s)-(%sx%s); new:(%s,%s)-(%sx%s)',
503 [arrIdx, mGUID, proxyId, px, py, pw, ph, x, y, width, height]);
505 g_Mark(px, py, pw, ph, MARK_WALL, false);
506 if (pw <> Width) or (ph <> Height) then mapGrid.moveResizeBody(proxyId, X, Y, Width, Height)
507 else mapGrid.moveBody(proxyId, X, Y);
508 g_Mark(X, Y, Width, Height, MARK_WALL);
509 end;
510 end;
511 end;
515 var
516 monMoveList: array of TMonster = nil;
517 monMoveListUsed: Integer = 0;
520 procedure TPanel.Update();
521 var
522 ox, oy: Integer;
523 nx, ny: Integer;
524 ex, ey, nex, ney: Integer;
525 cx0, cy0, cx1, cy1: Integer;
527 // return `true` if we should move by dx,dy
528 function tryMPlatMove (px, py, pw, ph: Integer; out dx, dy: Integer; out squash: Boolean): Boolean;
529 var
530 u0, u1: Single;
531 tex, tey: Integer;
532 pdx, pdy: Integer;
533 begin
534 squash := false;
535 dx := 0;
536 dy := 0;
537 pdx := mMovingSpeed.X;
538 pdy := mMovingSpeed.Y;
539 // standing on the platform?
540 if (py+ph = oy) then
541 begin
542 // yes, move with it
543 mapGrid.traceBox(tex, tey, px, py, pw, ph, pdx, pdy, nil, GridTagObstacle);
544 //e_LogWritefln('entity on the platform; tracing=(%s,%s); endpoint=(%s,%s); mustbe=(%s,%s)', [px, py, tex, tey, px+pdx, py+pdy]);
545 // still in platform?
546 squash := g_Collide(tex, tey, pw, ph, nx, ny, Width, Height);
547 dx := tex-px;
548 dy := tey-py;
549 end
550 else
551 begin
552 // not standing on the platform: trace platform to see if it hits the entity
553 // hitedge (for `it`): 0: top; 1: right; 2: bottom; 3: left
555 if g_Collide(px, py, pw, ph, ox, oy, Width, Height) then
556 begin
557 e_LogWritefln('entity is embedded: plr=(%s,%s)-(%s,%s); mpl=(%s,%s)-(%s,%s)', [px, py, px+pw-1, py+ph-1, ox, oy, ox+Width-1, oy+Height-1]);
558 end;
560 if sweepAABB(ox, oy, Width, Height, pdx, pdy, px, py, pw, ph, @u0, nil, @u1) then
561 begin
562 //e_LogWritefln('T: platsweep; u0=%s; u1=%s; hedge=%s; sweepAABB(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)', [u0, u1, hedge, ox, oy, Width, Height, pdx, pdy, px-1, py-1, pw+2, ph+2]);
563 // yes, platform hits the entity, push the entity in the direction of the platform
564 u0 := 1.0-u0; // how much path left?
565 pdx := trunc(pdx*u0);
566 pdy := trunc(pdy*u0);
567 //e_LogWritefln(' platsweep; uleft=%s; pd=(%s,%s)', [u0, pdx, pdy]);
568 if (pdx <> 0) or (pdy <> 0) then
569 begin
570 // has some path to go, trace the entity
571 mapGrid.traceBox(tex, tey, px, py, pw, ph, pdx, pdy, nil, GridTagObstacle);
572 //e_LogWritefln(' tracebox: te=(%s,%s)', [tex, tey]);
573 end
574 else
575 begin
576 // no movement
577 tex := px;
578 tey := py;
579 end;
580 // free to push along the whole path, or path was corrected
581 // still in platform?
582 squash := g_Collide(tex, tey, pw, ph, nx, ny, Width, Height);
583 dx := tex-px;
584 dy := tey-py;
585 end
586 else
587 begin
588 // no collistion, but may be embedded
589 //e_LogWritefln('F: platsweep; u0=%s; u1=%s; sweepAABB(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)', [u0, u1, ox, oy, Width, Height, pdx, pdy, px-1, py-1, pw+2, ph+2]);
590 squash := (u1 >= 0.0);
591 end;
592 end;
593 result := (dx <> 0) or (dy <> 0);
594 end;
596 (*
597 function monCollect (mon: TMonster): Boolean;
598 begin
599 result := false; // don't stop
600 monMoveList[monMoveListUsed] := mon;
601 Inc(monMoveListUsed);
602 end;
604 function doPush (px, py, pw, ph: Integer; out dx, dy: Integer): Boolean;
605 begin
606 result := g_Collide(px, py, pw, ph, nx, ny, Width, Height);
607 if result then
608 begin
609 // need to push
610 if (mMovingSpeed.X < 0) then dx := nx-(px+pw)
611 else if (mMovingSpeed.X > 0) then dx := (nx+Width)-px
612 else dx := 0;
613 if (mMovingSpeed.Y < 0) then dy := ny-(py+ph)
614 else if (mMovingSpeed.Y > 0) then dy := (ny+Height)-py
615 else dy := 0;
616 end
617 else
618 begin
619 dx := 0;
620 dy := 0;
621 end;
622 end;
624 function monMove (mon: TMonster): Boolean;
625 begin
626 result := false; // don't stop
627 //mon.GameX := mon.GameX+mMovingSpeed.X;
628 //mon.GameY := mon.GameY+mMovingSpeed.Y;
629 mon.setPosition(mon.GameX+mMovingSpeed.X, mon.GameY+mMovingSpeed.Y, false); // we can't call `positionChanged()` in grid callback
630 if (monMoveListUsed >= Length(monMoveList)) then SetLength(monMoveList, monMoveListUsed+64);
631 monMoveList[monMoveListUsed] := mon;
632 Inc(monMoveListUsed);
633 end;
635 function monPush (mon: TMonster): Boolean;
636 var
637 px, py, pw, ph, dx, dy: Integer;
638 begin
639 result := false; // don't stop
640 mon.getMapBox(px, py, pw, ph);
641 if doPush(px, py, pw, ph, dx, dy) then
642 begin
643 //mon.GameX := mon.GameX+dx;
644 //mon.GameY := mon.GameY+dy;
645 mon.setPosition(mon.GameX+dx, mon.GameY+dy, false); // we can't call `positionChanged()` in grid callback
646 if (monMoveListUsed >= Length(monMoveList)) then SetLength(monMoveList, monMoveListUsed+64);
647 monMoveList[monMoveListUsed] := mon;
648 Inc(monMoveListUsed);
649 end;
650 end;
652 procedure plrMove (plr: TPlayer);
653 var
654 px, py, pw, ph, dx, dy: Integer;
655 begin
656 // dead players leaves separate body entities, so don't move 'em
657 if (plr = nil) or not plr.alive then exit;
658 plr.getMapBox(px, py, pw, ph);
659 if (py+ph <> oy) then
660 begin
661 // push player
662 if doPush(px, py, pw, ph, dx, dy) then
663 begin
664 plr.GameX := plr.GameX+dx;
665 plr.GameY := plr.GameY+dy;
666 plr.positionChanged();
667 // check if we're squashed
668 if plr.alive then
669 begin
670 plr.getMapBox(px, py, pw, ph);
671 if g_Map_CollidePanel(px, py, pw, ph, (PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR)) then
672 begin
673 plr.Damage(15000, 0, 0, 0, HIT_TRAP);
674 end;
675 end;
676 end;
677 exit;
678 end;
679 if (px+pw <= ox) then exit;
680 if (px >= ox+Width) then exit;
681 plr.GameX := plr.GameX+mMovingSpeed.X;
682 plr.GameY := plr.GameY+mMovingSpeed.Y;
683 plr.positionChanged();
684 end;
686 procedure gibMove (gib: PGib);
687 var
688 px, py, pw, ph, dx, dy: Integer;
689 begin
690 if (gib = nil) or not gib.alive then exit;
691 gib.getMapBox(px, py, pw, ph);
693 writeln('gib: p=(', px, ',', py, '); obj=(', gib.Obj.X, ',', gib.Obj.Y, ')');
694 px := gib.Obj.X;
695 py := gib.Obj.Y;
697 if (py+ph <> oy) then
698 begin
699 // push gib
700 if doPush(px, py, pw, ph, dx, dy) then
701 begin
702 gib.Obj.X += dx;
703 gib.Obj.Y += dy;
704 gib.positionChanged();
705 end;
706 exit;
707 end;
708 if (px+pw <= ox) then exit;
709 if (px >= ox+Width) then exit;
710 gib.Obj.X += mMovingSpeed.X;
711 gib.Obj.Y += mMovingSpeed.Y;
712 gib.positionChanged();
713 end;
715 procedure corpseMove (cor: TCorpse);
716 var
717 px, py, pw, ph, dx, dy: Integer;
718 begin
719 if (cor = nil) then exit;
720 cor.getMapBox(px, py, pw, ph);
721 if (py+ph <> oy) then
722 begin
723 // push gib
724 if doPush(px, py, pw, ph, dx, dy) then
725 begin
726 cor.moveBy(dx, dy); // will call `positionChanged()` for us
727 end;
728 exit;
729 end;
730 if (px+pw <= ox) then exit;
731 if (px >= ox+Width) then exit;
732 cor.moveBy(mMovingSpeed.X, mMovingSpeed.Y); // will call `positionChanged()` for us
733 end;
734 *)
736 var
737 f: Integer;
738 plr: TPlayer;
739 px, py, pw, ph, pdx, pdy: Integer;
740 squash: Boolean;
742 mon: TMonster;
743 sdx, sdy, he: Integer;
744 u0, u1: Single;
746 begin
747 if (not Enabled) or (Width < 1) or (Height < 1) then exit;
749 if (FCurTexture >= 0) and
750 (FTextureIDs[FCurTexture].Anim) and
751 (FTextureIDs[FCurTexture].AnTex <> nil) and
752 (FAlpha < 255) then
753 begin
754 FTextureIDs[FCurTexture].AnTex.Update();
755 FCurFrame := FTextureIDs[FCurTexture].AnTex.CurrentFrame;
756 FCurFrameCount := FTextureIDs[FCurTexture].AnTex.CurrentCounter;
757 end;
759 // moving platform?
760 if mMovingActive and (not mMovingSpeed.isZero) and g_dbgpan_mplat_active then
761 begin
762 (*
763 * collect all monsters and players (aka entities) along the possible platform path
764 * if entity is standing on a platform:
765 * try to move it along the platform path, checking wall collisions
766 * if entity is NOT standing on a platform, but hit with sweeped platform aabb:
767 * try to push entity
768 * if we can't push entity all the way, squash it
769 *)
770 // old rect
771 ox := X;
772 oy := Y;
773 ex := ox+Width-1;
774 ey := ox+Height-1;
775 // new rect
776 nx := ox+mMovingSpeed.X;
777 ny := oy+mMovingSpeed.Y;
778 nex := nx+Width-1;
779 ney := ny+Height-1;
780 // full rect
781 cx0 := nmin(ox, nx);
782 cy0 := nmin(oy, ny);
783 cx1 := nmax(ex, nex);
784 cy1 := nmax(ey, ney);
786 // temporarily turn off this panel, so it won't interfere with collision checks
787 mapGrid.proxyEnabled[proxyId] := false;
789 for f := 0 to High(gPlayers) do
790 begin
791 plr := gPlayers[f];
792 if (plr = nil) or (not plr.alive) then continue;
793 plr.getMapBox(px, py, pw, ph);
794 if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash) then
795 begin
796 // set new position
797 plr.moveBy(pdx, pdy); // this will call `positionChanged()` for us
798 // squash player, if necessary
799 if squash then plr.Damage(15000, 0, 0, 0, HIT_TRAP);
800 end;
801 end;
803 // restore panel state
804 mapGrid.proxyEnabled[proxyId] := true;
805 // and really move it
806 X := nx;
807 Y := ny;
808 positionChanged();
810 // reverse moving direction, if necessary
811 if (mMovingSpeed.X < 0) and (nx <= mMovingStart.X) then mMovingSpeed.X := -mMovingSpeed.X
812 else if (mMovingSpeed.X > 0) and (nx >= mMovingEnd.X) then mMovingSpeed.X := -mMovingSpeed.X;
813 if (mMovingSpeed.Y < 0) and (ny <= mMovingStart.Y) then mMovingSpeed.Y := -mMovingSpeed.Y
814 else if (mMovingSpeed.Y > 0) and (ny >= mMovingEnd.Y) then mMovingSpeed.Y := -mMovingSpeed.Y;
816 (*
817 // collect monsters
818 monMoveListUsed := 0;
819 g_Mons_ForEachAt(cx0, cy0, cx1-cx0+1, cy1-cy0+1, monCollect);
820 // process collected monsters
823 // move monsters on lifts
824 g_Mons_ForEachAt(ox, oy-1, Width, 1, monMove);
825 X := nx;
826 Y := ny;
827 // fix grid
828 positionChanged();
829 // push monsters
830 g_Mons_ForEachAt(nx, ny, Width, Height, monPush);
831 // move and push players
832 for f := 0 to High(gPlayers) do
833 begin
834 gPlayers[f].getMapBox(px, py, pw, ph);
835 //if sweepAABB(ox, oy, Width, Height, mMovingSpeed.X, mMovingSpeed.Y, px, py, pw, ph, @u0, @u1) then
836 //if sweepAABB(ox, oy, Width, Height, mMovingSpeed.X, mMovingSpeed.Y, px, py, pw, ph, @u0, @u1) then
837 sdx := -mMovingSpeed.X;
838 sdy := -mMovingSpeed.Y;
839 if sweepAABB(ox, oy, Width, Height, mMovingSpeed.X, mMovingSpeed.Y, px, py, pw, ph, @u0, @he, @u1) then
840 begin
841 e_LogWritefln('player #%s sweep with platform %s: u0=%s; u1=%s; phe=%s', [f, mGUID, u0, u1, he]);
842 if (u1 > 0.0) then
843 begin
844 e_LogWritefln(' collide: %s', [g_Collide(px, py, pw, ph, nx, ny, Width, Height)]);
845 //gPlayers[f].GameX := gPlayers[f].GameX+trunc(sdx*(1.0-u0));
846 //gPlayers[f].GameY := gPlayers[f].GameY+trunc(mMovingSpeed.Y*(1.0-u0));
847 //gPlayers[f].positionChanged();
848 end;
849 end;
850 //plrMove(gPlayers[f]);
851 end;
852 // move and push gibs
853 for f := 0 to High(gGibs) do gibMove(@gGibs[f]);
854 // move and push corpses
855 for f := 0 to High(gCorpses) do corpseMove(gCorpses[f]);
856 // notify moved monsters about their movement
857 for f := 0 to monMoveListUsed-1 do
858 begin
859 monMoveList[f].positionChanged();
860 end;
861 for f := 0 to monMoveListUsed-1 do
862 begin
863 mon := monMoveList[f];
864 // check if it is squashed
865 if mon.alive then
866 begin
867 mon.getMapBox(px, py, pw, ph);
868 if g_Map_CollidePanel(px, py, pw, ph, (PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR)) then
869 begin
870 mon.Damage(15000, 0, 0, 0, HIT_TRAP);
871 end;
872 end;
873 end;
874 *)
875 end;
876 end;
878 procedure TPanel.SetFrame(Frame: Integer; Count: Byte);
880 function ClampInt(X, A, B: Integer): Integer;
881 begin
882 Result := X;
883 if X < A then Result := A else if X > B then Result := B;
884 end;
886 begin
887 if Enabled and (FCurTexture >= 0) and
888 (FTextureIDs[FCurTexture].Anim) and
889 (FTextureIDs[FCurTexture].AnTex <> nil) and
890 (Width > 0) and (Height > 0) and (FAlpha < 255) then
891 begin
892 FCurFrame := ClampInt(Frame, 0, FTextureIDs[FCurTexture].AnTex.TotalFrames);
893 FCurFrameCount := Count;
894 FTextureIDs[FCurTexture].AnTex.CurrentFrame := FCurFrame;
895 FTextureIDs[FCurTexture].AnTex.CurrentCounter := FCurFrameCount;
896 end;
897 end;
899 procedure TPanel.NextTexture(AnimLoop: Byte = 0);
900 begin
901 Assert(FCurTexture >= -1, 'FCurTexture < -1');
903 // Íåò òåêñòóð:
904 if Length(FTextureIDs) = 0 then
905 FCurTexture := -1
906 else
907 // Òîëüêî îäíà òåêñòóðà:
908 if Length(FTextureIDs) = 1 then
909 begin
910 if FCurTexture = 0 then
911 FCurTexture := -1
912 else
913 FCurTexture := 0;
914 end
915 else
916 // Áîëüøå îäíîé òåêñòóðû:
917 begin
918 // Ñëåäóþùàÿ:
919 Inc(FCurTexture);
920 // Ñëåäóþùåé íåò - âîçâðàò ê íà÷àëó:
921 if FCurTexture >= Length(FTextureIDs) then
922 FCurTexture := 0;
923 end;
925 // Ïåðåêëþ÷èëèñü íà âèäèìóþ àíèì. òåêñòóðó:
926 if (FCurTexture >= 0) and FTextureIDs[FCurTexture].Anim then
927 begin
928 if (FTextureIDs[FCurTexture].AnTex = nil) then
929 begin
930 g_FatalError(_lc[I_GAME_ERROR_SWITCH_TEXTURE]);
931 Exit;
932 end;
934 if AnimLoop = 1 then
935 FTextureIDs[FCurTexture].AnTex.Loop := True
936 else
937 if AnimLoop = 2 then
938 FTextureIDs[FCurTexture].AnTex.Loop := False;
940 FTextureIDs[FCurTexture].AnTex.Reset();
941 end;
943 LastAnimLoop := AnimLoop;
944 end;
946 procedure TPanel.SetTexture(ID: Integer; AnimLoop: Byte = 0);
947 begin
948 // Íåò òåêñòóð:
949 if Length(FTextureIDs) = 0 then
950 FCurTexture := -1
951 else
952 // Òîëüêî îäíà òåêñòóðà:
953 if Length(FTextureIDs) = 1 then
954 begin
955 if (ID = 0) or (ID = -1) then
956 FCurTexture := ID;
957 end
958 else
959 // Áîëüøå îäíîé òåêñòóðû:
960 begin
961 if (ID >= -1) and (ID <= High(FTextureIDs)) then
962 FCurTexture := ID;
963 end;
965 // Ïåðåêëþ÷èëèñü íà âèäèìóþ àíèì. òåêñòóðó:
966 if (FCurTexture >= 0) and FTextureIDs[FCurTexture].Anim then
967 begin
968 if (FTextureIDs[FCurTexture].AnTex = nil) then
969 begin
970 g_FatalError(_lc[I_GAME_ERROR_SWITCH_TEXTURE]);
971 Exit;
972 end;
974 if AnimLoop = 1 then
975 FTextureIDs[FCurTexture].AnTex.Loop := True
976 else
977 if AnimLoop = 2 then
978 FTextureIDs[FCurTexture].AnTex.Loop := False;
980 FTextureIDs[FCurTexture].AnTex.Reset();
981 end;
983 LastAnimLoop := AnimLoop;
984 end;
986 function TPanel.GetTextureID(): DWORD;
987 begin
988 Result := LongWord(TEXTURE_NONE);
990 if (FCurTexture >= 0) then
991 begin
992 if FTextureIDs[FCurTexture].Anim then
993 Result := FTextureIDs[FCurTexture].AnTex.FramesID
994 else
995 Result := FTextureIDs[FCurTexture].Tex;
996 end;
997 end;
999 function TPanel.GetTextureCount(): Integer;
1000 begin
1001 Result := Length(FTextureIDs);
1002 if Enabled and (FCurTexture >= 0) then
1003 if (FTextureIDs[FCurTexture].Anim) and
1004 (FTextureIDs[FCurTexture].AnTex <> nil) and
1005 (Width > 0) and (Height > 0) and (FAlpha < 255) then
1006 Result := Result + 100;
1007 end;
1009 procedure TPanel.SaveState(Var Mem: TBinMemoryWriter);
1010 var
1011 sig: DWORD;
1012 anim: Boolean;
1013 begin
1014 if (Mem = nil) then exit;
1015 //if not SaveIt then exit;
1017 // Ñèãíàòóðà ïàíåëè:
1018 sig := PANEL_SIGNATURE; // 'PANL'
1019 Mem.WriteDWORD(sig);
1020 // Îòêðûòà/çàêðûòà, åñëè äâåðü:
1021 Mem.WriteBoolean(FEnabled);
1022 // Íàïðàâëåíèå ëèôòà, åñëè ëèôò:
1023 Mem.WriteByte(FLiftType);
1024 // Íîìåð òåêóùåé òåêñòóðû:
1025 Mem.WriteInt(FCurTexture);
1026 // Êîîðäû
1027 Mem.WriteInt(FX);
1028 Mem.WriteInt(FY);
1029 // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà:
1030 if (FCurTexture >= 0) and (FTextureIDs[FCurTexture].Anim) then
1031 begin
1032 Assert(FTextureIDs[FCurTexture].AnTex <> nil,
1033 'TPanel.SaveState: No animation object');
1034 anim := True;
1035 end
1036 else
1037 anim := False;
1038 Mem.WriteBoolean(anim);
1039 // Åñëè äà - ñîõðàíÿåì àíèìàöèþ:
1040 if anim then
1041 FTextureIDs[FCurTexture].AnTex.SaveState(Mem);
1042 // moving platform state
1043 Mem.WriteInt(mMovingSpeed.X);
1044 Mem.WriteInt(mMovingSpeed.Y);
1045 Mem.WriteInt(mMovingStart.X);
1046 Mem.WriteInt(mMovingStart.Y);
1047 Mem.WriteInt(mMovingEnd.X);
1048 Mem.WriteInt(mMovingEnd.Y);
1049 Mem.WriteBoolean(mMovingActive);
1050 end;
1052 procedure TPanel.LoadState(var Mem: TBinMemoryReader);
1053 var
1054 sig: DWORD;
1055 anim: Boolean;
1056 //ox, oy: Integer;
1057 begin
1058 if (Mem = nil) then exit;
1059 //if not SaveIt then exit;
1061 // Ñèãíàòóðà ïàíåëè:
1062 Mem.ReadDWORD(sig);
1063 if sig <> PANEL_SIGNATURE then // 'PANL'
1064 begin
1065 raise EBinSizeError.Create('TPanel.LoadState: Wrong Panel Signature');
1066 end;
1067 // Îòêðûòà/çàêðûòà, åñëè äâåðü:
1068 Mem.ReadBoolean(FEnabled);
1069 // Íàïðàâëåíèå ëèôòà, åñëè ëèôò:
1070 Mem.ReadByte(FLiftType);
1071 // Íîìåð òåêóùåé òåêñòóðû:
1072 Mem.ReadInt(FCurTexture);
1073 // Êîîðäû
1074 //ox := FX;
1075 //oy := FY;
1076 Mem.ReadInt(FX);
1077 Mem.ReadInt(FY);
1078 //e_LogWritefln('panel %s(%s): old=(%s,%s); new=(%s,%s); delta=(%s,%s)', [arrIdx, proxyId, ox, oy, FX, FY, FX-ox, FY-oy]);
1079 // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà:
1080 Mem.ReadBoolean(anim);
1081 // Åñëè äà - çàãðóæàåì àíèìàöèþ:
1082 if anim then
1083 begin
1084 Assert((FCurTexture >= 0) and
1085 (FTextureIDs[FCurTexture].Anim) and
1086 (FTextureIDs[FCurTexture].AnTex <> nil),
1087 'TPanel.LoadState: No animation object');
1088 FTextureIDs[FCurTexture].AnTex.LoadState(Mem);
1089 end;
1090 // moving platform state
1091 Mem.ReadInt(mMovingSpeed.X);
1092 Mem.ReadInt(mMovingSpeed.Y);
1093 Mem.ReadInt(mMovingStart.X);
1094 Mem.ReadInt(mMovingStart.Y);
1095 Mem.ReadInt(mMovingEnd.X);
1096 Mem.ReadInt(mMovingEnd.Y);
1097 Mem.ReadBoolean(mMovingActive);
1099 positionChanged();
1100 //mapGrid.proxyEnabled[proxyId] := FEnabled; // done in g_map.pas
1101 end;
1103 end.