DEADSOFTWARE

draw talkbubble above the player corpse when necessary
[d2df-sdl.git] / src / game / g_player.pas
index db39cb84b0961e1260f840fb8a885e710648b1a8..dd5378b5162ce6e2fbbf3f67bf5eee90cda9efbb 100644 (file)
@@ -188,6 +188,7 @@ type
     FFireAngle: SmallInt;
     FIncCamOld:      Integer;
     FIncCam:         Integer;
+    FSlopeOld:       Integer;
     FShellTimer:     Integer;
     FShellType:      Byte;
     FSawSound:       TPlayableSound;
@@ -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;
@@ -350,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;
@@ -382,6 +386,7 @@ type
     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    SlopeOld: Integer read FSlopeOld write FSlopeOld;
     property    UID: Word read FUID write FUID;
     property    JustTeleported: Boolean read FJustTeleported write FJustTeleported;
     property    NetTime: LongWord read FNetTime write FNetTime;
@@ -595,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();
@@ -1435,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();
@@ -1568,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;
@@ -1610,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,
@@ -2197,6 +2209,7 @@ begin
   FFirePainTime := 0;
   FFireAttacker := 0;
   FHandicap := 100;
+  FCorpse := -1;
 
   FActualModelName := 'doomer';
 
@@ -2362,7 +2375,7 @@ end;
 
 procedure TPlayer.DrawIndicator(Color: TRGB);
 var
-  indX, indY, fX, fY: Integer;
+  indX, indY, fX, fY, fSlope: Integer;
   indW, indH: Word;
   indA: Single;
   a: TDFPoint;
@@ -2373,6 +2386,8 @@ begin
   if FAlive then
   begin
     FObj.lerp(gLerpFactor, fX, fY);
+    fSlope := nlerp(FSlopeOld, FObj.slopeUpLeft, gLerpFactor);
+
     case gPlayerIndicatorStyle of
       0:
         begin
@@ -2410,6 +2425,7 @@ begin
               indY := fY - indH;
             end;
 
+            indY := indY + fSlope;
             indX := EnsureRange(indX, 0, Max(gMapInfo.Width, gPlayerScreenSize.X) - indW);
             indY := EnsureRange(indY, 0, Max(gMapInfo.Height, gPlayerScreenSize.Y) - indH);
 
@@ -2424,7 +2440,7 @@ begin
         begin
           e_TextureFontGetSize(gStdFont, nW, nH);
           indX := fX + FObj.Rect.X + (FObj.Rect.Width - Length(FName) * nW) div 2;
-          indY := fY - nH;
+          indY := fY - nH + fSlope;
           e_TextureFontPrintEx(indX, indY, FName, gStdFont, Color.R, Color.G, Color.B, 1.0, True);
         end;
     end;
@@ -2438,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;
@@ -2519,9 +2538,10 @@ var
   w, h: Word;
   dr: Boolean;
   Mirror: TMirrorType;
-  fX, fY: Integer;
+  fX, fY, fSlope: Integer;
 begin
   FObj.lerp(gLerpFactor, fX, fY);
+  fSlope := nlerp(FSlopeOld, FObj.slopeUpLeft, gLerpFactor);
 
   if FAlive then
   begin
@@ -2533,7 +2553,7 @@ begin
     if FPunchAnim <> nil then
     begin
       FPunchAnim.Draw(fX+IfThen(Direction = TDirection.D_LEFT, 15-FObj.Rect.X, FObj.Rect.X-15),
-                      fY+FObj.Rect.Y-11, Mirror);
+                      fY+fSlope+FObj.Rect.Y-11, Mirror);
       if FPunchAnim.played then
       begin
         FPunchAnim.Free;
@@ -2547,10 +2567,10 @@ begin
         e_GetTextureSize(ID, @w, @h);
         if FDirection = TDirection.D_LEFT then
           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)
+                     fY+FObj.Rect.Y+(FObj.Rect.Height div 2)-(h div 2)-7+fSlope, 0, True, False)
         else
           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);
+                     fY+FObj.Rect.Y+(FObj.Rect.Height div 2)-(h div 2)-7+fSlope, 0, True, False);
       end;
 
     if FMegaRulez[MR_INVIS] > gTime then
@@ -2563,15 +2583,15 @@ begin
         else
           dr := True;
         if dr then
-          FModel.Draw(fX, fY+FObj.slopeUpLeft, 200)
+          FModel.Draw(fX, fY+fSlope, 200)
         else
-          FModel.Draw(fX, fY+FObj.slopeUpLeft);
+          FModel.Draw(fX, fY+fSlope);
       end
       else
-        FModel.Draw(fX, fY+FObj.slopeUpLeft, 254);
+        FModel.Draw(fX, fY+fSlope, 254);
     end
     else
-      FModel.Draw(fX, fY+FObj.slopeUpLeft);
+      FModel.Draw(fX, fY+fSlope);
   end;
 
   if g_debug_Frames then
@@ -3597,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
@@ -4408,6 +4428,7 @@ begin
   FDeath := 0;
   FSecrets := 0;
   FSpawnInvul := 0;
+  FCorpse := -1;
   FReady := False;
   if FNoRespawn then
   begin
@@ -4427,6 +4448,7 @@ begin
   ReleaseKeys();
 
   FDamageBuffer := 0;
+  FSlopeOld := 0;
   FIncCamOld := 0;
   FIncCam := 0;
   FBFGFireCounter := -1;
@@ -4596,6 +4618,7 @@ var
   Anim: TAnimation;
   ID: DWORD;
 begin
+  FSlopeOld := 0;
   FIncCamOld := 0;
   FIncCam := 0;
   FBFGFireCounter := -1;
@@ -4603,6 +4626,7 @@ begin
   FPain := 0;
   FLastHit := 0;
   FSpawnInvul := 0;
+  FCorpse := -1;
 
   if not g_Game_IsServer then
     Exit;
@@ -4785,6 +4809,7 @@ begin
   FPhysics := False;
   FWantsInGame := False;
   FSpawned := False;
+  FCorpse := -1;
 
   if FNoRespawn then
   begin
@@ -5017,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
@@ -5031,18 +5057,29 @@ 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;
   FIncCamOld := FIncCam;
   FObj.oldX := FObj.X;
   FObj.oldY := FObj.Y;
@@ -5060,8 +5097,11 @@ begin
   NetServer := g_Game_IsNet and g_Game_IsServer;
   AnyServer := g_Game_IsServer;
 
-  if FGhost then
-    DoLerp(4);
+  if g_Game_IsClient and (NetInterpLevel > 0) then
+    DoLerp(NetInterpLevel + 1)
+  else
+    if FGhost then
+      DoLerp(4);
 
   if NetServer then
     if FClientID >= 0 then
@@ -5121,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;
 
@@ -5256,8 +5295,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
   else
@@ -5739,6 +5777,8 @@ procedure TPlayer.SetLerp(XTo, YTo: Integer);
 var
   AX, AY: Integer;
 begin
+  FXTo := XTo;
+  FYTo := YTo;
   if NetInterpLevel < 1 then
   begin
     FObj.X := XTo;
@@ -5746,9 +5786,6 @@ begin
   end
   else
   begin
-    FXTo := XTo;
-    FYTo := YTo;
-
     AX := Abs(FXTo - FObj.X);
     AY := Abs(FYTo - FObj.Y);
     if (AX > 32) or (AX <= NetInterpLevel) then