DEADSOFTWARE

draw talkbubble above the player corpse when necessary
[d2df-sdl.git] / src / game / g_player.pas
index 362c2833ecbe9a7393caf11622c1ccb5bbfdb67e..dd5378b5162ce6e2fbbf3f67bf5eee90cda9efbb 100644 (file)
@@ -171,6 +171,7 @@ type
     FLastSpawnerUID: Word;
     FLastHit:   Byte;
     FObj:       TObj;
+    FXTo, FYTo: Integer;
     FSpectatePlayer: Integer;
     FFirePainTime:   Integer;
     FFireAttacker:   Word;
@@ -238,7 +239,7 @@ type
 
     procedure doDamage (v: Integer);
 
-    function followCorpse(): Boolean;
+    function refreshCorpse(): Boolean;
 
   public
     FDamageBuffer:   Integer;
@@ -271,6 +272,7 @@ type
     FSpawnInvul: Integer;
     FHandicap:  Integer;
     FWaitForFirstSpawn: Boolean; // set to `true` in server, used to spawn a player on first full state request
+    FCorpse:    Integer;
 
     // debug: viewport offset
     viewPortX, viewPortY, viewPortW, viewPortH: Integer;
@@ -334,6 +336,8 @@ type
     procedure   LoadState (st: TStream); virtual;
     procedure   PauseSounds(Enable: Boolean);
     procedure   NetFire(Wpn: Byte; X, Y, AX, AY: Integer; WID: Integer = -1);
+    procedure   DoLerp(Level: Integer = 2);
+    procedure   SetLerp(XTo, YTo: Integer);
     procedure   QueueWeaponSwitch(Weapon: Byte);
     procedure   RealizeCurrentWeapon();
     procedure   FlamerOn;
@@ -348,6 +352,8 @@ type
     procedure getMapBox (out x, y, w, h: Integer); inline;
     procedure moveBy (dx, dy: Integer); inline;
 
+    function getCameraObj(): TObj;
+
   public
     property    Vel: TPoint2i read FObj.Vel;
     property    Obj: TObj read FObj;
@@ -594,7 +600,7 @@ function  g_Player_Get(UID: Word): TPlayer;
 function  g_Player_GetCount(): Byte;
 function  g_Player_GetStats(): TPlayerStatArray;
 function  g_Player_ValidName(Name: String): Boolean;
-procedure g_Player_CreateCorpse(Player: TPlayer);
+function  g_Player_CreateCorpse(Player: TPlayer): Integer;
 procedure g_Player_CreateGibs(fX, fY: Integer; ModelName: String; fColor: TRGB);
 procedure g_Player_CreateShell(fX, fY, dX, dY: Integer; T: Byte);
 procedure g_Player_UpdatePhysicalObjects();
@@ -1434,6 +1440,8 @@ begin
   e_TextureFontPrint(0, fH * 3, 'Vel Y: ' + IntToStr(p.FObj.Vel.Y), gStdFont);
   e_TextureFontPrint(0, fH * 4, 'Acc X: ' + IntToStr(p.FObj.Accel.X), gStdFont);
   e_TextureFontPrint(0, fH * 5, 'Acc Y: ' + IntToStr(p.FObj.Accel.Y), gStdFont);
+  e_TextureFontPrint(0, fH * 6, 'Old X: ' + IntToStr(p.FObj.oldX), gStdFont);
+  e_TextureFontPrint(0, fH * 7, 'Old Y: ' + IntToStr(p.FObj.oldY), gStdFont);
 end;
 
 procedure g_Player_DrawHealth();
@@ -1567,21 +1575,24 @@ begin
       end;
 end;
 
-procedure g_Player_CreateCorpse(Player: TPlayer);
+function  g_Player_CreateCorpse(Player: TPlayer): Integer;
 var
   i: Integer;
   find_id: DWORD;
   ok: Boolean;
 begin
+  Result := -1;
+
   if Player.alive then
     Exit;
 
 // Ðàçðûâàåì ñâÿçü ñ ïðåæíèì òðóïîì:
-  if gCorpses <> nil then
-    for i := 0 to High(gCorpses) do
-      if gCorpses[i] <> nil then
-        if gCorpses[i].FPlayerUID = Player.FUID then
-          gCorpses[i].FPlayerUID := 0;
+  i := Player.FCorpse;
+  if (i >= 0) and (i < Length(gCorpses)) then
+  begin
+    if (gCorpses[i] <> nil) and (gCorpses[i].FPlayerUID = Player.FUID) then
+      gCorpses[i].FPlayerUID := 0;
+  end;
 
   if Player.FObj.Y >= gMapInfo.Height+128 then
     Exit;
