DEADSOFTWARE

added actor rendering interpolation; fixed vsync on startup plavnota
authorfgsfds <pvt.fgsfds@gmail.com>
Mon, 2 Mar 2020 21:59:55 +0000 (00:59 +0300)
committerfgsfds <pvt.fgsfds@gmail.com>
Mon, 2 Mar 2020 21:59:55 +0000 (00:59 +0300)
src/game/g_game.pas
src/game/g_gfx.pas
src/game/g_items.pas
src/game/g_monsters.pas
src/game/g_netmsg.pas
src/game/g_options.pas
src/game/g_phys.pas
src/game/g_player.pas
src/game/g_weapons.pas
src/game/g_window.pas
src/shared/utils.pas

index affc870bddbdb010228895f4b25277a4af62d0e9..b0e9e01feb36fd0bf5cbfb7e46c6c67b5134d917 100644 (file)
@@ -240,6 +240,7 @@ var
   gPlayer2: TPlayer = nil;
   gPlayerDrawn: TPlayer = nil;
   gTime: LongWord;
+  gLerpFactor: Single = 1.0;
   gSwitchGameMode: Byte = GM_DM;
   gHearPoint1, gHearPoint2: THearPoint;
   gSoundEffectsDF: Boolean = False;
@@ -2161,6 +2162,13 @@ begin
       end;
     end;
 
+  // these are in separate PreUpdate functions because they can interact during Update()
+  // we don't care that much about corpses and gibs
+    g_Player_PreUpdate();
+    g_Monsters_PreUpdate();
+    g_Items_PreUpdate();
+    g_Weapon_PreUpdate();
+
   // Îáíîâëÿåì âñå îñòàëüíîå:
     g_Map_Update();
     g_Items_Update();
@@ -3621,7 +3629,7 @@ end;
 
 procedure DrawPlayer(p: TPlayer);
 var
-  px, py, a, b, c, d, i: Integer;
+  px, py, a, b, c, d, i, fX, fY: Integer;
   //R: TRect;
 begin
   if (p = nil) or (p.FDummy) then
@@ -3639,8 +3647,9 @@ begin
 
   glPushMatrix();
 
-  px := p.GameX + PLAYER_RECT_CX;
-  py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
+  p.Obj.lerp(gLerpFactor, fX, fY);
+  px := fX + PLAYER_RECT_CX;
+  py := fY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
 
   if (g_dbg_scale = 1.0) and (not g_dbg_ignore_bounds) then
   begin
@@ -3697,7 +3706,7 @@ begin
       p.IncCam := nclamp(p.IncCam, min(0, -120 - i), 0);
   end;
 
-  sY := sY - p.IncCam;
+  sY := sY - nlerp(p.IncCamOld, p.IncCam, gLerpFactor);
 
   if (not g_dbg_ignore_bounds) then
   begin
@@ -7225,7 +7234,6 @@ begin
       end;
     'r_reset':
       begin
-        sys_EnableVSync(gVSync);
         gRC_Width := Max(1, gRC_Width);
         gRC_Height := Max(1, gRC_Height);
         gBPP := Max(1, gBPP);
@@ -7233,6 +7241,7 @@ begin
           e_LogWriteln('resolution changed')
         else
           e_LogWriteln('resolution not changed');
+        sys_EnableVSync(gVSync);
       end;
     'g_language':
       begin
index 8094c4e4e1af00e578f2f6d82d70340e75c565f0..bf8fc5aa2fd8a5e4d90b3dbd8ca47cbfb13ecfaa 100644 (file)
@@ -103,6 +103,7 @@ type
   PParticle = ^TParticle;
   TParticle = record
     x, y: Integer;
+    oldX, oldY: Integer;
     velX, velY: Single;
     accelX, accelY: Single;
     state: TPartState;
@@ -139,6 +140,7 @@ type
   TOnceAnim = record
     AnimType:   Byte;
     x, y:       Integer;
+    oldX, oldY: Integer;
     Animation:  TAnimation;
   end;
 
@@ -500,6 +502,8 @@ procedure TParticle.think (); inline;
   end;
 
 begin
+  oldx := x;
+  oldy := y;
   // awake sleeping particle, if necessary
   if awakeDirty then
   begin
@@ -979,6 +983,8 @@ begin
     begin
       x := fX-devX1+Random(devX2);
       y := fY-devY1+Random(devY2);
+      oldx := x;
+      oldy := y;
 
       // check for level bounds
       if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then continue;
@@ -1077,6 +1083,9 @@ begin
         accelY := 0.8;
       end;
 
+      oldx := x;
+      oldy := y;
+
       // check for level bounds
       if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then continue;
 
@@ -1219,6 +1228,8 @@ begin
     begin
       x := fX-devX1+Random(devX2);
       y := fY-devY1+Random(devY2);
