DEADSOFTWARE

`Grid.forEachInAABB()`: no more callbacks
[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 SysUtils, Classes,
24 MAPDEF, g_textures, xdynrec;
26 type
27 TAddTextureArray = Array of
28 record
29 Texture: Cardinal;
30 Anim: Boolean;
31 end;
33 PPanel = ^TPanel;
34 TPanel = Class (TObject)
35 private
36 const
37 private
38 mGUID: Integer; // will be assigned in "g_map.pas"
39 FTextureWidth: Word;
40 FTextureHeight: Word;
41 FAlpha: Byte;
42 FBlending: Boolean;
43 FTextureIDs: Array of
44 record
45 case Anim: Boolean of
46 False: (Tex: Cardinal);
47 True: (AnTex: TAnimation);
48 end;
50 mMovingSpeed: TDFPoint;
51 mMovingStart: TDFPoint;
52 mMovingEnd: TDFPoint;
53 mMovingActive: Boolean;
54 mMoveOnce: Boolean;
56 mOldMovingActive: Boolean;
58 mSizeSpeed: TDFSize;
59 mSizeEnd: TDFSize;
61 mEndPosTrig: Integer;
62 mEndSizeTrig: Integer;
64 mNeedSend: Boolean; // for network
66 private
67 function getx1 (): Integer; inline;
68 function gety1 (): Integer; inline;
69 function getvisvalid (): Boolean; inline;
71 function getMovingSpeedX (): Integer; inline;
72 procedure setMovingSpeedX (v: Integer); inline;
73 function getMovingSpeedY (): Integer; inline;
74 procedure setMovingSpeedY (v: Integer); inline;
76 function getMovingStartX (): Integer; inline;
77 procedure setMovingStartX (v: Integer); inline;
78 function getMovingStartY (): Integer; inline;
79 procedure setMovingStartY (v: Integer); inline;
81 function getMovingEndX (): Integer; inline;
82 procedure setMovingEndX (v: Integer); inline;
83 function getMovingEndY (): Integer; inline;
84 procedure setMovingEndY (v: Integer); inline;
86 function getSizeSpeedX (): Integer; inline;
87 procedure setSizeSpeedX (v: Integer); inline;
88 function getSizeSpeedY (): Integer; inline;
89 procedure setSizeSpeedY (v: Integer); inline;
91 function getSizeEndX (): Integer; inline;
92 procedure setSizeEndX (v: Integer); inline;
93 function getSizeEndY (): Integer; inline;
94 procedure setSizeEndY (v: Integer); inline;
96 public
97 FCurTexture: Integer; // Íîìåð òåêóùåé òåêñòóðû
98 FCurFrame: Integer;
99 FCurFrameCount: Byte;
100 FX, FY: Integer;
101 FWidth, FHeight: Word;
102 FPanelType: Word;
103 FEnabled: Boolean;
104 FDoor: Boolean;
105 FLiftType: Byte;
106 FLastAnimLoop: Byte;
107 // sorry, there fields are public to allow setting 'em in g_map; this should be fixed later
108 // for now, PLEASE, don't modify 'em, or all hell will break loose
109 arrIdx: Integer; // index in one of internal arrays; sorry
110 tag: Integer; // used in coldets and such; sorry; see g_map.GridTagXXX
111 proxyId: Integer; // proxy id in map grid (DO NOT USE!)
112 mapId: AnsiString; // taken directly from map file; dunno why it is here
114 constructor Create(PanelRec: TDynRecord;
115 AddTextures: TAddTextureArray;
116 CurTex: Integer;
117 var Textures: TLevelTextureArray; aguid: Integer);
118 destructor Destroy(); override;
120 procedure Draw (hasAmbient: Boolean; constref ambColor: TDFColor);
121 procedure DrawShadowVolume(lightX: Integer; lightY: Integer; radius: Integer);
122 procedure Update();
123 procedure SetFrame(Frame: Integer; Count: Byte);
124 procedure NextTexture(AnimLoop: Byte = 0);
125 procedure SetTexture(ID: Integer; AnimLoop: Byte = 0);
126 function GetTextureID(): Cardinal;
127 function GetTextureCount(): Integer;
129 procedure SaveState (st: TStream);
130 procedure LoadState (st: TStream);
132 procedure positionChanged (); inline;
134 function getIsGBack (): Boolean; inline; // gRenderBackgrounds
135 function getIsGStep (): Boolean; inline; // gSteps
136 function getIsGWall (): Boolean; inline; // gWalls
137 function getIsGAcid1 (): Boolean; inline; // gAcid1
138 function getIsGAcid2 (): Boolean; inline; // gAcid2
139 function getIsGWater (): Boolean; inline; // gWater
140 function getIsGFore (): Boolean; inline; // gRenderForegrounds
141 function getIsGLift (): Boolean; inline; // gLifts
142 function getIsGBlockMon (): Boolean; inline; // gBlockMon
144 // get-and-clear
145 function gncNeedSend (): Boolean; inline;
146 procedure setDirty (); inline; // why `dirty`? 'cause i may introduce property `needSend` later
148 public
149 property visvalid: Boolean read getvisvalid; // panel is "visvalid" when it's width and height are positive
151 published
152 property guid: Integer read mGUID; // will be assigned in "g_map.pas"
153 property x0: Integer read FX;
154 property y0: Integer read FY;
155 property x1: Integer read getx1; // inclusive!
156 property y1: Integer read gety1; // inclusive!
157 property x: Integer read FX write FX;
158 property y: Integer read FY write FY;
159 property width: Word read FWidth write FWidth;
160 property height: Word read FHeight write FHeight;
161 property panelType: Word read FPanelType write FPanelType;
162 property enabled: Boolean read FEnabled write FEnabled;
163 property door: Boolean read FDoor write FDoor;
164 property liftType: Byte read FLiftType write FLiftType;
165 property lastAnimLoop: Byte read FLastAnimLoop write FLastAnimLoop;
167 property movingSpeedX: Integer read getMovingSpeedX write setMovingSpeedX;
168 property movingSpeedY: Integer read getMovingSpeedY write setMovingSpeedY;
169 property movingStartX: Integer read getMovingStartX write setMovingStartX;
170 property movingStartY: Integer read getMovingStartY write setMovingStartY;
171 property movingEndX: Integer read getMovingEndX write setMovingEndX;
172 property movingEndY: Integer read getMovingEndY write setMovingEndY;
173 property movingActive: Boolean read mMovingActive write mMovingActive;
174 property moveOnce: Boolean read mMoveOnce write mMoveOnce;
176 property sizeSpeedX: Integer read getSizeSpeedX write setSizeSpeedX;
177 property sizeSpeedY: Integer read getSizeSpeedY write setSizeSpeedY;
178 property sizeEndX: Integer read getSizeEndX write setSizeEndX;
179 property sizeEndY: Integer read getSizeEndY write setSizeEndY;
181 property isGBack: Boolean read getIsGBack;
182 property isGStep: Boolean read getIsGStep;
183 property isGWall: Boolean read getIsGWall;
184 property isGAcid1: Boolean read getIsGAcid1;
185 property isGAcid2: Boolean read getIsGAcid2;
186 property isGWater: Boolean read getIsGWater;
187 property isGFore: Boolean read getIsGFore;
188 property isGLift: Boolean read getIsGLift;
189 property isGBlockMon: Boolean read getIsGBlockMon;
191 public
192 property movingSpeed: TDFPoint read mMovingSpeed write mMovingSpeed;
193 property movingStart: TDFPoint read mMovingStart write mMovingStart;
194 property movingEnd: TDFPoint read mMovingEnd write mMovingEnd;
196 property sizeSpeed: TDFSize read mSizeSpeed write mSizeSpeed;
197 property sizeEnd: TDFSize read mSizeEnd write mSizeEnd;
199 property endPosTrigId: Integer read mEndPosTrig write mEndPosTrig;
200 property endSizeTrigId: Integer read mEndSizeTrig write mEndSizeTrig;
201 end;
203 TPanelArray = Array of TPanel;
205 var
206 g_dbgpan_mplat_active: Boolean = {$IF DEFINED(D2F_DEBUG)}true{$ELSE}true{$ENDIF};
207 g_dbgpan_mplat_step: Boolean = false; // one step, and stop
210 implementation
212 uses
213 e_texture, g_basic, g_map, g_game, g_gfx, e_graphics, g_weapons, g_triggers,
214 g_console, g_language, g_monsters, g_player, g_grid, e_log, GL, geom, utils, xstreams;
216 const
217 PANEL_SIGNATURE = $4C4E4150; // 'PANL'
219 { T P a n e l : }
221 constructor TPanel.Create(PanelRec: TDynRecord;
222 AddTextures: TAddTextureArray;
223 CurTex: Integer;
224 var Textures: TLevelTextureArray; aguid: Integer);
225 var
226 i: Integer;
227 tnum: Integer;
228 begin
229 X := PanelRec.X;
230 Y := PanelRec.Y;
231 Width := PanelRec.Width;
232 Height := PanelRec.Height;
233 FAlpha := 0;
234 FBlending := False;
235 FCurFrame := 0;
236 FCurFrameCount := 0;
237 LastAnimLoop := 0;
239 mapId := PanelRec.id;
240 mGUID := aguid;
242 mMovingSpeed := PanelRec.moveSpeed;
243 mMovingStart := PanelRec.moveStart;
244 mMovingEnd := PanelRec.moveEnd;
245 mMovingActive := PanelRec['move_active'].value;
246 mOldMovingActive := mMovingActive;
247 mMoveOnce := PanelRec.moveOnce;
249 mSizeSpeed := PanelRec.sizeSpeed;
250 mSizeEnd := PanelRec.sizeEnd;
252 mEndPosTrig := PanelRec.endPosTrig;
253 mEndSizeTrig := PanelRec.endSizeTrig;
255 mNeedSend := false;
257 // Òèï ïàíåëè:
258 PanelType := PanelRec.PanelType;
259 Enabled := True;
260 Door := False;
261 LiftType := 0;
263 case PanelType of
264 PANEL_OPENDOOR: begin Enabled := False; Door := True; end;
265 PANEL_CLOSEDOOR: Door := True;
266 PANEL_LIFTUP: LiftType := 0; //???
267 PANEL_LIFTDOWN: LiftType := 1;
268 PANEL_LIFTLEFT: LiftType := 2;
269 PANEL_LIFTRIGHT: LiftType := 3;
270 end;
272 // Íåâèäèìàÿ:
273 if ByteBool(PanelRec.Flags and PANEL_FLAG_HIDE) then
274 begin
275 SetLength(FTextureIDs, 0);
276 FCurTexture := -1;
277 Exit;
278 end;
279 // Ïàíåëè, íå èñïîëüçóþùèå òåêñòóðû:
280 if ByteBool(PanelType and
281 (PANEL_LIFTUP or
282 PANEL_LIFTDOWN or
283 PANEL_LIFTLEFT or
284 PANEL_LIFTRIGHT or
285 PANEL_BLOCKMON)) then
286 begin
287 SetLength(FTextureIDs, 0);
288 FCurTexture := -1;
289 Exit;
290 end;
292 // Åñëè ýòî æèäêîñòü áåç òåêñòóðû - ñïåöòåêñòóðó:
293 if WordBool(PanelType and (PANEL_WATER or PANEL_ACID1 or PANEL_ACID2)) and
294 (not ByteBool(PanelRec.Flags and PANEL_FLAG_WATERTEXTURES)) then
295 begin
296 SetLength(FTextureIDs, 1);
297 FTextureIDs[0].Anim := False;
299 case PanelRec.PanelType of
300 PANEL_WATER:
301 FTextureIDs[0].Tex := LongWord(TEXTURE_SPECIAL_WATER);
302 PANEL_ACID1:
303 FTextureIDs[0].Tex := LongWord(TEXTURE_SPECIAL_ACID1);
304 PANEL_ACID2:
305 FTextureIDs[0].Tex := LongWord(TEXTURE_SPECIAL_ACID2);
306 end;
308 FCurTexture := 0;
309 Exit;
310 end;
312 SetLength(FTextureIDs, Length(AddTextures));
314 if CurTex < 0 then
315 FCurTexture := -1
316 else
317 if CurTex >= Length(FTextureIDs) then
318 FCurTexture := Length(FTextureIDs) - 1
319 else
320 FCurTexture := CurTex;
322 for i := 0 to Length(FTextureIDs)-1 do
323 begin
324 FTextureIDs[i].Anim := AddTextures[i].Anim;
325 if FTextureIDs[i].Anim then
326 begin // Àíèìèðîâàííàÿ òåêñòóðà
327 FTextureIDs[i].AnTex :=
328 TAnimation.Create(Textures[AddTextures[i].Texture].FramesID,
329 True, Textures[AddTextures[i].Texture].Speed);
330 FTextureIDs[i].AnTex.Blending := ByteBool(PanelRec.Flags and PANEL_FLAG_BLENDING);
331 FTextureIDs[i].AnTex.Alpha := PanelRec.Alpha;
332 end
333 else
334 begin // Îáû÷íàÿ òåêñòóðà
335 FTextureIDs[i].Tex := Textures[AddTextures[i].Texture].TextureID;
336 end;
337 end;
339 // Òåêñòóð íåñêîëüêî - íóæíî ñîõðàíÿòü òåêóùóþ:
340 //if Length(FTextureIDs) > 1 then SaveIt := True;
342 if (PanelRec.TextureRec = nil) then tnum := -1 else tnum := PanelRec.tagInt;
343 if (tnum < 0) then tnum := Length(Textures);
345 // Åñëè íå ñïåöòåêñòóðà, òî çàäàåì ðàçìåðû:
346 if ({PanelRec.TextureNum}tnum > High(Textures)) then
347 begin
348 e_WriteLog(Format('WTF?! tnum is out of limits! (%d : %d)', [tnum, High(Textures)]), TMsgType.Warning);
349 FTextureWidth := 2;
350 FTextureHeight := 2;
351 FAlpha := 0;
352 FBlending := ByteBool(0);
353 end
354 else if not g_Map_IsSpecialTexture(Textures[{PanelRec.TextureNum}tnum].TextureName) then
355 begin
356 FTextureWidth := Textures[{PanelRec.TextureNum}tnum].Width;
357 FTextureHeight := Textures[{PanelRec.TextureNum}tnum].Height;
358 FAlpha := PanelRec.Alpha;
359 FBlending := ByteBool(PanelRec.Flags and PANEL_FLAG_BLENDING);
360 end;
361 end;
363 destructor TPanel.Destroy();
364 var
365 i: Integer;
366 begin
367 for i := 0 to High(FTextureIDs) do
368 if FTextureIDs[i].Anim then
369 FTextureIDs[i].AnTex.Free();
370 SetLength(FTextureIDs, 0);
372 Inherited;
373 end;
375 function TPanel.getx1 (): Integer; inline; begin result := X+Width-1; end;
376 function TPanel.gety1 (): Integer; inline; begin result := Y+Height-1; end;
377 function TPanel.getvisvalid (): Boolean; inline; begin result := (Width > 0) and (Height > 0); end;
379 function TPanel.getMovingSpeedX (): Integer; inline; begin result := mMovingSpeed.X; end;
380 procedure TPanel.setMovingSpeedX (v: Integer); inline; begin mMovingSpeed.X := v; end;
381 function TPanel.getMovingSpeedY (): Integer; inline; begin result := mMovingSpeed.Y; end;
382 procedure TPanel.setMovingSpeedY (v: Integer); inline; begin mMovingSpeed.Y := v; end;
384 function TPanel.getMovingStartX (): Integer; inline; begin result := mMovingStart.X; end;
385 procedure TPanel.setMovingStartX (v: Integer); inline; begin mMovingStart.X := v; end;
386 function TPanel.getMovingStartY (): Integer; inline; begin result := mMovingStart.Y; end;
387 procedure TPanel.setMovingStartY (v: Integer); inline; begin mMovingStart.Y := v; end;
389 function TPanel.getMovingEndX (): Integer; inline; begin result := mMovingEnd.X; end;
390 procedure TPanel.setMovingEndX (v: Integer); inline; begin mMovingEnd.X := v; end;
391 function TPanel.getMovingEndY (): Integer; inline; begin result := mMovingEnd.Y; end;
392 procedure TPanel.setMovingEndY (v: Integer); inline; begin mMovingEnd.Y := v; end;
394 function TPanel.getSizeSpeedX (): Integer; inline; begin result := mSizeSpeed.w; end;
395 procedure TPanel.setSizeSpeedX (v: Integer); inline; begin mSizeSpeed.w := v; end;
396 function TPanel.getSizeSpeedY (): Integer; inline; begin result := mSizeSpeed.h; end;
397 procedure TPanel.setSizeSpeedY (v: Integer); inline; begin mSizeSpeed.h := v; end;
399 function TPanel.getSizeEndX (): Integer; inline; begin result := mSizeEnd.w; end;
400 procedure TPanel.setSizeEndX (v: Integer); inline; begin mSizeEnd.w := v; end;
401 function TPanel.getSizeEndY (): Integer; inline; begin result := mSizeEnd.h; end;
402 procedure TPanel.setSizeEndY (v: Integer); inline; begin mSizeEnd.h := v; end;
404 function TPanel.getIsGBack (): Boolean; inline; begin result := ((tag and GridTagBack) <> 0); end;
405 function TPanel.getIsGStep (): Boolean; inline; begin result := ((tag and GridTagStep) <> 0); end;
406 function TPanel.getIsGWall (): Boolean; inline; begin result := ((tag and (GridTagWall or GridTagDoor)) <> 0); end;
407 function TPanel.getIsGAcid1 (): Boolean; inline; begin result := ((tag and GridTagAcid1) <> 0); end;
408 function TPanel.getIsGAcid2 (): Boolean; inline; begin result := ((tag and GridTagAcid2) <> 0); end;
409 function TPanel.getIsGWater (): Boolean; inline; begin result := ((tag and GridTagWater) <> 0); end;
410 function TPanel.getIsGFore (): Boolean; inline; begin result := ((tag and GridTagFore) <> 0); end;
411 function TPanel.getIsGLift (): Boolean; inline; begin result := ((tag and GridTagLift) <> 0); end;
412 function TPanel.getIsGBlockMon (): Boolean; inline; begin result := ((tag and GridTagBlockMon) <> 0); end;
414 function TPanel.gncNeedSend (): Boolean; inline; begin result := mNeedSend; mNeedSend := false; end;
415 procedure TPanel.setDirty (); inline; begin mNeedSend := true; end;
418 procedure TPanel.Draw (hasAmbient: Boolean; constref ambColor: TDFColor);
419 var
420 xx, yy: Integer;
421 NoTextureID: DWORD;
422 NW, NH: Word;
423 begin
424 if {Enabled and} (FCurTexture >= 0) and
425 (Width > 0) and (Height > 0) and (FAlpha < 255) {and
426 g_Collide(X, Y, Width, Height, sX, sY, sWidth, sHeight)} then
427 begin
428 if FTextureIDs[FCurTexture].Anim then
429 begin // Àíèìèðîâàííàÿ òåêñòóðà
430 if FTextureIDs[FCurTexture].AnTex = nil then
431 Exit;
433 for xx := 0 to (Width div FTextureWidth)-1 do
434 for yy := 0 to (Height div FTextureHeight)-1 do
435 FTextureIDs[FCurTexture].AnTex.Draw(
436 X + xx*FTextureWidth,
437 Y + yy*FTextureHeight, TMirrorType.None);
438 end
439 else
440 begin // Îáû÷íàÿ òåêñòóðà
441 case FTextureIDs[FCurTexture].Tex of
442 LongWord(TEXTURE_SPECIAL_WATER): e_DrawFillQuad(X, Y, X+Width-1, Y+Height-1, 0, 0, 255, 0, TBlending.Filter);
443 LongWord(TEXTURE_SPECIAL_ACID1): e_DrawFillQuad(X, Y, X+Width-1, Y+Height-1, 0, 128, 0, 0, TBlending.Filter);
444 LongWord(TEXTURE_SPECIAL_ACID2): e_DrawFillQuad(X, Y, X+Width-1, Y+Height-1, 128, 0, 0, 0, TBlending.Filter);
445 LongWord(TEXTURE_NONE):
446 if g_Texture_Get('NOTEXTURE', NoTextureID) then
447 begin
448 e_GetTextureSize(NoTextureID, @NW, @NH);
449 e_DrawFill(NoTextureID, X, Y, Width div NW, Height div NH, 0, False, False);
450 end
451 else
452 begin
453 xx := X + (Width div 2);
454 yy := Y + (Height div 2);
455 e_DrawFillQuad(X, Y, xx, yy, 255, 0, 255, 0);
456 e_DrawFillQuad(xx, Y, X+Width-1, yy, 255, 255, 0, 0);
457 e_DrawFillQuad(X, yy, xx, Y+Height-1, 255, 255, 0, 0);
458 e_DrawFillQuad(xx, yy, X+Width-1, Y+Height-1, 255, 0, 255, 0);
459 end;
460 else
461 begin
462 if not mMovingActive then
463 e_DrawFill(FTextureIDs[FCurTexture].Tex, X, Y, Width div FTextureWidth, Height div FTextureHeight, FAlpha, True, FBlending, hasAmbient)
464 else
465 e_DrawFillX(FTextureIDs[FCurTexture].Tex, X, Y, Width, Height, FAlpha, True, FBlending, g_dbg_scale, hasAmbient);
466 if hasAmbient then e_AmbientQuad(X, Y, Width, Height, ambColor.r, ambColor.g, ambColor.b, ambColor.a);
467 end;
468 end;
469 end;
470 end;
471 end;
473 procedure TPanel.DrawShadowVolume(lightX: Integer; lightY: Integer; radius: Integer);
474 procedure extrude (x: Integer; y: Integer);
475 begin
476 glVertex2i(x+(x-lightX)*500, y+(y-lightY)*500);
477 //e_WriteLog(Format(' : (%d,%d)', [x+(x-lightX)*300, y+(y-lightY)*300]), MSG_WARNING);
478 end;
480 procedure drawLine (x0: Integer; y0: Integer; x1: Integer; y1: Integer);
481 begin
482 // does this side facing the light?
483 if ((x1-x0)*(lightY-y0)-(lightX-x0)*(y1-y0) >= 0) then exit;
484 //e_WriteLog(Format('lightpan: (%d,%d)-(%d,%d)', [x0, y0, x1, y1]), MSG_WARNING);
485 // this edge is facing the light, extrude and draw it
486 glVertex2i(x0, y0);
487 glVertex2i(x1, y1);
488 extrude(x1, y1);
489 extrude(x0, y0);
490 end;
492 begin
493 if radius < 4 then exit;
494 if Enabled and (FCurTexture >= 0) and (Width > 0) and (Height > 0) and (FAlpha < 255) {and
495 g_Collide(X, Y, Width, Height, sX, sY, sWidth, sHeight)} then
496 begin
497 if not FTextureIDs[FCurTexture].Anim then
498 begin
499 case FTextureIDs[FCurTexture].Tex of
500 LongWord(TEXTURE_SPECIAL_WATER): exit;
501 LongWord(TEXTURE_SPECIAL_ACID1): exit;
502 LongWord(TEXTURE_SPECIAL_ACID2): exit;
503 LongWord(TEXTURE_NONE): exit;
504 end;
505 end;
506 if (X+Width < lightX-radius) then exit;
507 if (Y+Height < lightY-radius) then exit;
508 if (X > lightX+radius) then exit;
509 if (Y > lightY+radius) then exit;
510 //e_DrawFill(FTextureIDs[FCurTexture].Tex, X, Y, Width div FTextureWidth, Height div FTextureHeight, FAlpha, True, FBlending);
512 glBegin(GL_QUADS);
513 drawLine(x, y, x+width, y); // top
514 drawLine(x+width, y, x+width, y+height); // right
515 drawLine(x+width, y+height, x, y+height); // bottom
516 drawLine(x, y+height, x, y); // left
517 glEnd();
518 end;
519 end;
522 procedure TPanel.positionChanged (); inline;
523 var
524 px, py, pw, ph: Integer;
525 begin
526 if (proxyId >= 0) then
527 begin
528 mapGrid.getBodyDims(proxyId, px, py, pw, ph);
529 if (px <> x) or (py <> y) or (pw <> Width) or (ph <> Height) then
530 begin
532 e_LogWritefln('panel moved: arridx=%s; guid=%s; proxyid=%s; old:(%s,%s)-(%sx%s); new:(%s,%s)-(%sx%s)',
533 [arrIdx, mGUID, proxyId, px, py, pw, ph, x, y, width, height]);
535 g_Mark(px, py, pw, ph, MARK_WALL, false);
536 if (Width < 1) or (Height < 1) then
537 begin
538 mapGrid.proxyEnabled[proxyId] := false;
539 end
540 else
541 begin
542 mapGrid.proxyEnabled[proxyId] := Enabled;
543 if (pw <> Width) or (ph <> Height) then
544 begin
545 //writeln('panel resize!');
546 mapGrid.moveResizeBody(proxyId, X, Y, Width, Height)
547 end
548 else
549 begin
550 mapGrid.moveBody(proxyId, X, Y);
551 end;
552 g_Mark(X, Y, Width, Height, MARK_WALL);
553 end;
554 end;
555 end;
556 end;
559 var
560 monCheckList: array of TMonster = nil;
561 monCheckListUsed: Integer = 0;
563 procedure TPanel.Update();
564 var
565 ox, oy: Integer;
566 nx, ny, nw, nh: Integer;
567 ex, ey, nex, ney: Integer;
568 mpw, mph: Integer;
570 // return `true` if we should move by dx,dy
571 function tryMPlatMove (px, py, pw, ph: Integer; out dx, dy: Integer; out squash: Boolean; ontop: PBoolean=nil): Boolean;
572 var
573 u0: Single;
574 tex, tey: Integer;
575 pdx, pdy: Integer;
576 trtag: Integer;
577 szdx, szdy: Integer;
578 begin
579 squash := false;
580 tex := px;
581 tey := py;
582 pdx := mMovingSpeed.X;
583 pdy := mMovingSpeed.Y;
584 // standing on the platform?
585 if (py+ph = oy) then
586 begin
587 if (ontop <> nil) then ontop^ := true;
588 // yes, move with it; but skip steps (no need to process size change here, 'cause platform top cannot be changed with it)
589 mapGrid.traceBox(tex, tey, px, py, pw, ph, pdx, pdy, nil, (GridTagWall or GridTagDoor));
590 end
591 else
592 begin
593 if (ontop <> nil) then ontop^ := false;
594 // not standing on the platform: trace platform to see if it hits the entity
595 // first, process size change (as we cannot sweeptest both move and size change)
596 // but we don't have to check for pushing if the panel is shrinking
597 szdx := nw-mpw;
598 szdy := nh-mph;
599 if (szdx > 0) or (szdy > 0) then
600 begin
601 // ignore shrinking dimension
602 if (szdx < 0) then szdx := 0;
603 if (szdy < 0) then szdy := 0;
604 // move platform by szd* back, and check for szd* movement
605 if sweepAABB(ox-szdx, oy-szdy, nw, nh, szdx, szdy, px, py, pw, ph, @u0) then
606 begin
607 // yes, platform hits the entity, push the entity in the resizing direction
608 u0 := 1.0-u0; // how much path left?
609 szdx := trunc(szdx*u0);
610 szdy := trunc(szdy*u0);
611 if (szdx <> 0) or (szdy <> 0) then
612 begin
613 // has some path to go, trace the entity
614 trtag := (GridTagWall or GridTagDoor);
615 // if we're moving down, consider steps too
616 if (szdy > 0) then trtag := trtag or GridTagStep;
617 mapGrid.traceBox(tex, tey, px, py, pw, ph, szdx, szdy, nil, trtag);
618 end;
619 end;
620 end;
621 // second, process platform movement, using te* as entity starting point
622 if sweepAABB(ox, oy, nw, nh, pdx, pdy, tex, tey, pw, ph, @u0) then
623 begin
624 //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]);
625 // yes, platform hits the entity, push the entity in the direction of the platform
626 u0 := 1.0-u0; // how much path left?
627 pdx := trunc(pdx*u0);
628 pdy := trunc(pdy*u0);
629 //e_LogWritefln(' platsweep; uleft=%s; pd=(%s,%s)', [u0, pdx, pdy]);
630 if (pdx <> 0) or (pdy <> 0) then
631 begin
632 // has some path to go, trace the entity
633 trtag := (GridTagWall or GridTagDoor);
634 // if we're moving down, consider steps too
635 if (pdy > 0) then trtag := trtag or GridTagStep;
636 mapGrid.traceBox(tex, tey, px, py, pw, ph, pdx, pdy, nil, trtag);
637 end;
638 end;
639 end;
640 // done with entity movement, new coords are in te*
641 dx := tex-px;
642 dy := tey-py;
643 result := (dx <> 0) or (dy <> 0);
644 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
645 begin
646 // check for squashing; as entity cannot be pushed into a wall, check only collision with the platform itself
647 squash := g_Collide(tex, tey, pw, ph, nx, ny, nw, nh); // squash, if still in platform
648 end;
649 end;
651 function monCollect (mon: TMonster): Boolean;
652 begin
653 result := false; // don't stop
654 if (monCheckListUsed >= Length(monCheckList)) then SetLength(monCheckList, monCheckListUsed+128);
655 monCheckList[monCheckListUsed] := mon;
656 Inc(monCheckListUsed);
657 end;
659 var
660 cx0, cy0, cx1, cy1, cw, ch: Integer;
661 f: Integer;
662 px, py, pw, ph, pdx, pdy: Integer;
663 squash: Boolean;
664 plr: TPlayer;
665 gib: PGib;
666 cor: TCorpse;
667 mon: TMonster;
668 mpfrid: LongWord;
669 ontop: Boolean;
670 actMoveTrig: Boolean;
671 actSizeTrig: Boolean;
672 begin
673 if (not Enabled) or (Width < 1) or (Height < 1) then exit;
675 if (FCurTexture >= 0) and
676 (FTextureIDs[FCurTexture].Anim) and
677 (FTextureIDs[FCurTexture].AnTex <> nil) and
678 (FAlpha < 255) then
679 begin
680 FTextureIDs[FCurTexture].AnTex.Update();
681 FCurFrame := FTextureIDs[FCurTexture].AnTex.CurrentFrame;
682 FCurFrameCount := FTextureIDs[FCurTexture].AnTex.CurrentCounter;
683 end;
685 if not g_dbgpan_mplat_active then exit;
687 if (mOldMovingActive <> mMovingActive) then mNeedSend := true;
688 mOldMovingActive := mMovingActive;
690 if not mMovingActive then exit;
691 if mMovingSpeed.isZero and mSizeSpeed.isZero then exit;
693 //TODO: write wall size change processing
695 // moving platform?
696 begin
697 (*
698 * collect all monsters and players (aka entities) along the possible platform path
699 * if entity is standing on a platform:
700 * try to move it along the platform path, checking wall collisions
701 * if entity is NOT standing on a platform, but hit with sweeped platform aabb:
702 * try to push entity
703 * if we can't push entity all the way, squash it
704 *)
705 ox := X;
706 oy := Y;
707 mpw := Width;
708 mph := Height;
710 nw := mpw+mSizeSpeed.w;
711 nh := mph+mSizeSpeed.h;
712 nx := ox+mMovingSpeed.X;
713 ny := oy+mMovingSpeed.Y;
715 // force network updates only if some sudden change happened
716 // set the flag here, so we can sync affected monsters
717 if not mSizeSpeed.isZero and (nw = mSizeEnd.w) and (nh = mSizeEnd.h) then
718 begin
719 mNeedSend := true;
720 end
721 else if ((mMovingSpeed.X < 0) and (nx <= mMovingStart.X)) or ((mMovingSpeed.X > 0) and (nx >= mMovingEnd.X)) then
722 begin
723 mNeedSend := true;
724 end
725 else if ((mMovingSpeed.Y < 0) and (ny <= mMovingStart.Y)) or ((mMovingSpeed.Y > 0) and (ny >= mMovingEnd.Y)) then
726 begin
727 mNeedSend := true;
728 end;
730 // if pannel disappeared, we don't have to do anything
731 if (nw > 0) and (nh > 0) then
732 begin
733 // old rect
734 ex := ox+mpw-1;
735 ey := ox+mph-1;
736 // new rect
737 nex := nx+nw-1;
738 ney := ny+nh-1;
739 // full rect
740 cx0 := nmin(ox, nx);
741 cy0 := nmin(oy, ny);
742 cx1 := nmax(ex, nex);
743 cy1 := nmax(ey, ney);
744 // extrude
745 cx0 -= 1;
746 cy0 -= 1;
747 cx1 += 1;
748 cy1 += 1;
749 cw := cx1-cx0+1;
750 ch := cy1-cy0+1;
752 // process "obstacle" panels
753 if ((tag and GridTagObstacle) <> 0) then
754 begin
755 // temporarily turn off this panel, so it won't interfere with collision checks
756 mapGrid.proxyEnabled[proxyId] := false;
758 // process players
759 for f := 0 to High(gPlayers) do
760 begin
761 plr := gPlayers[f];
762 if (plr = nil) or (not plr.alive) then continue;
763 plr.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) then
766 begin
767 // set new position
768 plr.moveBy(pdx, pdy); // this will call `positionChanged()` for us
769 end;
770 // squash player, if necessary
771 if not g_Game_IsClient and squash then plr.Damage(15000, 0, 0, 0, HIT_TRAP);
772 end;
774 // process gibs
775 for f := 0 to High(gGibs) do
776 begin
777 gib := @gGibs[f];
778 if not gib.alive then continue;
779 gib.getMapBox(px, py, pw, ph);
780 if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue;
781 if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash, @ontop) then
782 begin
783 // set new position
784 gib.moveBy(pdx, pdy); // this will call `positionChanged()` for us
785 end;
786 end;
788 // move and push corpses
789 for f := 0 to High(gCorpses) do
790 begin
791 cor := gCorpses[f];
792 if (cor = nil) then continue;
793 cor.getMapBox(px, py, pw, ph);
794 if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue;
795 if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash, @ontop) then
796 begin
797 // set new position
798 cor.moveBy(pdx, pdy); // this will call `positionChanged()` for us
799 end;
800 end;
802 // collect monsters
803 monCheckListUsed := 0;
804 g_Mons_ForEachAt(cx0, cy0, cw, ch, monCollect);
806 // process collected monsters
807 if (monCheckListUsed > 0) then
808 begin
809 mpfrid := g_Mons_getNewMPlatFrameId();
810 for f := 0 to monCheckListUsed do
811 begin
812 mon := monCheckList[f];
813 if (mon = nil) or (not mon.alive) or (mon.mplatCheckFrameId = mpfrid) then continue;
814 mon.mplatCheckFrameId := mpfrid;
815 mon.getMapBox(px, py, pw, ph);
816 //if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue;
817 if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash) then
818 begin
819 // set new position
820 mon.moveBy(pdx, pdy); // this will call `positionChanged()` for us
821 //???FIXME: do we really need to send monsters over the net?
822 // i don't think so, as dead reckoning should take care of 'em
823 // ok, send new monster position only if platform is going to change it's direction
824 if mNeedSend then mon.setDirty();
825 end;
826 // squash monster, if necessary
827 if not g_Game_IsClient and squash then mon.Damage(15000, 0, 0, 0, HIT_TRAP);
828 end;
829 end;
831 // restore panel state
832 mapGrid.proxyEnabled[proxyId] := true;
833 end;
834 end;
836 // move panel
837 X := nx;
838 Y := ny;
839 FWidth := nw;
840 FHeight := nh;
841 positionChanged();
843 actMoveTrig := false;
844 actSizeTrig := false;
846 // `mNeedSend` was set above
848 // check "size stop"
849 if not mSizeSpeed.isZero and (nw = mSizeEnd.w) and (nh = mSizeEnd.h) then
850 begin
851 mSizeSpeed.w := 0;
852 mSizeSpeed.h := 0;
853 actSizeTrig := true;
854 if (nw < 1) or (nh < 1) then mMovingActive := false; //HACK!
855 end;
857 // reverse moving direction, if necessary
858 if ((mMovingSpeed.X < 0) and (nx <= mMovingStart.X)) or ((mMovingSpeed.X > 0) and (nx >= mMovingEnd.X)) then
859 begin
860 if mMoveOnce then mMovingActive := false else mMovingSpeed.X := -mMovingSpeed.X;
861 actMoveTrig := true;
862 end;
864 if ((mMovingSpeed.Y < 0) and (ny <= mMovingStart.Y)) or ((mMovingSpeed.Y > 0) and (ny >= mMovingEnd.Y)) then
865 begin
866 if mMoveOnce then mMovingActive := false else mMovingSpeed.Y := -mMovingSpeed.Y;
867 actMoveTrig := true;
868 end;
870 if (mOldMovingActive <> mMovingActive) then mNeedSend := true;
871 mOldMovingActive := mMovingActive;
873 if not g_Game_IsClient then
874 begin
875 if actMoveTrig then g_Triggers_Press(mEndPosTrig, ACTIVATE_CUSTOM);
876 if actSizeTrig then g_Triggers_Press(mEndSizeTrig, ACTIVATE_CUSTOM);
877 end;
879 // some triggers may activate this, don't delay sending
880 //TODO: when triggers will be able to control speed and size, check that here too
881 if (mOldMovingActive <> mMovingActive) then mNeedSend := true;
882 mOldMovingActive := mMovingActive;
883 end;
884 end;
887 procedure TPanel.SetFrame(Frame: Integer; Count: Byte);
889 function ClampInt(X, A, B: Integer): Integer;
890 begin
891 Result := X;
892 if X < A then Result := A else if X > B then Result := B;
893 end;
895 begin
896 if Enabled and (FCurTexture >= 0) and
897 (FTextureIDs[FCurTexture].Anim) and
898 (FTextureIDs[FCurTexture].AnTex <> nil) and
899 (Width > 0) and (Height > 0) and (FAlpha < 255) then
900 begin
901 FCurFrame := ClampInt(Frame, 0, FTextureIDs[FCurTexture].AnTex.TotalFrames);
902 FCurFrameCount := Count;
903 FTextureIDs[FCurTexture].AnTex.CurrentFrame := FCurFrame;
904 FTextureIDs[FCurTexture].AnTex.CurrentCounter := FCurFrameCount;
905 end;
906 end;
908 procedure TPanel.NextTexture(AnimLoop: Byte = 0);
909 begin
910 Assert(FCurTexture >= -1, 'FCurTexture < -1');
912 // Íåò òåêñòóð:
913 if Length(FTextureIDs) = 0 then
914 FCurTexture := -1
915 else
916 // Òîëüêî îäíà òåêñòóðà:
917 if Length(FTextureIDs) = 1 then
918 begin
919 if FCurTexture = 0 then
920 FCurTexture := -1
921 else
922 FCurTexture := 0;
923 end
924 else
925 // Áîëüøå îäíîé òåêñòóðû:
926 begin
927 // Ñëåäóþùàÿ:
928 Inc(FCurTexture);
929 // Ñëåäóþùåé íåò - âîçâðàò ê íà÷àëó:
930 if FCurTexture >= Length(FTextureIDs) then
931 FCurTexture := 0;
932 end;
934 // Ïåðåêëþ÷èëèñü íà âèäèìóþ àíèì. òåêñòóðó:
935 if (FCurTexture >= 0) and FTextureIDs[FCurTexture].Anim then
936 begin
937 if (FTextureIDs[FCurTexture].AnTex = nil) then
938 begin
939 g_FatalError(_lc[I_GAME_ERROR_SWITCH_TEXTURE]);
940 Exit;
941 end;
943 if AnimLoop = 1 then
944 FTextureIDs[FCurTexture].AnTex.Loop := True
945 else
946 if AnimLoop = 2 then
947 FTextureIDs[FCurTexture].AnTex.Loop := False;
949 FTextureIDs[FCurTexture].AnTex.Reset();
950 end;
952 LastAnimLoop := AnimLoop;
953 end;
955 procedure TPanel.SetTexture(ID: Integer; AnimLoop: Byte = 0);
956 begin
957 // Íåò òåêñòóð:
958 if Length(FTextureIDs) = 0 then
959 FCurTexture := -1
960 else
961 // Òîëüêî îäíà òåêñòóðà:
962 if Length(FTextureIDs) = 1 then
963 begin
964 if (ID = 0) or (ID = -1) then
965 FCurTexture := ID;
966 end
967 else
968 // Áîëüøå îäíîé òåêñòóðû:
969 begin
970 if (ID >= -1) and (ID <= High(FTextureIDs)) then
971 FCurTexture := ID;
972 end;
974 // Ïåðåêëþ÷èëèñü íà âèäèìóþ àíèì. òåêñòóðó:
975 if (FCurTexture >= 0) and FTextureIDs[FCurTexture].Anim then
976 begin
977 if (FTextureIDs[FCurTexture].AnTex = nil) then
978 begin
979 g_FatalError(_lc[I_GAME_ERROR_SWITCH_TEXTURE]);
980 Exit;
981 end;
983 if AnimLoop = 1 then
984 FTextureIDs[FCurTexture].AnTex.Loop := True
985 else
986 if AnimLoop = 2 then
987 FTextureIDs[FCurTexture].AnTex.Loop := False;
989 FTextureIDs[FCurTexture].AnTex.Reset();
990 end;
992 LastAnimLoop := AnimLoop;
993 end;
995 function TPanel.GetTextureID(): DWORD;
996 begin
997 Result := LongWord(TEXTURE_NONE);
999 if (FCurTexture >= 0) then
1000 begin
1001 if FTextureIDs[FCurTexture].Anim then
1002 Result := FTextureIDs[FCurTexture].AnTex.FramesID
1003 else
1004 Result := FTextureIDs[FCurTexture].Tex;
1005 end;
1006 end;
1008 function TPanel.GetTextureCount(): Integer;
1009 begin
1010 Result := Length(FTextureIDs);
1011 if Enabled and (FCurTexture >= 0) then
1012 if (FTextureIDs[FCurTexture].Anim) and
1013 (FTextureIDs[FCurTexture].AnTex <> nil) and
1014 (Width > 0) and (Height > 0) and (FAlpha < 255) then
1015 Result := Result + 100;
1016 end;
1019 const
1020 PAN_SAVE_VERSION = 1;
1022 procedure TPanel.SaveState (st: TStream);
1023 var
1024 anim: Boolean;
1025 begin
1026 if (st = nil) then exit;
1028 // Ñèãíàòóðà ïàíåëè
1029 utils.writeSign(st, 'PANL');
1030 utils.writeInt(st, Byte(PAN_SAVE_VERSION));
1031 // Îòêðûòà/çàêðûòà, åñëè äâåðü
1032 utils.writeBool(st, FEnabled);
1033 // Íàïðàâëåíèå ëèôòà, åñëè ëèôò
1034 utils.writeInt(st, Byte(FLiftType));
1035 // Íîìåð òåêóùåé òåêñòóðû
1036 utils.writeInt(st, Integer(FCurTexture));
1037 // Êîîðäèíàòû è ðàçìåð
1038 utils.writeInt(st, Integer(FX));
1039 utils.writeInt(st, Integer(FY));
1040 utils.writeInt(st, Word(FWidth));
1041 utils.writeInt(st, Word(FHeight));
1042 // Àíèìèðîâàíà ëè òåêóùàÿ òåêñòóðà
1043 if (FCurTexture >= 0) and (FTextureIDs[FCurTexture].Anim) then
1044 begin
1045 assert(FTextureIDs[FCurTexture].AnTex <> nil, 'TPanel.SaveState: No animation object');
1046 anim := true;
1047 end
1048 else
1049 begin
1050 anim := false;
1051 end;
1052 utils.writeBool(st, anim);
1053 // Åñëè äà - ñîõðàíÿåì àíèìàöèþ
1054 if anim then FTextureIDs[FCurTexture].AnTex.SaveState(st);
1056 // moving platform state
1057 utils.writeInt(st, Integer(mMovingSpeed.X));
1058 utils.writeInt(st, Integer(mMovingSpeed.Y));
1059 utils.writeInt(st, Integer(mMovingStart.X));
1060 utils.writeInt(st, Integer(mMovingStart.Y));
1061 utils.writeInt(st, Integer(mMovingEnd.X));
1062 utils.writeInt(st, Integer(mMovingEnd.Y));
1064 utils.writeInt(st, Integer(mSizeSpeed.w));
1065 utils.writeInt(st, Integer(mSizeSpeed.h));
1066 utils.writeInt(st, Integer(mSizeEnd.w));
1067 utils.writeInt(st, Integer(mSizeEnd.h));
1069 utils.writeBool(st, mMovingActive);
1070 utils.writeBool(st, mMoveOnce);
1072 utils.writeInt(st, Integer(mEndPosTrig));
1073 utils.writeInt(st, Integer(mEndSizeTrig));
1074 end;
1077 procedure TPanel.LoadState (st: TStream);
1078 begin
1079 if (st = nil) then exit;
1081 // Ñèãíàòóðà ïàíåëè
1082 if not utils.checkSign(st, 'PANL') then raise XStreamError.create('wrong panel signature');
1083 if (utils.readByte(st) <> PAN_SAVE_VERSION) then raise XStreamError.create('wrong panel version');
1084 // Îòêðûòà/çàêðûòà, åñëè äâåðü
1085 FEnabled := utils.readBool(st);
1086 // Íàïðàâëåíèå ëèôòà, åñëè ëèôò
1087 FLiftType := utils.readByte(st);
1088 // Íîìåð òåêóùåé òåêñòóðû
1089 FCurTexture := utils.readLongInt(st);
1090 // Êîîðäèíàòû è ðàçìåð
1091 FX := utils.readLongInt(st);
1092 FY := utils.readLongInt(st);
1093 FWidth := utils.readWord(st);
1094 FHeight := utils.readWord(st);
1095 // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà
1096 if utils.readBool(st) then
1097 begin
1098 // Åñëè äà - çàãðóæàåì àíèìàöèþ
1099 Assert((FCurTexture >= 0) and
1100 (FTextureIDs[FCurTexture].Anim) and
1101 (FTextureIDs[FCurTexture].AnTex <> nil),
1102 'TPanel.LoadState: No animation object');
1103 FTextureIDs[FCurTexture].AnTex.LoadState(st);
1104 end;
1106 // moving platform state
1107 mMovingSpeed.X := utils.readLongInt(st);
1108 mMovingSpeed.Y := utils.readLongInt(st);
1109 mMovingStart.X := utils.readLongInt(st);
1110 mMovingStart.Y := utils.readLongInt(st);
1111 mMovingEnd.X := utils.readLongInt(st);
1112 mMovingEnd.Y := utils.readLongInt(st);
1114 mSizeSpeed.w := utils.readLongInt(st);
1115 mSizeSpeed.h := utils.readLongInt(st);
1116 mSizeEnd.w := utils.readLongInt(st);
1117 mSizeEnd.h := utils.readLongInt(st);
1119 mMovingActive := utils.readBool(st);
1120 mMoveOnce := utils.readBool(st);
1122 mEndPosTrig := utils.readLongInt(st);
1123 mEndSizeTrig := utils.readLongInt(st);
1125 positionChanged();
1126 //mapGrid.proxyEnabled[proxyId] := FEnabled; // done in g_map.pas
1127 end;
1130 end.