@@ -1609,6 +1620,8 @@ begin
         gCorpses[find_id].FObj.Vel := FObj.Vel;
         gCorpses[find_id].FObj.Accel := FObj.Accel;
         gCorpses[find_id].FPlayerUID := FUID;
+
+        Result := find_id;
       end
     else
       g_Player_CreateGibs(FObj.X + PLAYER_RECT_CX,
@@ -2196,6 +2209,7 @@ begin
   FFirePainTime := 0;
   FFireAttacker := 0;
   FHandicap := 100;
+  FCorpse := -1;
 
   FActualModelName := 'doomer';
 
@@ -2440,8 +2454,11 @@ var
   Rb, Gb, Bb,
   Rw, Gw, Bw: SmallInt;
   Dot: Byte;
+  CObj: TObj;
 begin
-  FObj.lerp(gLerpFactor, fX, fY);
+  CObj := getCameraObj();
+  CObj.lerp(gLerpFactor, fX, fY);
+  // NB: _F_Obj.Rect is used to keep the bubble higher; this is not a mistake
   bubX := fX+FObj.Rect.X + IfThen(FDirection = TDirection.D_LEFT, -4, 18);
   bubY := fY+FObj.Rect.Y - 18;
   Rb := 64;
@@ -3600,7 +3617,7 @@ begin
     DropFlag(KillType = K_FALLKILL);
   end;
 
-  g_Player_CreateCorpse(Self);
+  FCorpse := g_Player_CreateCorpse(Self);
 
   if Srv and (gGameSettings.MaxLives > 0) and FNoRespawn and
      (gLMSRespawn = LMS_RESPAWN_NONE) then
@@ -4411,6 +4428,7 @@ begin
   FDeath := 0;
   FSecrets := 0;
   FSpawnInvul := 0;
+  FCorpse := -1;
   FReady := False;
   if FNoRespawn then
   begin
@@ -4608,6 +4626,7 @@ begin
   FPain := 0;
   FLastHit := 0;
   FSpawnInvul := 0;
+  FCorpse := -1;
 
   if not g_Game_IsServer then
     Exit;
@@ -4781,6 +4800,8 @@ begin
     GameX := gMapInfo.Width div 2;
     GameY := gMapInfo.Height div 2;
   end;
+  FXTo := GameX;
+  FYTo := GameY;
 
   FAlive := False;
   FSpectator := True;
@@ -4788,6 +4809,7 @@ begin
   FPhysics := False;
   FWantsInGame := False;
   FSpawned := False;
+  FCorpse := -1;
 
   if FNoRespawn then
   begin
@@ -4813,7 +4835,11 @@ begin
     Exit;
   FGhost := not FGhost;
   FPhysics := not FGhost;
-  if not FGhost then
+  if FGhost then
+  begin
+    FXTo := FObj.X;
+    FYTo := FObj.Y;
+  end else
   begin
     FObj.Accel.X := 0;
     FObj.Accel.Y := 0;
@@ -4958,6 +4984,11 @@ begin
   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;
+    FYTo := FObj.Y;
+  end;
 
   if not g_Game_IsNet then
   begin
@@ -5011,11 +5042,12 @@ begin
     Result := 1;
 end;
 
-function TPlayer.followCorpse(): Boolean;
+function TPlayer.refreshCorpse(): Boolean;
 var
   i: Integer;
 begin
   Result := False;
+  FCorpse := -1;
   if FAlive or FSpectator then
     Exit;
   if (gCorpses = nil) or (Length(gCorpses) = 0) then
@@ -5025,16 +5057,26 @@ begin
       if gCorpses[i].FPlayerUID = FUID then
       begin
         Result := True;
-        FObj.X := gCorpses[i].FObj.X;
-        FObj.Y := gCorpses[i].FObj.Y;
-        FObj.Vel.X := gCorpses[i].FObj.Vel.X;
-        FObj.Vel.Y := gCorpses[i].FObj.Vel.Y;
-        FObj.Accel.X := gCorpses[i].FObj.Accel.X;
-        FObj.Accel.Y := gCorpses[i].FObj.Accel.Y;
+        FCorpse := i;
         break;
       end;
 end;
 
+function TPlayer.getCameraObj(): TObj;
+begin
+  if (not FAlive) and (not FSpectator) and
+     (FCorpse >= 0) and (FCorpse < Length(gCorpses)) and
+     (gCorpses[FCorpse] <> nil) and (gCorpses[FCorpse].FPlayerUID = FUID) then
+  begin
+    gCorpses[FCorpse].FObj.slopeUpLeft := FObj.slopeUpLeft;
+    Result := gCorpses[FCorpse].FObj;
+  end
+  else
+  begin
+    Result := FObj;
+  end;
+end;
+
 procedure TPlayer.PreUpdate();
 begin
   FSlopeOld := FObj.slopeUpLeft;
@@ -5055,6 +5097,12 @@ 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 NetServer then
     if FClientID >= 0 then
     begin
@@ -5113,8 +5161,7 @@ begin
 
     if FPhysics then
     begin
-      if not followCorpse() then
-        g_Obj_Move(@FObj, True, True, True);
+      g_Obj_Move(@FObj, True, True, True);
       positionChanged(); // this updates spatial accelerators
     end;
 
@@ -5217,39 +5264,38 @@ begin
   begin
     if FKeys[KEY_UP].Pressed or FKeys[KEY_JUMP].Pressed then
     begin
-      FObj.Y := FObj.Y - 32;
+      FYTo := FObj.Y - 32;
       FSpectatePlayer := -1;
     end;
     if FKeys[KEY_DOWN].Pressed then
     begin
-      FObj.Y := FObj.Y + 32;
+      FYTo := FObj.Y + 32;
       FSpectatePlayer := -1;
     end;
     if FKeys[KEY_LEFT].Pressed then
     begin
-      FObj.X := FObj.X - 32;
+      FXTo := FObj.X - 32;
       FSpectatePlayer := -1;
     end;
     if FKeys[KEY_RIGHT].Pressed then
     begin
-      FObj.X := FObj.X + 32;
+      FXTo := FObj.X + 32;
       FSpectatePlayer := -1;
     end;
 
-    if (FObj.X < -64) then
-      FObj.X := -64
-    else if (FObj.X > gMapInfo.Width + 32) then
-      FObj.X := gMapInfo.Width + 32;
-    if (FObj.Y < -72) then
-      FObj.Y := -72
-    else if (FObj.Y > gMapInfo.Height + 32) then
-      FObj.Y := gMapInfo.Height + 32;
+    if (FXTo < -64) then
+      FXTo := -64
+    else if (FXTo > gMapInfo.Width + 32) then
+      FXTo := gMapInfo.Width + 32;
+    if (FYTo < -72) then
+      FYTo := -72
+    else if (FYTo > gMapInfo.Height + 32) then
+      FYTo := gMapInfo.Height + 32;
   end;
 
   if FPhysics then
   begin
-    if not followCorpse() then
-      g_Obj_Move(@FObj, True, True, True);
+    g_Obj_Move(@FObj, True, True, True);
     positionChanged(); // this updates spatial accelerators
   end
   else
@@ -5261,8 +5307,8 @@ begin
         if gPlayers[FSpectatePlayer] <> nil then
           if gPlayers[FSpectatePlayer].alive then
           begin
-            FObj.X := gPlayers[FSpectatePlayer].GameX;
-            FObj.Y := gPlayers[FSpectatePlayer].GameY;
+            FXTo := gPlayers[FSpectatePlayer].GameX;
+            FYTo := gPlayers[FSpectatePlayer].GameY;
           end;
   end;
 
@@ -5721,6 +5767,34 @@ begin
       else if (FAngle = ANGLE_LEFTUP) or (FAngle = ANGLE_RIGHTUP) then SetAction(A_ATTACKUP);
 end;
 
+procedure TPlayer.DoLerp(Level: Integer = 2);
+begin
+  if FObj.X <> FXTo then FObj.X := Lerp(FObj.X, FXTo, Level);
+  if FObj.Y <> FYTo then FObj.Y := Lerp(FObj.Y, FYTo, Level);
+end;
+
+procedure TPlayer.SetLerp(XTo, YTo: Integer);
+var
+  AX, AY: Integer;
+begin
+  FXTo := XTo;
+  FYTo := YTo;
+  if NetInterpLevel < 1 then
+  begin
+    FObj.X := XTo;
+    FObj.Y := YTo;
+  end
+  else
+  begin
+    AX := Abs(FXTo - FObj.X);
+    AY := Abs(FYTo - FObj.Y);
+    if (AX > 32) or (AX <= NetInterpLevel) then
+      FObj.X := FXTo;
+    if (AY > 32) or (AY <= NetInterpLevel) then
+      FObj.Y := FYTo;
+  end;
+end;
+
 function TPlayer.FullInLift(XInc, YInc: Integer): Integer;
 begin
   if g_Map_CollidePanel(FObj.X+PLAYER_RECT.X+XInc, FObj.Y+PLAYER_RECT.Y+YInc,