+      oldx := x;
+      oldy := y;
 
       // check for level bounds
       if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then continue;
@@ -1388,6 +1399,8 @@ begin
     begin
       x := fX-devX1+Random(devX2);
       y := fY-devY1+Random(devY2);
+      oldx := x;
+      oldy := y;
 
       // check for level bounds
       if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then continue;
@@ -1470,6 +1483,8 @@ begin
     begin
       x := fX-devX1+Random(devX2);
       y := fY-devY1+Random(devY2);
+      oldx := x;
+      oldy := y;
 
       // check for level bounds
       if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then continue;
@@ -1649,6 +1664,9 @@ begin
     for a := 0 to High(OnceAnims) do
       if OnceAnims[a].Animation <> nil then
       begin
+        OnceAnims[a].oldx := OnceAnims[a].x;
+        OnceAnims[a].oldy := OnceAnims[a].y;
+
         case OnceAnims[a].AnimType of
           ONCEANIM_SMOKE:
             begin
@@ -1673,7 +1691,7 @@ end;
 
 procedure g_GFX_Draw ();
   var
-    a, len: Integer;
+    a, len, fx, fy: Integer;
 begin
   if not gpart_dbg_enabled then exit;
 
@@ -1698,8 +1716,10 @@ begin
         if not alive then continue;
         if (x >= sX) and (y >= sY) and (x <= sX+sWidth) and (sY <= sY+sHeight) then
         begin
+          fx := nlerp(oldx, x, gLerpFactor);
+          fy := nlerp(oldy, y, gLerpFactor);
           glColor4ub(red, green, blue, alpha);
-          glVertex2f(x+0.37, y+0.37);
+          glVertex2f(fx+0.37, fy+0.37);
         end;
       end;
     end;
@@ -1716,7 +1736,12 @@ begin
     begin
       if (OnceAnims[a].Animation <> nil) then
       begin
-        with OnceAnims[a] do Animation.Draw(x, y, TMirrorType.None);
+        with OnceAnims[a] do
+        begin
+          fx := nlerp(oldx, x, gLerpFactor);
+          fy := nlerp(oldy, y, gLerpFactor);
+          Animation.Draw(x, y, TMirrorType.None);
+        end;
       end;
     end;
   end;
index 8d476efe76e6fa3fc1c27f98644e2def961704b4..88a9cba1c433057f3ede53d4bf118ad43aecbc9c 100644 (file)
@@ -54,6 +54,7 @@ procedure g_Items_Free();
 function g_Items_Create(X, Y: Integer; ItemType: Byte;
            Fall, Respawnable: Boolean; AdjCoord: Boolean = False; ForcedID: Integer = -1): DWORD;
 procedure g_Items_SetDrop (ID: DWORD);
+procedure g_Items_PreUpdate();
 procedure g_Items_Update();
 procedure g_Items_Draw();
 procedure g_Items_DrawDrop();
@@ -462,6 +463,8 @@ begin
   it.dropped := false;
 
   g_Obj_Init(@it.Obj);
+  it.Obj.oldX := X;
+  it.Obj.oldY := Y;
   it.Obj.X := X;
   it.Obj.Y := Y;
   it.Obj.Rect.Width := ITEMSIZE[ItemType][0];
@@ -500,6 +503,18 @@ begin
   result := find_id;
 end;
 
+procedure g_Items_PreUpdate ();
+var
+  i: Integer;
+begin
+  if (ggItems = nil) then Exit;
+  for i := 0 to High(ggItems) do
+    if (ggItems[i].ItemType <> ITEM_NONE) and ggItems[i].slotIsUsed then
+    begin
+      ggItems[i].Obj.oldX := ggItems[i].Obj.X;
+      ggItems[i].Obj.oldY := ggItems[i].Obj.Y;
+    end;
+end;
 
 procedure g_Items_Update ();
 var
@@ -607,6 +622,8 @@ begin
             Anim.Free();
           end;
 
+          Obj.oldX := InitX;
+          Obj.oldY := InitY;
           Obj.X := InitX;
           Obj.Y := InitY;
           Obj.Vel.X := 0;
@@ -630,7 +647,7 @@ end;
 
 procedure itemsDrawInternal (dropflag: Boolean);
 var
-  i: Integer;
+  i, fX, fY: Integer;
   it: PItem;
 begin
   if (ggItems = nil) then exit;
@@ -646,13 +663,14 @@ begin
     begin
       if g_Collide(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height, sX, sY, sWidth, sHeight) then
       begin
+        Obj.lerp(gLerpFactor, fX, fY);
         if (Animation = nil) then
         begin
-          e_Draw(gItemsTexturesID[ItemType], Obj.X, Obj.Y, 0, true, false);
+          e_Draw(gItemsTexturesID[ItemType], fX, fY, 0, true, false);
         end
         else
         begin
