summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 638c2b7)
raw | patch | inline | side by side (parent: 638c2b7)
author | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Wed, 6 Sep 2017 00:52:18 +0000 (03:52 +0300) | ||
committer | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Wed, 6 Sep 2017 00:55:28 +0000 (03:55 +0300) |
there was a bug with panel syncing on join (introduced by me earlier);
it is fixed now.
internally, server only sends new panel state when something "interesting"
happens (active state changed, direction changed, etc.). in this case,
server will also send monster positions for affected monsters (just in case).
otherwise, dead reckoning should do it's work.
maybe it will be better to spam network with panel/monster state constantly,
i don't know. for now, players with huge lag can be suddenly squashed by
unsynced mplat. this should be checked with real network games.
note that network protocol will likely be changed again when we'll get final
specs for mplats.
it is fixed now.
internally, server only sends new panel state when something "interesting"
happens (active state changed, direction changed, etc.). in this case,
server will also send monster positions for affected monsters (just in case).
otherwise, dead reckoning should do it's work.
maybe it will be better to spam network with panel/monster state constantly,
i don't know. for now, players with huge lag can be suddenly squashed by
unsynced mplat. this should be checked with real network games.
note that network protocol will likely be changed again when we'll get final
specs for mplats.
diff --git a/src/game/g_game.pas b/src/game/g_game.pas
index c83fb8e455e06decf3ce5a07a13f94a8aa06a33c..3be96517735e7b8f12b98fe79a453dadde11c3e2 100644 (file)
--- a/src/game/g_game.pas
+++ b/src/game/g_game.pas
function sendMonsPos (mon: TMonster): Boolean;
begin
result := false; // don't stop
- if (mon.MonsterType = MONSTER_BARREL) then
+ // this will also reset "need-send" flag
+ if mon.gncNeedSend then
+ begin
+ MH_SEND_MonsterPos(mon.UID);
+ end
+ else if (mon.MonsterType = MONSTER_BARREL) then
begin
if (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
end
- else
- if (mon.MonsterState <> MONSTATE_SLEEP) then
- begin
- if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then
- begin
- MH_SEND_MonsterPos(mon.UID);
- end;
- end;
+ else if (mon.MonsterState <> MONSTATE_SLEEP) then
+ begin
+ if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
+ end;
end;
+ function sendMonsPosUnexpected (mon: TMonster): Boolean;
+ begin
+ result := false; // don't stop
+ // this will also reset "need-send" flag
+ if mon.gncNeedSend then MH_SEND_MonsterPos(mon.UID);
+ end;
+
+var
+ reliableUpdate: Boolean;
begin
g_ResetDynlights();
// Ïîðà âûêëþ÷àòü èãðó:
g_GFX_Update();
g_Player_UpdateAll();
g_Player_UpdatePhysicalObjects();
- if gGameSettings.GameType = GT_SERVER then
- if Length(gMonstersSpawned) > 0 then
+
+ // server: send newly spawned monsters unconditionally
+ if (gGameSettings.GameType = GT_SERVER) then
+ begin
+ if (Length(gMonstersSpawned) > 0) then
begin
- for I := 0 to High(gMonstersSpawned) do
- MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
+ for I := 0 to High(gMonstersSpawned) do MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
SetLength(gMonstersSpawned, 0);
end;
+ end;
if (gSoundTriggerTime > 8) then
begin
gSoundTriggerTime := 0;
end
else
+ begin
Inc(gSoundTriggerTime);
+ end;
if (NetMode = NET_SERVER) then
begin
Inc(NetTimeToUpdate);
Inc(NetTimeToReliable);
- if NetTimeToReliable >= NetRelupdRate then
+
+ // send monster updates
+ if (NetTimeToReliable >= NetRelupdRate) or (NetTimeToUpdate >= NetUpdateRate) then
begin
+ // send all monsters (periodic sync)
+ reliableUpdate := (NetTimeToReliable >= NetRelupdRate);
+
for I := 0 to High(gPlayers) do
- if gPlayers[I] <> nil then
- MH_SEND_PlayerPos(True, gPlayers[I].UID);
+ begin
+ if (gPlayers[I] <> nil) then MH_SEND_PlayerPos(reliableUpdate, gPlayers[I].UID);
+ end;
g_Mons_ForEach(sendMonsPos);
- NetTimeToReliable := 0;
- NetTimeToUpdate := NetUpdateRate;
+ if reliableUpdate then
+ begin
+ NetTimeToReliable := 0;
+ NetTimeToUpdate := NetUpdateRate;
+ end
+ else
+ begin
+ NetTimeToUpdate := 0;
+ end;
end
- else if NetTimeToUpdate >= NetUpdateRate then
+ else
begin
- if gPlayers <> nil then
- for I := 0 to High(gPlayers) do
- if gPlayers[I] <> nil then
- MH_SEND_PlayerPos(False, gPlayers[I].UID);
-
- g_Mons_ForEach(sendMonsPos);
-
- NetTimeToUpdate := 0;
+ // send only mosters with some unexpected changes
+ g_Mons_ForEach(sendMonsPosUnexpected);
end;
+ // send unexpected platform changes
+ g_Map_NetSendInterestingPanels();
+
if NetUseMaster then
+ begin
if gTime >= NetTimeToMaster then
begin
if (NetMHost = nil) or (NetMPeer = nil) then
- if not g_Net_Slist_Connect then
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
+ begin
+ if not g_Net_Slist_Connect then g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
+ end;
g_Net_Slist_Update;
NetTimeToMaster := gTime + NetMasterRate;
end;
+ end;
end
- else
- if NetMode = NET_CLIENT then
- MC_SEND_PlayerPos();
+ else if (NetMode = NET_CLIENT) then
+ begin
+ MC_SEND_PlayerPos();
+ end;
end; // if gameOn ...
// Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
diff --git a/src/game/g_map.pas b/src/game/g_map.pas
index 101c3c544d06658454afb94777d730c333832545..6cfd0da3b578ccd8d20f6ca93fe46e1637dcca37 100644 (file)
--- a/src/game/g_map.pas
+++ b/src/game/g_map.pas
function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
type
- TForEachPanelCB = function (pan: TPanel): Boolean; // return `true` to stop
+ TForEachPanelCB = function (pan: TPanel): Boolean is nested; // return `true` to stop
function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel;
function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
+// return `true` from `cb` to stop
+function g_Map_ForEachPanel (cb: TForEachPanelCB): TPanel;
+
+procedure g_Map_NetSendInterestingPanels (); // yay!
+
+
procedure g_Map_ProfilersBegin ();
procedure g_Map_ProfilersEnd ();
end;
+// return `true` from `cb` to stop
+function g_Map_ForEachPanel (cb: TForEachPanelCB): TPanel;
+var
+ pan: TPanel;
+begin
+ result := nil;
+ if not assigned(cb) then exit;
+ for pan in panByGUID do
+ begin
+ if cb(pan) then begin result := pan; exit; end;
+ end;
+end;
+
+
+procedure g_Map_NetSendInterestingPanels ();
+var
+ pan: TPanel;
+begin
+ if g_Game_IsServer and g_Game_IsNet then
+ begin
+ for pan in panByGUID do
+ begin
+ if pan.gncNeedSend then MH_SEND_PanelState(pan.panelType, pan.guid);
+ end;
+ end;
+end;
+
+
// ////////////////////////////////////////////////////////////////////////// //
function g_Map_MinX (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridX0 else result := 0; end;
function g_Map_MinY (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridY0 else result := 0; end;
//pan := gWalls[ID];
pan := g_Map_PanelByGUID(pguid);
if (pan = nil) then exit;
+ if pan.Enabled and mapGrid.proxyEnabled[pan.proxyId] then exit;
+
pan.Enabled := True;
g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, true);
//if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := true
//else pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, GridTagDoor);
- if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState({gWalls[ID]}pan.PanelType, pguid);
+ //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState({gWalls[ID]}pan.PanelType, pguid);
+ // mark platform as interesting
+ pan.setDirty();
{$IFDEF MAP_DEBUG_ENABLED_FLAG}
//e_WriteLog(Format('ENABLE: wall #%d(%d) enabled (%d) (%d,%d)-(%d,%d)', [Integer(ID), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.x, pan.y, pan.width, pan.height]), MSG_NOTIFY);
//pan := gWalls[ID];
pan := g_Map_PanelByGUID(pguid);
if (pan = nil) then exit;
+ if (not pan.Enabled) and (not mapGrid.proxyEnabled[pan.proxyId]) then exit;
+
pan.Enabled := False;
g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, false);
mapGrid.proxyEnabled[pan.proxyId] := false;
//if (pan.proxyId >= 0) then begin mapGrid.removeBody(pan.proxyId); pan.proxyId := -1; end;
- if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pan.PanelType, pguid);
+ //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pan.PanelType, pguid);
+ // mark platform as interesting
+ pan.setDirty();
{$IFDEF MAP_DEBUG_ENABLED_FLAG}
//e_WriteLog(Format('DISABLE: wall #%d(%d) disabled (%d) (%d,%d)-(%d,%d)', [Integer(ID), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.x, pan.y, pan.width, pan.height]), MSG_NOTIFY);
3: g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT);
end;
- if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, pguid);
+ //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, pguid);
+ // mark platform as interesting
+ pan.setDirty();
end;
end;
index 13dfb21ab6e307ce5858967c9bf307d32283c416..accdcb7ee23d5892e4f0fc39ebdfbe52ec3509ea 100644 (file)
--- a/src/game/g_monsters.pas
+++ b/src/game/g_monsters.pas
FDieTriggers: Array of Integer;
FSpawnTrigger: Integer;
- mNeedSend: Boolean; // for networl
+ mNeedSend: Boolean; // for network
procedure Turn();
function findNewPrey(): Boolean;
{$ENDIF}
if (mProxyId = -1) then
begin
- mNeedSend := true;
+ //mNeedSend := true;
mProxyId := monsGrid.insertBody(self, FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height);
{$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
monsGrid.getBodyXY(mProxyId, x, y);
if (w <> nw) or (h <> nh) then
begin
- mNeedSend := true;
+ //mNeedSend := true;
{$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
e_WriteLog(Format('monster #%d:(%u): resized; mProxyid=%d; gx=%d; gy=%d', [mArrIdx, UID, mProxyId, x-monsGrid.gridX0, y-monsGrid.gridY0]), MSG_NOTIFY);
{$ENDIF}
end
else if (x <> nx) or (y <> ny) then
begin
- mNeedSend := true;
+ //mNeedSend := true;
{$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
e_WriteLog(Format('monster #%d:(%u): updating grid; mProxyid=%d; gx=%d; gy=%d', [mArrIdx, UID, mProxyId, x-monsGrid.gridX0, y-monsGrid.gridY0]), MSG_NOTIFY);
{$ENDIF}
diff --git a/src/game/g_net.pas b/src/game/g_net.pas
index 16e6bb511d3c2437e8d0bd76da86a26fec99343b..95f96739e62eea4e74a3d662097d2421261c1a50 100644 (file)
--- a/src/game/g_net.pas
+++ b/src/game/g_net.pas
e_log, e_msg, ENet, Classes;
const
- NET_PROTOCOL_VER = 172;
+ NET_PROTOCOL_VER = 173;
NET_MAXCLIENTS = 24;
NET_CHANS = 11;
diff --git a/src/game/g_netmsg.pas b/src/game/g_netmsg.pas
index 21046a048064e930129811bc684715206448f37c..8b9aa1e05da1bb65c9ac50872d49f3dd4fd944bc 100644 (file)
--- a/src/game/g_netmsg.pas
+++ b/src/game/g_netmsg.pas
@@ -640,6 +640,13 @@ procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_E
MH_SEND_MonsterSpawn(mon.UID, ID);
end;
+ function sendPanelState (pan: TPanel): Boolean;
+ begin
+ result := false; // don't stop
+ MH_SEND_PanelState(pan.PanelType, pan.guid, ID); // anyway, to sync mplats
+ if (pan.GetTextureCount > 1) then MH_SEND_PanelTexture(pan.PanelType, pan.guid, pan.LastAnimLoop, ID);
+ end;
+
var
I: Integer;
begin
MH_SEND_PlayerStats(gPlayers[I].UID, ID);
if (gPlayers[I].Flag <> FLAG_NONE) and (gGameSettings.GameMode = GM_CTF) then
+ begin
MH_SEND_FlagEvent(FLAG_STATE_CAPTURED, gPlayers[I].Flag, gPlayers[I].UID, True, ID);
+ end;
end;
end;
end;
g_Items_ForEachAlive(sendItemRespawn, true); // backwards
g_Mons_ForEach(sendMonSpawn);
+ g_Map_ForEachPanel(sendPanelState);
+
+ (* replaced with the `g_Map_ForEachPanel()` call above
if gWalls <> nil then
for I := Low(gWalls) to High(gWalls) do
if gWalls[I] <> nil then
with gWalls[I] do
begin
- if Door then
+ {if Door then} // anyway, to sync mplats
MH_SEND_PanelState(PanelType, I, ID);
if GetTextureCount > 1 then
begin
if (GetTextureCount > 1) then
MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
- if Moved then
+ {if Moved then} // anyway, to sync mplats
MH_SEND_PanelState(PanelType, I, ID);
end;
if gRenderBackgrounds <> nil then
begin
if (GetTextureCount > 1) then
MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
- if Moved then
+ {if Moved then} // anyway, to sync mplats
MH_SEND_PanelState(PanelType, I, ID);
end;
if gWater <> nil then
for I := Low(gWater) to High(gWater) do
if gWater[I] <> nil then
with gWater[I] do
+ begin
if GetTextureCount > 1 then
MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
+ {if Moved then} // anyway, to sync mplats
+ MH_SEND_PanelState(PanelType, I, ID);
+ end;
if gAcid1 <> nil then
for I := Low(gAcid1) to High(gAcid1) do
if gAcid1[I] <> nil then
with gAcid1[I] do
+ begin
if GetTextureCount > 1 then
MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
+ {if Moved then} // anyway, to sync mplats
+ MH_SEND_PanelState(PanelType, I, ID);
+ end;
if gAcid2 <> nil then
for I := Low(gAcid2) to High(gAcid2) do
if gAcid2[I] <> nil then
with gAcid2[I] do
+ begin
if GetTextureCount > 1 then
MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
+ {if Moved then} // anyway, to sync mplats
+ MH_SEND_PanelState(PanelType, I, ID);
+ end;
if gSteps <> nil then
for I := Low(gSteps) to High(gSteps) do
if gSteps[I] <> nil then
with gSteps[I] do
+ begin
if GetTextureCount > 1 then
MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
+ {if Moved then} // anyway, to sync mplats
+ MH_SEND_PanelState(PanelType, I, ID);
+ end;
+ *)
if gTriggers <> nil then
for I := Low(gTriggers) to High(gTriggers) do
procedure MH_SEND_PanelState(PType: Word; PGUID: Integer; ID: Integer = NET_EVERYONE);
var
TP: TPanel;
+ mpflags: Byte = 0;
begin
TP := g_Map_PanelByGUID(PGUID);
if (TP = nil) then exit;
NetOut.Write(TP.LiftType);
NetOut.Write(TP.X);
NetOut.Write(TP.Y);
+ NetOut.Write(Word(TP.Width));
+ NetOut.Write(Word(TP.Height));
+ // mplats
+ NetOut.Write(LongInt(TP.movingSpeedX));
+ NetOut.Write(LongInt(TP.movingSpeedY));
+ NetOut.Write(LongInt(TP.movingStartX));
+ NetOut.Write(LongInt(TP.movingStartY));
+ NetOut.Write(LongInt(TP.movingEndX));
+ NetOut.Write(LongInt(TP.movingEndY));
+ NetOut.Write(LongInt(TP.sizeSpeedX));
+ NetOut.Write(LongInt(TP.sizeSpeedY));
+ NetOut.Write(LongInt(TP.sizeEndX));
+ NetOut.Write(LongInt(TP.sizeEndY));
+ if TP.movingActive then mpflags := mpflags or 1;
+ if TP.moveOnce then mpflags := mpflags or 2;
+ NetOut.Write(Byte(mpflags));
g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
end;
end;
}
- if TP <> nil then
+ if (TP <> nil) then
begin
if Loop = 0 then
begin
E: Boolean;
Lift: Byte;
PType: Word;
- X, Y: Integer;
+ X, Y, W, H: Integer;
TP: TPanel;
+ speedX, speedY, startX, startY, endX, endY: Integer;
+ sizeSpX, sizeSpY, sizeEX, sizeEY: Integer;
+ mpflags: Byte;
begin
if not gGameOn then Exit;
PType := M.ReadWord();
Lift := M.ReadByte();
X := M.ReadLongInt();
Y := M.ReadLongInt();
+ W := M.ReadWord();
+ H := M.ReadWord();
+ // mplats
+ speedX := M.ReadLongInt();
+ speedY := M.ReadLongInt();
+ startX := M.ReadLongInt();
+ startY := M.ReadLongInt();
+ endX := M.ReadLongInt();
+ endY := M.ReadLongInt();
+ sizeSpX := M.ReadLongInt();
+ sizeSpY := M.ReadLongInt();
+ sizeEX := M.ReadLongInt();
+ sizeEY := M.ReadLongInt();
+ mpflags := M.ReadByte(); // bit0: TP.movingActive; bit1: TP.moveOnce
case PType of
- PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
- if E then g_Map_EnableWallGUID(PGUID) else g_Map_DisableWallGUID(PGUID);
+ {PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
+ if E then g_Map_EnableWallGUID(PGUID) else g_Map_DisableWallGUID(PGUID);}
PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
g_Map_SetLiftGUID(PGUID, Lift);
{PANEL_BACK,
PANEL_FORE:}
+ {
else
begin
TP := g_Map_PanelByGUID(PGUID);
//gRenderBackgrounds[ID].X := X;
//gRenderBackgrounds[ID].Y := Y;
end;
+ }
{
PANEL_FORE:
end;
}
end;
+
+ // update enabled/disabled state for all panels
+ if E then g_Map_EnableWallGUID(PGUID) else g_Map_DisableWallGUID(PGUID);
+
+ // update panel position, as it can be moved
+ TP := g_Map_PanelByGUID(PGUID);
+ if (TP <> nil) then
+ begin
+ // mplat
+ TP.movingSpeedX := speedX;
+ TP.movingSpeedY := speedY;
+ TP.movingStartX := startX;
+ TP.movingStartY := startY;
+ TP.movingEndX := endX;
+ TP.movingEndY := endY;
+ TP.sizeSpeedX := sizeSpX;
+ TP.sizeSpeedY := sizeSpY;
+ TP.sizeEndX := sizeEX;
+ TP.sizeEndY := sizeEY;
+ TP.movingActive := ((mpflags and 1) <> 0);
+ TP.moveOnce := ((mpflags and 2) <> 0);
+ // position
+ TP.X := X;
+ TP.Y := Y;
+ TP.Width := W;
+ TP.Height := H;
+ TP.positionChanged();
+ end;
end;
// TRIGGERS
diff --git a/src/game/g_panel.pas b/src/game/g_panel.pas
index d3101aa0e1b6e0f1c7102b458f7b61db447d2280..00407dbd68921463601b5b932ae4cd444d9d07d6 100644 (file)
--- a/src/game/g_panel.pas
+++ b/src/game/g_panel.pas
mMovingActive: Boolean;
mMoveOnce: Boolean;
+ mOldMovingActive: Boolean;
+
mSizeSpeed: TDFSize;
mSizeEnd: TDFSize;
mEndPosTrig: Integer;
mEndSizeTrig: Integer;
+ mNeedSend: Boolean; // for network
+
private
function getx1 (): Integer; inline;
function gety1 (): Integer; inline;
function getMovingEndY (): Integer; inline;
procedure setMovingEndY (v: Integer); inline;
+ function getSizeSpeedX (): Integer; inline;
+ procedure setSizeSpeedX (v: Integer); inline;
+ function getSizeSpeedY (): Integer; inline;
+ procedure setSizeSpeedY (v: Integer); inline;
+
+ function getSizeEndX (): Integer; inline;
+ procedure setSizeEndX (v: Integer); inline;
+ function getSizeEndY (): Integer; inline;
+ procedure setSizeEndY (v: Integer); inline;
+
public
FCurTexture: Integer; // Íîìåð òåêóùåé òåêñòóðû
FCurFrame: Integer;
function getIsGLift (): Boolean; inline; // gLifts
function getIsGBlockMon (): Boolean; inline; // gBlockMon
+ // get-and-clear
+ function gncNeedSend (): Boolean; inline;
+ procedure setDirty (); inline; // why `dirty`? 'cause i may introduce property `needSend` later
+
public
property visvalid: Boolean read getvisvalid; // panel is "visvalid" when it's width and height are positive
property movingActive: Boolean read mMovingActive write mMovingActive;
property moveOnce: Boolean read mMoveOnce write mMoveOnce;
+ property sizeSpeedX: Integer read getSizeSpeedX write setSizeSpeedX;
+ property sizeSpeedY: Integer read getSizeSpeedY write setSizeSpeedY;
+ property sizeEndX: Integer read getSizeEndX write setSizeEndX;
+ property sizeEndY: Integer read getSizeEndY write setSizeEndY;
+
property isGBack: Boolean read getIsGBack;
property isGStep: Boolean read getIsGStep;
property isGWall: Boolean read getIsGWall;
property movingStart: TDFPoint read mMovingStart write mMovingStart;
property movingEnd: TDFPoint read mMovingEnd write mMovingEnd;
+ property sizeSpeed: TDFSize read mSizeSpeed write mSizeSpeed;
+ property sizeEnd: TDFSize read mSizeEnd write mSizeEnd;
+
property endPosTrigId: Integer read mEndPosTrig write mEndPosTrig;
property endSizeTrigId: Integer read mEndSizeTrig write mEndSizeTrig;
end;
mMovingStart := PanelRec.moveStart;
mMovingEnd := PanelRec.moveEnd;
mMovingActive := PanelRec['move_active'].varvalue;
+ mOldMovingActive := mMovingActive;
mMoveOnce := PanelRec.moveOnce;
mSizeSpeed := PanelRec.sizeSpeed;
mEndPosTrig := PanelRec.endPosTrig;
mEndSizeTrig := PanelRec.endSizeTrig;
+ mNeedSend := false;
+
// Òèï ïàíåëè:
PanelType := PanelRec.PanelType;
Enabled := True;
@@ -386,6 +415,16 @@ procedure TPanel.setMovingEndX (v: Integer); inline; begin mMovingEnd.X := v; en
function TPanel.getMovingEndY (): Integer; inline; begin result := mMovingEnd.Y; end;
procedure TPanel.setMovingEndY (v: Integer); inline; begin mMovingEnd.Y := v; end;
+function TPanel.getSizeSpeedX (): Integer; inline; begin result := mSizeSpeed.w; end;
+procedure TPanel.setSizeSpeedX (v: Integer); inline; begin mSizeSpeed.w := v; end;
+function TPanel.getSizeSpeedY (): Integer; inline; begin result := mSizeSpeed.h; end;
+procedure TPanel.setSizeSpeedY (v: Integer); inline; begin mSizeSpeed.h := v; end;
+
+function TPanel.getSizeEndX (): Integer; inline; begin result := mSizeEnd.w; end;
+procedure TPanel.setSizeEndX (v: Integer); inline; begin mSizeEnd.w := v; end;
+function TPanel.getSizeEndY (): Integer; inline; begin result := mSizeEnd.h; end;
+procedure TPanel.setSizeEndY (v: Integer); inline; begin mSizeEnd.h := v; end;
+
function TPanel.getIsGBack (): Boolean; inline; begin result := ((tag and GridTagBack) <> 0); end;
function TPanel.getIsGStep (): Boolean; inline; begin result := ((tag and GridTagStep) <> 0); end;
function TPanel.getIsGWall (): Boolean; inline; begin result := ((tag and (GridTagWall or GridTagDoor)) <> 0); end;
@@ -396,7 +435,11 @@ function TPanel.getIsGFore (): Boolean; inline; begin result := ((tag and GridTa
function TPanel.getIsGLift (): Boolean; inline; begin result := ((tag and GridTagLift) <> 0); end;
function TPanel.getIsGBlockMon (): Boolean; inline; begin result := ((tag and GridTagBlockMon) <> 0); end;
-procedure TPanel.Draw();
+function TPanel.gncNeedSend (): Boolean; inline; begin result := mNeedSend; mNeedSend := false; end;
+procedure TPanel.setDirty (); inline; begin mNeedSend := true; end;
+
+
+procedure TPanel.Draw ();
var
xx, yy: Integer;
NoTextureID: DWORD;
@@ -480,7 +523,8 @@ procedure TPanel.DrawShadowVolume(lightX: Integer; lightY: Integer; radius: Inte
begin
if radius < 4 then exit;
- 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
+ if Enabled and (FCurTexture >= 0) and (Width > 0) and (Height > 0) and (FAlpha < 255) and
+ ((g_dbg_scale <> 1.0) or g_Collide(X, Y, Width, Height, sX, sY, sWidth, sHeight)) then
begin
if not FTextureIDs[FCurTexture].Anim then
begin
if not g_dbgpan_mplat_active then exit;
+ if (mOldMovingActive <> mMovingActive) then mNeedSend := true;
+ mOldMovingActive := mMovingActive;
+
if not mMovingActive then exit;
if mMovingSpeed.isZero and mSizeSpeed.isZero then exit;
nx := ox+mMovingSpeed.X;
ny := oy+mMovingSpeed.Y;
+ // force network updates only if some sudden change happened
+ // set the flag here, so we can sync affected monsters
+ if not mSizeSpeed.isZero and (nw = mSizeEnd.w) and (nh = mSizeEnd.h) then
+ begin
+ mNeedSend := true;
+ end
+ else if ((mMovingSpeed.X < 0) and (nx <= mMovingStart.X)) or ((mMovingSpeed.X > 0) and (nx >= mMovingEnd.X)) then
+ begin
+ mNeedSend := true;
+ end
+ else if ((mMovingSpeed.Y < 0) and (ny <= mMovingStart.Y)) or ((mMovingSpeed.Y > 0) and (ny >= mMovingEnd.Y)) then
+ begin
+ mNeedSend := true;
+ end;
+
// if pannel disappeared, we don't have to do anything
if (nw > 0) and (nh > 0) then
begin
begin
// set new position
mon.moveBy(pdx, pdy); // this will call `positionChanged()` for us
+ //???FIXME: do we really need to send monsters over the net?
+ // i don't think so, as dead reckoning should take care of 'em
+ // ok, send new monster position only if platform is going to change it's direction
+ if mNeedSend then mon.setDirty();
end;
- // squash player, if necessary
+ // squash monster, if necessary
if not g_Game_IsClient and squash then mon.Damage(15000, 0, 0, 0, HIT_TRAP);
end;
end;
actMoveTrig := false;
actSizeTrig := false;
+ // `mNeedSend` was set above
+
// check "size stop"
if not mSizeSpeed.isZero and (nw = mSizeEnd.w) and (nh = mSizeEnd.h) then
begin
mSizeSpeed.h := 0;
actSizeTrig := true;
if (nw < 1) or (nh < 1) then mMovingActive := false; //HACK!
- //e_LogWritefln('FUUUUUUUUUUUUUU', []);
end;
// reverse moving direction, if necessary
actMoveTrig := true;
end;
+ if (mOldMovingActive <> mMovingActive) then mNeedSend := true;
+ mOldMovingActive := mMovingActive;
- if actMoveTrig then g_Triggers_Press(mEndPosTrig, ACTIVATE_CUSTOM);
- if actSizeTrig then g_Triggers_Press(mEndSizeTrig, ACTIVATE_CUSTOM);
+ if g_Game_IsServer and g_Game_IsNet then
+ begin
+ if actMoveTrig then g_Triggers_Press(mEndPosTrig, ACTIVATE_CUSTOM);
+ if actSizeTrig then g_Triggers_Press(mEndSizeTrig, ACTIVATE_CUSTOM);
+ end;
+
+ // some triggers may activate this, don't delay sending
+ //TODO: when triggers will be able to control speed and size, check that here too
+ if (mOldMovingActive <> mMovingActive) then mNeedSend := true;
+ mOldMovingActive := mMovingActive;
end;
end;
Result := Result + 100;
end;
+const
+ PAN_SAVE_VERSION = 1;
+
procedure TPanel.SaveState(Var Mem: TBinMemoryWriter);
var
sig: DWORD;
anim: Boolean;
+ ver: Byte;
begin
if (Mem = nil) then exit;
//if not SaveIt then exit;
// Ñèãíàòóðà ïàíåëè:
sig := PANEL_SIGNATURE; // 'PANL'
Mem.WriteDWORD(sig);
+ ver := PAN_SAVE_VERSION;
+ Mem.WriteByte(ver);
// Îòêðûòà/çàêðûòà, åñëè äâåðü:
Mem.WriteBoolean(FEnabled);
// Íàïðàâëåíèå ëèôòà, åñëè ëèôò:
// Êîîðäû
Mem.WriteInt(FX);
Mem.WriteInt(FY);
+ Mem.WriteWord(FWidth);
+ Mem.WriteWord(FHeight);
// Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà:
if (FCurTexture >= 0) and (FTextureIDs[FCurTexture].Anim) then
begin
// Åñëè äà - ñîõðàíÿåì àíèìàöèþ:
if anim then
FTextureIDs[FCurTexture].AnTex.SaveState(Mem);
+
// moving platform state
Mem.WriteInt(mMovingSpeed.X);
Mem.WriteInt(mMovingSpeed.Y);
Mem.WriteInt(mMovingStart.Y);
Mem.WriteInt(mMovingEnd.X);
Mem.WriteInt(mMovingEnd.Y);
+
+ Mem.WriteInt(mSizeSpeed.w);
+ Mem.WriteInt(mSizeSpeed.h);
+ Mem.WriteInt(mSizeEnd.w);
+ Mem.WriteInt(mSizeEnd.h);
Mem.WriteBoolean(mMovingActive);
+ Mem.WriteBoolean(mMoveOnce);
+
+ Mem.WriteInt(mEndPosTrig);
+ Mem.WriteInt(mEndSizeTrig);
end;
procedure TPanel.LoadState(var Mem: TBinMemoryReader);
var
sig: DWORD;
anim: Boolean;
+ ver: Byte;
//ox, oy: Integer;
begin
if (Mem = nil) then exit;
// Ñèãíàòóðà ïàíåëè:
Mem.ReadDWORD(sig);
- if sig <> PANEL_SIGNATURE then // 'PANL'
- begin
- raise EBinSizeError.Create('TPanel.LoadState: Wrong Panel Signature');
- end;
+ if (sig <> PANEL_SIGNATURE) then raise EBinSizeError.Create('TPanel.LoadState: wrong panel signature'); // 'PANL'
+ Mem.ReadByte(ver);
+ if (ver <> PAN_SAVE_VERSION) then raise EBinSizeError.Create('TPanel.LoadState: invalid panel version');
// Îòêðûòà/çàêðûòà, åñëè äâåðü:
Mem.ReadBoolean(FEnabled);
// Íàïðàâëåíèå ëèôòà, åñëè ëèôò:
//oy := FY;
Mem.ReadInt(FX);
Mem.ReadInt(FY);
+ Mem.ReadWord(FWidth);
+ Mem.ReadWord(FHeight);
//e_LogWritefln('panel %s(%s): old=(%s,%s); new=(%s,%s); delta=(%s,%s)', [arrIdx, proxyId, ox, oy, FX, FY, FX-ox, FY-oy]);
// Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà:
Mem.ReadBoolean(anim);
'TPanel.LoadState: No animation object');
FTextureIDs[FCurTexture].AnTex.LoadState(Mem);
end;
+
// moving platform state
Mem.ReadInt(mMovingSpeed.X);
Mem.ReadInt(mMovingSpeed.Y);
Mem.ReadInt(mMovingStart.Y);
Mem.ReadInt(mMovingEnd.X);
Mem.ReadInt(mMovingEnd.Y);
+ Mem.ReadInt(mSizeSpeed.w);
+ Mem.ReadInt(mSizeSpeed.h);
+ Mem.ReadInt(mSizeEnd.w);
+ Mem.ReadInt(mSizeEnd.h);
Mem.ReadBoolean(mMovingActive);
+ Mem.ReadBoolean(mMoveOnce);
+
+ Mem.ReadInt(mEndPosTrig);
+ Mem.ReadInt(mEndSizeTrig);
positionChanged();
//mapGrid.proxyEnabled[proxyId] := FEnabled; // done in g_map.pas
index bf56c6eeb0afb6b227e1dcbee88595b806f49489..93c392ce8804dc298ccdbc9b9a5fa9be42037392 100644 (file)
--- a/src/game/g_saveload.pas
+++ b/src/game/g_saveload.pas
const
SAVE_SIGNATURE = $56534644; // 'DFSV'
- SAVE_VERSION = $05;
+ SAVE_VERSION = $06;
END_MARKER_STRING = 'END';
PLAYER_VIEW_SIGNATURE = $57564C50; // 'PLVW'
OBJ_SIGNATURE = $4A424F5F; // '_OBJ'