diff --git a/src/game/g_player.pas b/src/game/g_player.pas
index 362c2833ecbe9a7393caf11622c1ccb5bbfdb67e..28032e95d4ea5c2b767c165ff8b6993915e5af21 100644 (file)
--- a/src/game/g_player.pas
+++ b/src/game/g_player.pas
Kills: Word;
Color: TRGB;
Spectator: Boolean;
+ UID: Word;
end;
TPlayerStatArray = Array of TPlayerStat;
FLastSpawnerUID: Word;
FLastHit: Byte;
FObj: TObj;
+ FXTo, FYTo: Integer;
FSpectatePlayer: Integer;
FFirePainTime: Integer;
FFireAttacker: Word;
procedure doDamage (v: Integer);
- function followCorpse(): Boolean;
+ function refreshCorpse(): Boolean;
public
FDamageBuffer: Integer;
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;
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;
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;
public
constructor Create(X, Y: Integer; ModelName: String; aMess: Boolean);
destructor Destroy(); override;
- procedure Damage(Value: Word; vx, vy: Integer);
+ procedure Damage(Value: Word; SpawnerUID: Word; vx, vy: Integer);
procedure Update();
procedure Draw();
procedure SaveState (st: TStream);
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();
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();
Color := gPlayers[a].FModel.Color;
Lives := gPlayers[a].FLives;
Spectator := gPlayers[a].FSpectator;
+ UID := gPlayers[a].FUID;
end;
end;
end;
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;
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,
FFirePainTime := 0;
FFireAttacker := 0;
FHandicap := 100;
+ FCorpse := -1;
FActualModelName := 'doomer';
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;
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
FDeath := 0;
FSecrets := 0;
FSpawnInvul := 0;
+ FCorpse := -1;
FReady := False;
if FNoRespawn then
begin
// Îäèíî÷íàÿ èãðà/êîîïåðàòèâ
if gGameSettings.GameMode in [GM_COOP, GM_SINGLE] then
begin
- if (Self = gPlayer1) or (Self = gPlayer2) then
+ if Self = gPlayer1 then
begin
- // Òî÷êà ïîÿâëåíèÿ ñâîåãî èãðîêà
- if Self = gPlayer1 then
- c := RESPAWNPOINT_PLAYER1
- else
- c := RESPAWNPOINT_PLAYER2;
- if g_Map_GetPointCount(c) > 0 then
- begin
- Result := c;
- Exit;
- end;
-
- // Òî÷êà ïîÿâëåíèÿ äðóãîãî èãðîêà
- if Self = gPlayer1 then
- c := RESPAWNPOINT_PLAYER2
- else
- c := RESPAWNPOINT_PLAYER1;
- if g_Map_GetPointCount(c) > 0 then
- begin
- Result := c;
- Exit;
- end;
- end else
+ // player 1 should try to spawn on the player 1 point
+ if g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) > 0 then
+ Exit(RESPAWNPOINT_PLAYER1)
+ else if g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) > 0 then
+ Exit(RESPAWNPOINT_PLAYER2);
+ end
+ else if Self = gPlayer2 then
begin
- // Òî÷êà ïîÿâëåíèÿ ëþáîãî èãðîêà (áîòà)
- if Random(2) = 0 then
- c := RESPAWNPOINT_PLAYER1
- else
- c := RESPAWNPOINT_PLAYER2;
- if g_Map_GetPointCount(c) > 0 then
- begin
- Result := c;
- Exit;
- end;
- end;
-
- // Òî÷êà ëþáîé èç êîìàíä
- if Random(2) = 0 then
- c := RESPAWNPOINT_RED
+ // player 2 should try to spawn on the player 2 point
+ if g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) > 0 then
+ Exit(RESPAWNPOINT_PLAYER2)
+ else if g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) > 0 then
+ Exit(RESPAWNPOINT_PLAYER1);
+ end
else
- c := RESPAWNPOINT_BLUE;
- if g_Map_GetPointCount(c) > 0 then
- begin
- Result := c;
- Exit;
- end;
-
- // Òî÷êà DM
- c := RESPAWNPOINT_DM;
- if g_Map_GetPointCount(c) > 0 then
begin
- Result := c;
- Exit;
+ // other players randomly pick either the first or the second point
+ c := IfThen((Random(2) = 0), RESPAWNPOINT_PLAYER1, RESPAWNPOINT_PLAYER2);
+ if g_Map_GetPointCount(c) > 0 then
+ Exit(c);
+ // try the other one
+ c := IfThen((c = RESPAWNPOINT_PLAYER1), RESPAWNPOINT_PLAYER2, RESPAWNPOINT_PLAYER1);
+ if g_Map_GetPointCount(c) > 0 then
+ Exit(c);
end;
end;
// Ìÿñîïîâàë
if gGameSettings.GameMode = GM_DM then
begin
- // Òî÷êà DM
- c := RESPAWNPOINT_DM;
- if g_Map_GetPointCount(c) > 0 then
- begin
- Result := c;
- Exit;
- end;
-
- // Òî÷êà ïîÿâëåíèÿ ëþáîãî èãðîêà
- if Random(2) = 0 then
- c := RESPAWNPOINT_PLAYER1
- else
- c := RESPAWNPOINT_PLAYER2;
- if g_Map_GetPointCount(c) > 0 then
- begin
- Result := c;
- Exit;
- end;
-
- // Òî÷êà ëþáîé èç êîìàíä
- if Random(2) = 0 then
- c := RESPAWNPOINT_RED
- else
- c := RESPAWNPOINT_BLUE;
- if g_Map_GetPointCount(c) > 0 then
- begin
- Result := c;
- Exit;
- end;
+ // try DM points first
+ if g_Map_GetPointCount(RESPAWNPOINT_DM) > 0 then
+ Exit(RESPAWNPOINT_DM);
end;
// Êîìàíäíûå
if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
begin
- // Òî÷êà ñâîåé êîìàíäû
- c := RESPAWNPOINT_DM;
- if FTeam = TEAM_RED then
- c := RESPAWNPOINT_RED;
- if FTeam = TEAM_BLUE then
- c := RESPAWNPOINT_BLUE;
- if g_Map_GetPointCount(c) > 0 then
- begin
- Result := c;
- Exit;
- end;
-
- // Òî÷êà DM
- c := RESPAWNPOINT_DM;
- if g_Map_GetPointCount(c) > 0 then
- begin
- Result := c;
- Exit;
- end;
-
- // Òî÷êà ïîÿâëåíèÿ ëþáîãî èãðîêà
- if Random(2) = 0 then
- c := RESPAWNPOINT_PLAYER1
- else
- c := RESPAWNPOINT_PLAYER2;
- if g_Map_GetPointCount(c) > 0 then
- begin
- Result := c;
- Exit;
- end;
-
- // Òî÷êà äðóãîé êîìàíäû
+ // try team points first
c := RESPAWNPOINT_DM;
if FTeam = TEAM_RED then
+ c := RESPAWNPOINT_RED
+ else if FTeam = TEAM_BLUE then
c := RESPAWNPOINT_BLUE;
- if FTeam = TEAM_BLUE then
- c := RESPAWNPOINT_RED;
if g_Map_GetPointCount(c) > 0 then
- begin
- Result := c;
- Exit;
- end;
+ Exit(c);
end;
+
+ // still haven't found a spawnpoint, try random shit
+ Result := g_Map_GetRandomPointType();
end;
procedure TPlayer.Respawn(Silent: Boolean; Force: Boolean = False);
FPain := 0;
FLastHit := 0;
FSpawnInvul := 0;
+ FCorpse := -1;
if not g_Game_IsServer then
Exit;
GameX := gMapInfo.Width div 2;
GameY := gMapInfo.Height div 2;
end;
+ FXTo := GameX;
+ FYTo := GameY;
FAlive := False;
FSpectator := True;
FPhysics := False;
FWantsInGame := False;
FSpawned := False;
+ FCorpse := -1;
if FNoRespawn then
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;
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
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
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;
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
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;
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
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;
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 FJustTeleported or (NetInterpLevel < 1) then
+ begin
+ FObj.X := XTo;
+ FObj.Y := YTo;
+ if FJustTeleported then
+ begin
+ FObj.oldX := FObj.X;
+ FObj.oldY := FObj.Y;
+ end;
+ 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,
end;
-procedure TCorpse.Damage(Value: Word; vx, vy: Integer);
+procedure TCorpse.Damage(Value: Word; SpawnerUID: Word; vx, vy: Integer);
var
pm: TPlayerModel;
Blood: TModelBlood;
if (gBodyKillEvent <> -1)
and gDelayedEvents[gBodyKillEvent].Pending then
gDelayedEvents[gBodyKillEvent].Pending := False;
- gBodyKillEvent := g_Game_DelayEvent(DE_BODYKILL, 1050, 0);
+ gBodyKillEvent := g_Game_DelayEvent(DE_BODYKILL, 1050, SpawnerUID);
end;
end
else