-          Animation.Draw(Obj.X, Obj.Y, TMirrorType.None);
+          Animation.Draw(fX, fY, TMirrorType.None);
         end;
 
         if g_debug_Frames then
@@ -693,6 +711,8 @@ procedure g_Items_Pick (ID: DWORD);
 begin
   if (ID < Length(ggItems)) then
   begin
+    ggItems[ID].Obj.oldX := ggItems[ID].Obj.X;
+    ggItems[ID].Obj.oldY := ggItems[ID].Obj.Y;
     ggItems[ID].alive := false;
     ggItems[ID].RespawnTime := IfThen(gLMSRespawn = LMS_RESPAWN_NONE, gGameSettings.ItemRespawnTime, 15) * 36;
   end;
@@ -714,6 +734,8 @@ begin
   it := @ggItems[ID];
   if (it.arrIdx <> Integer(ID)) then raise Exception.Create('g_Items_Remove: arrIdx desync');
 
+  it.Obj.oldX := it.Obj.X;
+  it.Obj.oldY := it.Obj.Y;
   trig := it.SpawnTrigger;
 
   releaseItem(ID);
@@ -817,6 +839,8 @@ begin
   for i := 0 to High(ggItems) do
   begin
     it := @ggItems[i];
+    it.Obj.oldX := it.Obj.X;
+    it.Obj.oldY := it.Obj.Y;
     if not it.slotIsUsed then continue;
     if it.Respawnable and (it.ItemType <> ITEM_NONE) then
     begin
index ac28f340a08b8fcd694d0aee764a53a32b086c4c..02e7ed589ae6fa4b69b7b686ebd50f69ee5752af 100644 (file)
@@ -124,6 +124,7 @@ type
     function Damage(aDamage: Word; VelX, VelY: Integer; SpawnerUID: Word; t: Byte): Boolean;
     function Heal(Value: Word): Boolean;
     procedure BFGHit();
+    procedure PreUpdate();
     procedure Update();
     procedure ClientUpdate();
     procedure ClientAttack(wx, wy, atx, aty: Integer);
@@ -232,6 +233,7 @@ procedure g_Monsters_Init ();
 procedure g_Monsters_Free (clearGrid: Boolean=true);
 function g_Monsters_Create (MonsterType: Byte; X, Y: Integer; Direction: TDirection;
   AdjCoord: Boolean = False; ForcedUID: Integer = -1): TMonster;
+procedure g_Monsters_PreUpdate ();
 procedure g_Monsters_Update ();
 procedure g_Monsters_Draw ();
 procedure g_Monsters_DrawHealth ();
@@ -1377,6 +1379,8 @@ begin
     FStartDirection := Direction;
     FStartX := GameX;
     FStartY := GameY;
+    FObj.oldX := FObj.X;
+    FObj.oldY := FObj.Y;
   end;
 
   mon.positionChanged();
@@ -1412,6 +1416,16 @@ begin
   end;
 end;
 
+procedure g_Monsters_PreUpdate();
+var
+  a: Integer;
+begin
+  if gMonsters = nil then Exit;
+  for a := 0 to High(gMonsters) do
+    if (gMonsters[a] <> nil) and (not gMonsters[a].FRemoved) then
+      gMonsters[a].PreUpdate();
+end;
+
 procedure g_Monsters_Update();
 var
   a: Integer;
@@ -2209,12 +2223,14 @@ end;
 procedure TMonster.Draw();
 var
   m: TMirrorType;
-  dx, dy, c: Integer;
+  dx, dy, c, fX, fY: Integer;
   o: TObj;
 begin
   //e_CharFont_Print(gMenuSmallFont, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, 'TYPE: '+IntToStr(FMonsterType));
   //e_CharFont_Print(gMenuSmallFont, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y+16, 'STATE: '+IntToStr(FState));
 
+  FObj.lerp(gLerpFactor, fX, fY);
+
 // Åñëè êîëäóí ñòðåëÿåò, òî ðèñóåì îãîíü:
   if FMonsterType = MONSTER_VILE then
     if FState = MONSTATE_SHOOT then
@@ -2274,7 +2290,7 @@ begin
       end;
 
   // Ðèñóåì:
-    FAnim[FCurAnim, FDirection].Draw(Obj.X+dx, Obj.Y+dy, m);
+    FAnim[FCurAnim, FDirection].Draw(fX+dx, fY+dy, m);
   end;
 
   if g_debug_Frames then
@@ -2406,6 +2422,8 @@ begin
 
   FObj.X := X - FObj.Rect.X;
   FObj.Y := Y - FObj.Rect.Y;
+  FObj.oldX := FObj.X; // don't interpolate after teleport
+  FObj.oldY := FObj.Y;
   positionChanged();
 
   if dir = 1 then
@@ -2439,6 +2457,12 @@ begin
   Result := True;
 end;
 
+procedure TMonster.PreUpdate();
+begin
+  FObj.oldX := FObj.X;
+  FObj.oldY := FObj.Y;
+end;
+
 procedure TMonster.Update();
 var
   a, b, sx, sy, wx, wy, oldvelx: Integer;
index 5e95a6a57dd97b42a4d31178c153e94589092816..92084b1304662060019422a77096ad59f138a132 100644 (file)
@@ -2282,7 +2282,8 @@ begin
     GameVelY := M.ReadLongInt();
     GameAccelX := M.ReadLongInt();
     GameAccelY := M.ReadLongInt();
-    SetLerp(TmpX, TmpY);
+    GameX := TmpX;
+    GameY := TmpY;
     if NetForcePlayerUpdate then Update();
   end;
 end;
index 3f53262185953dc4fc8a1a306df54d3ef49cdd62..4a561e090c08bfa6670a3102abc6c3d5c84741c1 100644 (file)
@@ -38,6 +38,7 @@ var
   glLegacyNPOT: Boolean;
   glRenderToFBO: Boolean = True;
   gTextureFilter: Boolean;
+  gLerpActors: Boolean = True;
   gNoSound: Boolean;
   gSoundLevel: Integer;
   gMusicLevel: Integer;
@@ -327,6 +328,7 @@ initialization
   conRegVar('r_vsync', @gVSync, '', '');
   conRegVar('r_texfilter', @gTextureFilter, '', '');
   conRegVar('r_npot', @glNPOTOverride, '', '');
+  conRegVar('r_interp', @gLerpActors, '', 'interpolate actors');
 
   (* Sound *)
   conRegVar('s_nosound', @gNoSound, '', '');
index 9d93a5b560ec059d0dc6e5d984103f66acf88d57..af569ea1e1784185f70f13aa5ae47f0ef955fa1d 100644 (file)
@@ -31,6 +31,9 @@ type
     // this is purely visual change, it won't affect anything else
     slopeUpLeft: Integer; // left to go
     slopeFramesLeft: Integer; // frames left to go
+    // for frame interpolation
+    oldX, oldY: Integer;
+    procedure lerp(t: Single; out fX, fY: Integer);
   end;
 
 const
@@ -82,6 +85,11 @@ uses
 const
   SmoothSlopeFrames = 4;
 
+procedure TObj.lerp(t: Single; out fX, fY: Integer);
+begin
+  fX := nlerp(oldX, X, t);
+  fY := nlerp(oldY, Y, t);
+end;
 
 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
 begin
index 49cd657bb73ba05a37606ec76588e011be622590..db39cb84b0961e1260f840fb8a885e710648b1a8 100644 (file)
@@ -186,6 +186,7 @@ type
     FActionChanged:  Boolean;
     FAngle:     SmallInt;
     FFireAngle: SmallInt;
+    FIncCamOld:      Integer;
     FIncCam:         Integer;
     FShellTimer:     Integer;
     FShellType:      Byte;
@@ -325,6 +326,7 @@ type
     procedure   DrawIndicator(Color: TRGB);
     procedure   DrawBubble();
     procedure   DrawGUI();
+    procedure   PreUpdate();
     procedure   Update(); virtual;
     procedure   RememberState();
     procedure   RecallState();
@@ -379,6 +381,7 @@ type
     property    GameAccelX: Integer read FObj.Accel.X write FObj.Accel.X;
     property    GameAccelY: Integer read FObj.Accel.Y write FObj.Accel.Y;
     property    IncCam: Integer read FIncCam write FIncCam;
+    property    IncCamOld: Integer read FIncCamOld write FIncCamOld;
     property    UID: Word read FUID write FUID;
     property    JustTeleported: Boolean read FJustTeleported write FJustTeleported;
     property    NetTime: LongWord read FNetTime write FNetTime;
@@ -581,6 +584,7 @@ function  g_Player_Create(ModelName: String; Color: TRGB; Team: Byte; Bot: Boole
 function  g_Player_CreateFromState (st: TStream): Word;
 procedure g_Player_Remove(UID: Word);
 procedure g_Player_ResetTeams();
+procedure g_Player_PreUpdate();
 procedure g_Player_UpdateAll();
 procedure g_Player_DrawAll();
 procedure g_Player_DrawDebug(p: TPlayer);
@@ -1368,6 +1372,16 @@ begin
   SetLength(SavedStates, 0);
 end;
 
+procedure g_Player_PreUpdate();
+var
+  i: Integer;
+begin
+  if gPlayers = nil then Exit;
+  for i := 0 to High(gPlayers) do
+    if gPlayers[i] <> nil then
+      gPlayers[i].PreUpdate();
+end;
+
 procedure g_Player_UpdateAll();
 var
   i: Integer;
@@ -1713,6 +1727,9 @@ begin
       if gGibs[i].alive then
         with gGibs[i] do
         begin
+          Obj.oldX := Obj.X;
+          Obj.oldY := Obj.Y;
+
           vel := Obj.Vel;
           mr := g_Obj_Move(@Obj, True, False, True);
           positionChanged(); // this updates spatial accelerators
@@ -1763,6 +1780,9 @@ begin
       if gShells[i].alive then
         with gShells[i] do
         begin
+          Obj.oldX := Obj.X;
+          Obj.oldY := Obj.Y;
+
           vel := Obj.Vel;
           mr := g_Obj_Move(@Obj, True, False, True);
           positionChanged(); // this updates spatial accelerators
@@ -1851,7 +1871,7 @@ procedure TShell.positionChanged (); inline; begin end;
 
 procedure g_Player_DrawCorpses();
 var
-  i: Integer;
+  i, fX, fY: Integer;
   a: TDFPoint;
 begin
   if gGibs <> nil then
@@ -1862,13 +1882,15 @@ begin
           if not g_Obj_Collide(sX, sY, sWidth, sHeight, @Obj) then
             Continue;
 
+          Obj.lerp(gLerpFactor, fX, fY);
+
           a.X := Obj.Rect.X+(Obj.Rect.Width div 2);
           a.y := Obj.Rect.Y+(Obj.Rect.Height div 2);
 
-          e_DrawAdv(ID, Obj.X, Obj.Y, 0, True, False, RAngle, @a, TMirrorType.None);
+          e_DrawAdv(ID, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None);
 
           e_Colors := Color;
-          e_DrawAdv(MaskID, Obj.X, Obj.Y, 0, True, False, RAngle, @a, TMirrorType.None);
+          e_DrawAdv(MaskID, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None);
           e_Colors.R := 255;
           e_Colors.G := 255;
           e_Colors.B := 255;
@@ -1882,7 +1904,7 @@ end;
 
 procedure g_Player_DrawShells();
 var
-  i: Integer;
+  i, fX, fY: Integer;
   a: TDFPoint;
 begin
   if gShells <> nil then
@@ -1893,10 +1915,12 @@ begin
           if not g_Obj_Collide(sX, sY, sWidth, sHeight, @Obj) then
             Continue;
 
+          Obj.lerp(gLerpFactor, fX, fY);
+
           a.X := CX;
           a.Y := CY;
 
-          e_DrawAdv(SpriteID, Obj.X, Obj.Y, 0, True, False, RAngle, @a, TMirrorType.None);
+          e_DrawAdv(SpriteID, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None);
         end;
 end;
 
@@ -2338,7 +2362,7 @@ end;
 
 procedure TPlayer.DrawIndicator(Color: TRGB);
 var
-  indX, indY: Integer;
+  indX, indY, fX, fY: Integer;
   indW, indH: Word;
   indA: Single;
   a: TDFPoint;
@@ -2347,6 +2371,8 @@ var
   c: TRGB;
 begin
   if FAlive then
+  begin
+    FObj.lerp(gLerpFactor, fX, fY);
     case gPlayerIndicatorStyle of
       0:
         begin
@@ -2359,29 +2385,29 @@ begin
             if (FObj.X + FObj.Rect.X) < 0 then
             begin
               indA := 90;
-              indX := FObj.X + FObj.Rect.X + FObj.Rect.Width;
-              indY := FObj.Y + FObj.Rect.Y + (FObj.Rect.Height - indW) div 2;
+              indX := fX + FObj.Rect.X + FObj.Rect.Width;
+              indY := fY + FObj.Rect.Y + (FObj.Rect.Height - indW) div 2;
             end
 
             else if (FObj.X + FObj.Rect.X + FObj.Rect.Width) > Max(gMapInfo.Width, gPlayerScreenSize.X) then
             begin
               indA := 270;
-              indX := FObj.X + FObj.Rect.X - indH;
-              indY := FObj.Y + FObj.Rect.Y + (FObj.Rect.Height - indW) div 2;
+              indX := fX + FObj.Rect.X - indH;
+              indY := fY + FObj.Rect.Y + (FObj.Rect.Height - indW) div 2;
             end
 
-            else if (fObj.Y - indH) < 0 then
+            else if (FObj.Y - indH) < 0 then
             begin
               indA := 180;
-              indX := FObj.X + FObj.Rect.X + (FObj.Rect.Width - indW) div 2;
-              indY := FObj.Y + FObj.Rect.Y + FObj.Rect.Height;
+              indX := fX + FObj.Rect.X + (FObj.Rect.Width - indW) div 2;
+              indY := fY + FObj.Rect.Y + FObj.Rect.Height;
             end
 
             else
             begin
               indA := 0;
-              indX := FObj.X + FObj.Rect.X + (FObj.Rect.Width - indW) div 2;
-              indY := FObj.Y - indH;
+              indX := fX + FObj.Rect.X + (FObj.Rect.Width - indW) div 2;
+              indY := fY - indH;
             end;
 
             indX := EnsureRange(indX, 0, Max(gMapInfo.Width, gPlayerScreenSize.X) - indW);
@@ -2397,23 +2423,25 @@ begin
       1:
         begin
           e_TextureFontGetSize(gStdFont, nW, nH);
-          indX := FObj.X + FObj.Rect.X + (FObj.Rect.Width - Length(FName) * nW) div 2;
-          indY := FObj.Y - nH;
+          indX := fX + FObj.Rect.X + (FObj.Rect.Width - Length(FName) * nW) div 2;
+          indY := fY - nH;
           e_TextureFontPrintEx(indX, indY, FName, gStdFont, Color.R, Color.G, Color.B, 1.0, True);
         end;
     end;
+  end;
 end;
 
 procedure TPlayer.DrawBubble();
 var
-  bubX, bubY: Integer;
+  bubX, bubY, fX, fY: Integer;
   ID: LongWord;
   Rb, Gb, Bb,
   Rw, Gw, Bw: SmallInt;
   Dot: Byte;
 begin
-  bubX := FObj.X+FObj.Rect.X + IfThen(FDirection = TDirection.D_LEFT, -4, 18);
-  bubY := FObj.Y+FObj.Rect.Y - 18;
+  FObj.lerp(gLerpFactor, fX, fY);
+  bubX := fX+FObj.Rect.X + IfThen(FDirection = TDirection.D_LEFT, -4, 18);
+  bubY := fY+FObj.Rect.Y - 18;
   Rb := 64;
   Gb := 64;
   Bb := 64;
@@ -2423,8 +2451,8 @@ begin
   case gChatBubble of
     1: // simple textual non-bubble
     begin
-      bubX := FObj.X+FObj.Rect.X - 11;
-      bubY := FObj.Y+FObj.Rect.Y - 17;
+      bubX := fX+FObj.Rect.X - 11;
+      bubY := fY+FObj.Rect.Y - 17;
       e_TextureFontPrint(bubX, bubY, '[...]', gStdFont);
       Exit;
     end;
@@ -2491,7 +2519,10 @@ var
   w, h: Word;
   dr: Boolean;
   Mirror: TMirrorType;
+  fX, fY: Integer;
 begin
+  FObj.lerp(gLerpFactor, fX, fY);
+
   if FAlive then
   begin
     if Direction = TDirection.D_RIGHT then
@@ -2501,8 +2532,8 @@ begin
 
     if FPunchAnim <> nil then
     begin
-      FPunchAnim.Draw(FObj.X+IfThen(Direction = TDirection.D_LEFT, 15-FObj.Rect.X, FObj.Rect.X-15),
-                      FObj.Y+FObj.Rect.Y-11, Mirror);
+      FPunchAnim.Draw(fX+IfThen(Direction = TDirection.D_LEFT, 15-FObj.Rect.X, FObj.Rect.X-15),
+                      fY+FObj.Rect.Y-11, Mirror);
       if FPunchAnim.played then
       begin
         FPunchAnim.Free;
@@ -2515,11 +2546,11 @@ begin
       begin
         e_GetTextureSize(ID, @w, @h);
         if FDirection = TDirection.D_LEFT then
-          e_Draw(ID, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-(w div 2)+4,
-                     FObj.Y+FObj.Rect.Y+(FObj.Rect.Height div 2)-(h div 2)-7+FObj.slopeUpLeft, 0, True, False)
+          e_Draw(ID, fX+FObj.Rect.X+(FObj.Rect.Width div 2)-(w div 2)+4,
+                     fY+FObj.Rect.Y+(FObj.Rect.Height div 2)-(h div 2)-7+FObj.slopeUpLeft, 0, True, False)
         else
-          e_Draw(ID, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-(w div 2)-2,
-                     FObj.Y+FObj.Rect.Y+(FObj.Rect.Height div 2)-(h div 2)-7+FObj.slopeUpLeft, 0, True, False);
+          e_Draw(ID, fX+FObj.Rect.X+(FObj.Rect.Width div 2)-(w div 2)-2,
+                     fY+FObj.Rect.Y+(FObj.Rect.Height div 2)-(h div 2)-7+FObj.slopeUpLeft, 0, True, False);
       end;
 
     if FMegaRulez[MR_INVIS] > gTime then
@@ -2532,15 +2563,15 @@ begin
         else
           dr := True;
         if dr then
-          FModel.Draw(FObj.X, FObj.Y+FObj.slopeUpLeft, 200)
+          FModel.Draw(fX, fY+FObj.slopeUpLeft, 200)
         else
-          FModel.Draw(FObj.X, FObj.Y+FObj.slopeUpLeft);
+          FModel.Draw(fX, fY+FObj.slopeUpLeft);
       end
       else
-        FModel.Draw(FObj.X, FObj.Y+FObj.slopeUpLeft, 254);
+        FModel.Draw(fX, fY+FObj.slopeUpLeft, 254);
     end
     else
-      FModel.Draw(FObj.X, FObj.Y+FObj.slopeUpLeft);
+      FModel.Draw(fX, fY+FObj.slopeUpLeft);
   end;
 
   if g_debug_Frames then
@@ -4396,6 +4427,7 @@ begin
   ReleaseKeys();
 
   FDamageBuffer := 0;
+  FIncCamOld := 0;
   FIncCam := 0;
   FBFGFireCounter := -1;
   FShellTimer := -1;
@@ -4564,6 +4596,7 @@ var
   Anim: TAnimation;
   ID: DWORD;
 begin
+  FIncCamOld := 0;
   FIncCam := 0;
   FBFGFireCounter := -1;
   FShellTimer := -1;
@@ -4665,6 +4698,8 @@ begin
 // Óñòàíîâêà êîîðäèíàò è ñáðîñ âñåõ ïàðàìåòðîâ:
   FObj.X := RespawnPoint.X-PLAYER_RECT.X;
   FObj.Y := RespawnPoint.Y-PLAYER_RECT.Y;
+  FObj.oldX := FObj.X; // don't interpolate after respawn
+  FObj.oldY := FObj.Y;
   FObj.Vel.X := 0;
   FObj.Vel.Y := 0;
   FObj.Accel.X := 0;
@@ -4922,6 +4957,8 @@ begin
 
   FObj.X := X-PLAYER_RECT.X;
   FObj.Y := Y-PLAYER_RECT.Y;
+  FObj.oldX := FObj.X; // don't interpolate after respawn
+  FObj.oldY := FObj.Y;
   if FAlive and FGhost then
   begin
     FXTo := FObj.X;
@@ -5004,6 +5041,13 @@ begin
       end;
 end;
 
+procedure TPlayer.PreUpdate();
+begin
+  FIncCamOld := FIncCam;
+  FObj.oldX := FObj.X;
+  FObj.oldY := FObj.Y;
+end;
+
 procedure TPlayer.Update();
 var
   b: Byte;
@@ -5016,11 +5060,8 @@ begin
   NetServer := g_Game_IsNet and g_Game_IsServer;
   AnyServer := g_Game_IsServer;
 
-  if g_Game_IsClient and (NetInterpLevel > 0) then
-    DoLerp(NetInterpLevel + 1)
-  else
-    if FGhost then
-      DoLerp(4);
+  if FGhost then
+    DoLerp(4);
 
   if NetServer then
     if FClientID >= 0 then
@@ -6586,17 +6627,21 @@ begin
 end;
 
 procedure TCorpse.Draw();
+var
+  fX, fY: Integer;
 begin
   if FState = CORPSE_STATE_REMOVEME then
     Exit;
 
+  FObj.lerp(gLerpFactor, fX, fY);
+
   if FAnimation <> nil then
-    FAnimation.Draw(FObj.X, FObj.Y, TMirrorType.None);
+    FAnimation.Draw(fX, fY, TMirrorType.None);
 
   if FAnimationMask <> nil then
   begin
     e_Colors := FColor;
-    FAnimationMask.Draw(FObj.X, FObj.Y, TMirrorType.None);
+    FAnimationMask.Draw(fX, fY, TMirrorType.None);
     e_Colors.R := 255;
     e_Colors.G := 255;
     e_Colors.B := 255;
@@ -6610,6 +6655,9 @@ begin
   if FState = CORPSE_STATE_REMOVEME then
     Exit;
 
+  FObj.oldX := FObj.X;
+  FObj.oldY := FObj.Y;
+
   if gTime mod (GAME_TICK*2) <> 0 then
   begin
     g_Obj_Move(@FObj, True, True, True);
index 632cb1344c3e4728f37478e4e683e1d1ac652cb0..bb17a4138972d82f07ac7674dc966e543cec69d3 100644 (file)
@@ -72,6 +72,7 @@ procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boo
 
 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
+procedure g_Weapon_PreUpdate();
 procedure g_Weapon_Update();
 procedure g_Weapon_Draw();
 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
@@ -737,6 +738,8 @@ begin
     end;
   end;
 
+  Shots[find_id].Obj.oldX := X;
+  Shots[find_id].Obj.oldY := Y;
   Shots[find_id].Obj.X := X;
   Shots[find_id].Obj.Y := Y;
   Shots[find_id].Obj.Vel.X := XV;
@@ -762,6 +765,8 @@ begin
   if a = 0 then
     a := 1;
 
+  Shots[i].Obj.oldX := x;
+  Shots[i].Obj.oldY := y;
   Shots[i].Obj.X := x;
   Shots[i].Obj.Y := y;
   Shots[i].Obj.Vel.X := (xd*s) div a;
@@ -2093,6 +2098,19 @@ begin
   end;
 end;
 
+procedure g_Weapon_PreUpdate();
+var
+  i: Integer;
+begin
+  if Shots = nil then Exit;
+  for i := 0 to High(Shots) do
+    if Shots[i].ShotType <> 0 then
+    begin
+      Shots[i].Obj.oldX := Shots[i].Obj.X;
+      Shots[i].Obj.oldY := Shots[i].Obj.Y;
+    end;
+end;
+
 procedure g_Weapon_Update();
 var
   i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
@@ -2501,7 +2519,7 @@ end;
 
 procedure g_Weapon_Draw();
 var
-  i: Integer;
+  i, fX, fY: Integer;
   a: SmallInt;
   p: TDFPoint;
 begin
@@ -2520,6 +2538,7 @@ begin
         else
           a := 0;
 
+        Obj.lerp(gLerpFactor, fX, fY);
         p.X := Obj.Rect.Width div 2;
         p.Y := Obj.Rect.Height div 2;
 
@@ -2528,16 +2547,16 @@ begin
             if (Shots[i].ShotType = WEAPON_BARON_FIRE) or
                (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
                (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
-              Animation.DrawEx(Obj.X, Obj.Y, TMirrorType.None, p, a)
+              Animation.DrawEx(fX, fY, TMirrorType.None, p, a)
             else
-              Animation.Draw(Obj.X, Obj.Y, TMirrorType.None);
+              Animation.Draw(fX, fY, TMirrorType.None);
           end
         else if TextureID <> 0 then
           begin
             if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then
-              e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, TMirrorType.None)
+              e_DrawAdv(TextureID, fX, fY, 0, True, False, a, @p, TMirrorType.None)
             else if (Shots[i].ShotType <> WEAPON_FLAMETHROWER) then
-              e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False);
+              e_Draw(TextureID, fX, fY, 0, True, False);
           end;
 
           if g_debug_Frames then
index 7774c0a34c0c03b2bda5fd5418ece817dc492963..351edf913e91396a154c5ae8a5c52a08cf32c5e2 100644 (file)
@@ -52,6 +52,7 @@ const
 
 var
   Time, Time_Delta, Time_Old: Int64;
+  Frame: Int64;
   flag: Boolean;
   wNeedTimeReset: Boolean = false;
   wMinimized: Boolean = false;
@@ -124,6 +125,7 @@ begin
 
   if wNeedTimeReset then
   begin
+    Frame := 0;
     Time_Delta := 28;
     wNeedTimeReset := false;
   end;
@@ -167,18 +169,23 @@ begin
 
   // Âðåìÿ ïðåäûäóùåãî îáíîâëåíèÿ
   if flag then
-  begin
     Time_Old := Time - (Time_Delta mod 28);
+
+  if (Time - Frame > 4) then
+  begin
     if (not wMinimized) then
     begin
+      if gPause or not gLerpActors then
+        gLerpFactor := 1.0
+      else
+        gLerpFactor := nmin(1.0, (Time - Time_Old) / 28.0);
       Draw;
       sys_Repaint
-    end
+    end;
+    Frame := Time
   end
   else
-  begin
-    sys_Delay(1) // release time slice, so we won't eat 100% CPU
-  end;
+    sys_Delay(1);
 
   e_SoundUpdate();
 end;
index 946e4f5a07c7a31a5422fad1f2f479fb83ffce6a..57ce782cad7f8a10c7186575b5143de7b0753888 100644 (file)
@@ -189,6 +189,7 @@ function readLongIntBE (st: TStream): LongInt;
 function readInt64BE (st: TStream): Int64;
 function readUInt64BE (st: TStream): UInt64;
 
+function nlerp (a, b: Integer; t: Single): Integer; inline;
 
 function nmin (a, b: Byte): Byte; inline; overload;
 function nmin (a, b: ShortInt): ShortInt; inline; overload;
@@ -1528,6 +1529,8 @@ function readUInt64BE (st: TStream): UInt64; begin readIntegerBE(st, @result, 8)
 
 
 // ////////////////////////////////////////////////////////////////////////// //
+function nlerp (a, b: Integer; t: Single): Integer; inline; begin result := round((1.0 - t) * a + t * b); end;
+
 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;