DEADSOFTWARE

render: separate player logic and drawing
authorDeaDDooMER <deaddoomer@deadsoftware.ru>
Tue, 8 Jun 2021 05:42:00 +0000 (08:42 +0300)
committerDeaDDooMER <deaddoomer@deadsoftware.ru>
Fri, 9 Jun 2023 07:42:07 +0000 (10:42 +0300)
src/game/Doom2DF.lpr
src/game/g_player.pas
src/game/opengl/r_game.pas
src/game/opengl/r_player.pas [new file with mode: 0644]

index 8ee5396710b5d96c35dd0962b55b6c4f2d79af56..5e81dcd199cfd14f9a23f2c88c4cb0b5b1f58450 100644 (file)
@@ -161,6 +161,7 @@ uses
   r_monsters in 'opengl/r_monsters.pas',
   r_netmaster in 'opengl/r_netmaster.pas',
   r_panel in 'opengl/r_panel.pas',
+  r_player in 'opengl/r_player.pas',
   r_weapons in 'opengl/r_weapons.pas',
 
 {$IFDEF USE_FMOD}
index a7afb5c9d17116a2ed8a5624ec881c62124056c7..f31bceed0487a08e2d101ef3c8f633b05a0fb60d 100644 (file)
@@ -104,6 +104,17 @@ const
   PLAYER1_DEF_COLOR: TRGB = (R:64; G:175; B:48);
   PLAYER2_DEF_COLOR: TRGB = (R:96; G:96; B:96);
 
+  AIR_DEF = 360;
+  AIR_MAX = 1091;
+  JET_MAX = 540; // ~30 sec
+  ANGLE_RIGHTUP   = 55;
+  ANGLE_RIGHTDOWN = -35;
+  ANGLE_LEFTUP    = 125;
+  ANGLE_LEFTDOWN  = -145;
+  WEAPONPOINT: Array [TDirection] of TDFPoint = ((X:16; Y:32), (X:47; Y:32));
+  TEAMCOLOR: Array [TEAM_RED..TEAM_BLUE] of TRGB = ((R:255; G:0; B:0),
+                                                   (R:0; G:0; B:255));
+
 type
   TPlayerStat = record
     Num: Integer;
@@ -222,7 +233,6 @@ type
     {procedure CollideItem();}
     procedure FlySmoke(Times: DWORD = 1);
     procedure OnFireFlame(Times: DWORD = 1);
-    function GetAmmoByWeapon(Weapon: Byte): Word;
     procedure SetAction(Action: Byte; Force: Boolean = False);
     procedure OnDamage(Angle: SmallInt); virtual;
     function firediry(): Integer;
@@ -336,14 +346,6 @@ type
     procedure   Spectate(NoMove: Boolean = False);
     procedure   SwitchNoClip;
     procedure   SoftReset();
-    procedure   Draw(); virtual;
-    procedure   DrawPain();
-    procedure   DrawPickup();
-    procedure   DrawRulez();
-    procedure   DrawAim();
-    procedure   DrawIndicator(Color: TRGB);
-    procedure   DrawBubble();
-    procedure   DrawGUI();
     procedure   PreUpdate();
     procedure   Update(); virtual;
     procedure   RememberState();
@@ -371,6 +373,8 @@ type
 
     function getCameraObj(): TObj;
 
+    function GetAmmoByWeapon(Weapon: Byte): Word; // private state
+
   public
     property    Vel: TPoint2i read FObj.Vel;
     property    Obj: TObj read FObj;
@@ -411,6 +415,17 @@ type
     property    JustTeleported: Boolean read FJustTeleported write FJustTeleported;
     property    NetTime: LongWord read FNetTime write FNetTime;
 
+    (* internal state *)
+    property    Angle_: SmallInt read FAngle;
+    property    Spectator: Boolean read FSpectator;
+    property    NoRespawn: Boolean read FNoRespawn;
+    property    Berserk: Integer read FBerserk;
+    property    Pain: Integer read FPain;
+    property    Pickup: Integer read FPickup;
+    property    PunchAnim: TAnimation read FPunchAnim write FPunchAnim;
+    property    SpawnInvul: Integer read FSpawnInvul;
+    property    Ghost: Boolean read FGhost;
+
   published
     property eName: String read FName write FName;
     property eHealth: Integer read FHealth write FHealth;
@@ -498,7 +513,6 @@ type
     procedure   Respawn(Silent: Boolean; Force: Boolean = False); override;
     constructor Create(); override;
     destructor  Destroy(); override;
-    procedure   Draw(); override;
     function    PickItem(ItemType: Byte; force: Boolean; var remove: Boolean): Boolean; override;
     function    Heal(value: Word; Soft: Boolean): Boolean; override;
     procedure   Update(); override;
@@ -555,7 +569,6 @@ type
     destructor  Destroy(); override;
     procedure   Damage(Value: Word; SpawnerUID: Word; vx, vy: Integer);
     procedure   Update();
-    procedure   Draw();
     procedure   SaveState (st: TStream);
     procedure   LoadState (st: TStream);
 
@@ -569,6 +582,11 @@ type
     property    Obj: TObj read FObj; // copies object
     property    State: Byte read FState;
     property    Mess: Boolean read FMess;
+
+    (* internal state *)
+    property    Color: TRGB read FColor;
+    property    Animation: TAnimation read FAnimation;
+    property    AnimationMask: TAnimation read FAnimationMask;
   end;
 
   TTeamStat = Array [TEAM_RED..TEAM_BLUE] of
@@ -615,9 +633,6 @@ 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);
-procedure g_Player_DrawHealth();
 procedure g_Player_RememberAll();
 procedure g_Player_ResetAll(Force, Silent: Boolean);
 function  g_Player_Get(UID: Word): TPlayer;
@@ -628,8 +643,6 @@ 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();
-procedure g_Player_DrawCorpses();
-procedure g_Player_DrawShells();
 procedure g_Player_RemoveAllCorpses();
 procedure g_Player_Corpses_SaveState (st: TStream);
 procedure g_Player_Corpses_LoadState (st: TStream);
@@ -676,26 +689,16 @@ const
   TIME_RESPAWN1 = 1500;
   TIME_RESPAWN2 = 2000;
   TIME_RESPAWN3 = 3000;
-  AIR_DEF = 360;
-  AIR_MAX = 1091;
-  JET_MAX = 540; // ~30 sec
   PLAYER_SUIT_TIME    = 30000;
   PLAYER_INVUL_TIME   = 30000;
   PLAYER_INVIS_TIME   = 35000;
   FRAG_COMBO_TIME = 3000;
   VEL_SW  = 4;
   VEL_FLY = 6;
-  ANGLE_RIGHTUP   = 55;
-  ANGLE_RIGHTDOWN = -35;
-  ANGLE_LEFTUP    = 125;
-  ANGLE_LEFTDOWN  = -145;
   PLAYER_HEADRECT: TRectWH = (X:24; Y:12; Width:20; Height:12);
-  WEAPONPOINT: Array [TDirection] of TDFPoint = ((X:16; Y:32), (X:47; Y:32));
   BOT_MAXJUMP = 84;
   BOT_LONGDIST   = 300;
   BOT_UNSAFEDIST = 128;
-  TEAMCOLOR: Array [TEAM_RED..TEAM_BLUE] of TRGB = ((R:255; G:0; B:0),
-                                                   (R:0; G:0; B:255));
   DIFFICULT_EASY: TDifficult = (DiagFire: 32; InvisFire: 32; DiagPrecision: 32;
                                 FlyPrecision: 32; Cover: 32; CloseJump: 32;
                                 WeaponPrior:(0,0,0,0,0,0,0,0,0,0,0); CloseWeaponPrior:(0,0,0,0,0,0,0,0,0,0,0));
@@ -832,7 +835,7 @@ begin
   ok := False;
   a := 0;
 
-// Åñòü ëè ìåñòî â gPlayers:
+// Есть ли место в gPlayers:
   if gPlayers <> nil then
     for a := 0 to High(gPlayers) do
       if gPlayers[a] = nil then
@@ -841,14 +844,14 @@ begin
         Break;
       end;
 
-// Íåò ìåñòà - ðàñøèðÿåì gPlayers:
+// Нет места - расширяем gPlayers:
   if not ok then
   begin
     SetLength(gPlayers, Length(gPlayers)+1);
     a := High(gPlayers);
   end;
 
-// Ñîçäàåì îáúåêò èãðîêà:
+// Создаем объект игрока:
   if Bot then
     gPlayers[a] := TBot.Create()
   else
@@ -860,7 +863,7 @@ begin
   if Bot and (g_Force_Model_Get() <> 0) then
     gPlayers[a].SetModel(g_Forced_Model_GetName());
 
-// Íåò ìîäåëè - ñîçäàíèå íå âîçìîæíî:
+// Нет модели - создание не возможно:
   if gPlayers[a].FModel = nil then
   begin
     gPlayers[a].Free();
@@ -884,7 +887,7 @@ begin
     GM_COOP: gPlayers[a].FTeam := TEAM_COOP;
   end;
 
-// Åñëè êîìàíäíàÿ èãðà - êðàñèì ìîäåëü â öâåò êîìàíäû:
+// Если командная игра - красим модель в цвет команды:
   gPlayers[a].FColor := Color;
   if gPlayers[a].FTeam in [TEAM_RED, TEAM_BLUE] then
     gPlayers[a].FModel.Color := TEAMCOLOR[gPlayers[a].FTeam]
@@ -977,15 +980,14 @@ var
 begin
   if not g_Game_IsServer then Exit;
 
-// Íå äîáàâëÿåì áîòîâ åñëè ëèìèò óæå äîñòèãíóò
   if (g_Bot_GetCount() >= gMaxBots) then Exit;
 
-// Ñïèñîê íàçâàíèé ìîäåëåé:
+// Список названий моделей:
   m := g_PlayerModel_GetNames();
   if m = nil then
     Exit;
 
-// Êîìàíäà:
+// Команда:
   if (gGameSettings.GameType = GT_SINGLE) or (gGameSettings.GameMode = GM_COOP) then
     Team := TEAM_COOP // COOP
   else
@@ -994,7 +996,7 @@ begin
     else
       if Team = TEAM_NONE then // CTF / TDM
       begin
-       // Àâòîáàëàíñ êîìàíä:
+       // Автобаланс команд:
         tr := 0;
         tb := 0;
 
@@ -1020,7 +1022,7 @@ begin
               Team := TEAM_BLUE;
       end;
 
-// Âûáèðàåì áîòó èìÿ:
+// Выбираем боту имя:
   _name := '';
   if BotNames <> nil then
     for a := 0 to High(BotNames) do
@@ -1030,17 +1032,17 @@ begin
         Break;
       end;
 
-// Âûáèðàåì ñëó÷àéíóþ ìîäåëü:
+// Выбираем случайную модель:
   _model := m[Random(Length(m))];
 
-// Ñîçäàåì áîòà:
+// Создаем бота:
   with g_Player_Get(g_Player_Create(_model,
                                     _RGB(Min(Random(9)*32, 255),
                                          Min(Random(9)*32, 255),
                                          Min(Random(9)*32, 255)),
                                     Team, True)) as TBot do
   begin
-  // Åñëè èìåíè íåò, äåëàåì åãî èç UID áîòà
+  // Если имени нет, делаем его из UID бота
     if _name = '' then
       Name := Format('DFBOT%.5d', [UID])
     else
@@ -1077,15 +1079,14 @@ var
 begin
   if not g_Game_IsServer then Exit;
 
-// Íå äîáàâëÿåì áîòîâ åñëè ëèìèò óæå äîñòèãíóò
   if (g_Bot_GetCount() >= gMaxBots) then Exit;
 
-// Ñïèñîê íàçâàíèé ìîäåëåé:
+// Список названий моделей:
   m := g_PlayerModel_GetNames();
   if m = nil then
     Exit;
 
-// Êîìàíäà:
+// Команда:
   if (gGameSettings.GameType = GT_SINGLE) or (gGameSettings.GameMode = GM_COOP) then
     Team := TEAM_COOP // COOP
   else
@@ -1095,7 +1096,7 @@ begin
       if Team = TEAM_NONE then
         Team := BotList[num].team; // CTF / TDM
 
-// Âûáèðàåì íàñòðîéêè áîòà èç ñïèñêà ïî íîìåðó èëè èìåíè:
+// Выбираем настройки бота из списка по номеру или имени:
   lName := AnsiLowerCase(lName);
   if (num < 0) or (num > Length(BotList)-1) then
     num := -1;
@@ -1109,21 +1110,21 @@ begin
   if num = -1 then
     Exit;
 
-// Èìÿ áîòà:
+// Имя бота:
   _name := BotList[num].name;
-// Çàíÿòî - âûáèðàåì ñëó÷àéíîå:
+// Занято - выбираем случайное:
   if not g_Player_ValidName(_name) then
   repeat
     _name := Format('DFBOT%.2d', [Random(100)]);
   until g_Player_ValidName(_name);
 
-// Ìîäåëü:
+// Модель:
   _model := BotList[num].model;
-// Íåò òàêîé - âûáèðàåì ñëó÷àéíóþ:
+// Нет такой - выбираем случайную:
   if not InSArray(_model, m) then
     _model := m[Random(Length(m))];
 
-// Ñîçäàåì áîòà:
+// Создаем бота:
   with g_Player_Get(g_Player_Create(_model, BotList[num].color, Team, True)) as TBot do
   begin
     Name := _name;
@@ -1222,7 +1223,7 @@ begin
   if e_FindResource(DataDirs, path) = false then
     Exit;
 
-// ×èòàåì âîçìîæíûå èìåíà áîòîâ èç ôàéëà:
+// Читаем возможные имена ботов из файла:
   AssignFile(F, path);
   Reset(F);
 
@@ -1240,10 +1241,10 @@ begin
 
   CloseFile(F);
 
-// Ïåðåìåøèâàåì èõ:
+// Перемешиваем их:
   g_Bot_MixNames();
 
-// ×èòàåì ôàéë ñ ïàðàìåòðàìè áîòîâ:
+// Читаем файл с параметрами ботов:
   config := TConfig.CreateFile(path);
   BotList := nil;
   a := 0;
@@ -1254,38 +1255,38 @@ begin
 
     with BotList[High(BotList)] do
     begin
-    // Èìÿ áîòà:
+    // Имя бота:
       name := config.ReadStr(IntToStr(a), 'name', '');
-    // Ìîäåëü:
+    // Модель:
       model := config.ReadStr(IntToStr(a), 'model', '');
-    // Êîìàíäà:
+    // Команда:
       if config.ReadStr(IntToStr(a), 'team', 'red') = 'red' then
         team := TEAM_RED
       else
         team := TEAM_BLUE;
-    // Öâåò ìîäåëè:
+    // Цвет модели:
       sa := parse(config.ReadStr(IntToStr(a), 'color', ''));
       color.R := StrToIntDef(sa[0], 0);
       color.G := StrToIntDef(sa[1], 0);
       color.B := StrToIntDef(sa[2], 0);
-    // Âåðîÿòíîñòü ñòðåëüáû ïîä óãëîì:
+    // Вероятность стрельбы под углом:
       diag_fire := config.ReadInt(IntToStr(a), 'diag_fire', 0);
-    // Âåðîÿòíîñòü îòâåòíîãî îãíÿ ïî íåâèäèìîìó ñîïåðíèêó:
+    // Вероятность ответного огня по невидимому сопернику:
       invis_fire := config.ReadInt(IntToStr(a), 'invis_fire', 0);
-    // Òî÷íîñòü ñòðåëüáû ïîä óãëîì:
+    // Точность стрельбы под углом:
       diag_precision := config.ReadInt(IntToStr(a), 'diag_precision', 0);
-    // Òî÷íîñòü ñòðåëüáû â ïîëåòå:
+    // Точность стрельбы в полете:
       fly_precision := config.ReadInt(IntToStr(a), 'fly_precision', 0);
-    // Òî÷íîñòü óêëîíåíèÿ îò ñíàðÿäîâ:
+    // Точность уклонения от снарядов:
       cover := config.ReadInt(IntToStr(a), 'cover', 0);
-    // Âåðîÿòíîñòü ïðûæêà ïðè ïðèáëèæåíèè ñîïåðíèêà:
+    // Вероятность прыжка при приближении соперника:
       close_jump := config.ReadInt(IntToStr(a), 'close_jump', 0);
-    // Ïðèîðèòåòû îðóæèÿ äëÿ äàëüíåãî áîÿ:
+    // Приоритеты оружия для дальнего боя:
       sa := parse(config.ReadStr(IntToStr(a), 'w_prior1', ''));
       if Length(sa) = 10 then
         for b := 0 to 9 do
           w_prior1[b] := EnsureRange(StrToInt(sa[b]), 0, 9);
-    // Ïðèîðèòåòû îðóæèÿ äëÿ áëèæíåãî áîÿ:
+    // Приоритеты оружия для ближнего боя:
       sa := parse(config.ReadStr(IntToStr(a), 'w_prior2', ''));
       if Length(sa) = 10 then
         for b := 0 to 9 do
@@ -1364,57 +1365,6 @@ begin
   //e_WriteLog('***g_Player_UpdateAll: EXIT', MSG_WARNING);
 end;
 
-procedure g_Player_DrawAll();
-var
-  i: Integer;
-begin
-  if gPlayers = nil then Exit;
-
-  for i := 0 to High(gPlayers) do
-    if gPlayers[i] <> nil then
-      if gPlayers[i] is TPlayer then gPlayers[i].Draw()
-      else TBot(gPlayers[i]).Draw();
-end;
-
-procedure g_Player_DrawDebug(p: TPlayer);
-var
-  fW, fH: Byte;
-begin
-  if p = nil then Exit;
-  if (@p.FObj) = nil then Exit;
-
-  e_TextureFontGetSize(gStdFont, fW, fH);
-
-  e_TextureFontPrint(0, 0     , 'Pos X: ' + IntToStr(p.FObj.X), gStdFont);
-  e_TextureFontPrint(0, fH    , 'Pos Y: ' + IntToStr(p.FObj.Y), gStdFont);
-  e_TextureFontPrint(0, fH * 2, 'Vel X: ' + IntToStr(p.FObj.Vel.X), gStdFont);
-  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();
-var
-  i: Integer;
-  fW, fH: Byte;
-begin
-  if gPlayers = nil then Exit;
-  e_TextureFontGetSize(gStdFont, fW, fH);
-
-  for i := 0 to High(gPlayers) do
-    if gPlayers[i] <> nil then
-    begin
-      e_TextureFontPrint(gPlayers[i].FObj.X + gPlayers[i].FObj.Rect.X,
-      gPlayers[i].FObj.Y + gPlayers[i].FObj.Rect.Y + gPlayers[i].FObj.Rect.Height - fH * 2,
-      IntToStr(gPlayers[i].FHealth), gStdFont);
-      e_TextureFontPrint(gPlayers[i].FObj.X + gPlayers[i].FObj.Rect.X,
-      gPlayers[i].FObj.Y + gPlayers[i].FObj.Rect.Y + gPlayers[i].FObj.Rect.Height - fH,
-      IntToStr(gPlayers[i].FArmor), gStdFont);
-    end;
-end;
-
 function g_Player_Get(UID: Word): TPlayer;
 var
   a: Integer;
@@ -1552,7 +1502,7 @@ begin
   if Player.alive then
     Exit;
 
-// Ðàçðûâàåì ñâÿçü ñ ïðåæíèì òðóïîì:
+// Ð Ð°Ð·Ñ\80Ñ\8bваем Ñ\81вÑ\8fзÑ\8c Ñ\81 Ð¿Ñ\80ежним Ñ\82Ñ\80Ñ\83пом:
   i := Player.FCorpse;
   if (i >= 0) and (i < Length(gCorpses)) then
   begin
@@ -1699,7 +1649,7 @@ var
   end;
 
 begin
-// Êóñêè ìÿñà:
+// Куски мяса:
   if gGibs <> nil then
     for i := 0 to High(gGibs) do
       if gGibs[i].alive then
@@ -1718,7 +1668,7 @@ begin
             Continue;
           end;
 
-        // Îòëåòàåò îò óäàðà î ñòåíó/ïîòîëîê/ïîë:
+        // Отлетает от удара о стену/потолок/пол:
           if WordBool(mr and MOVE_HITWALL) then
             Obj.Vel.X := -(vel.X div 2);
           if WordBool(mr and (MOVE_HITCEIL or MOVE_HITLAND)) then
@@ -1735,12 +1685,12 @@ begin
               RAngle := (360 - (Abs(RAngle) mod 360)) mod 360;
           end;
 
-        // Ñîïðîòèâëåíèå âîçäóõà äëÿ êóñêà òðóïà:
+        // Сопротивление воздуха для куска трупа:
           if gTime mod (GAME_TICK*3) = 0 then
             Obj.Vel.X := z_dec(Obj.Vel.X, 1);
         end;
 
-// Òðóïû:
+// Трупы:
   if gCorpses <> nil then
     for i := 0 to High(gCorpses) do
       if gCorpses[i] <> nil then
@@ -1752,7 +1702,7 @@ begin
         else
           gCorpses[i].Update();
 
-// Ãèëüçû:
+// Гильзы:
   if gShells <> nil then
     for i := 0 to High(gShells) do
       if gShells[i].alive then
@@ -1771,7 +1721,7 @@ begin
             Continue;
           end;
 
-        // Îòëåòàåò îò óäàðà î ñòåíó/ïîòîëîê/ïîë:
+        // Отлетает от удара о стену/потолок/пол:
           if WordBool(mr and MOVE_HITWALL) then
           begin
             Obj.Vel.X := -(vel.X div 2);
@@ -1847,61 +1797,6 @@ procedure TGib.positionChanged (); inline; begin end;
 procedure TShell.positionChanged (); inline; begin end;
 
 
-procedure g_Player_DrawCorpses();
-var
-  i, fX, fY: Integer;
-  a: TDFPoint;
-begin
-  if gGibs <> nil then
-    for i := 0 to High(gGibs) do
-      if gGibs[i].alive then
-        with gGibs[i] do
-        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, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None);
-
-          e_Colors := Color;
-          e_DrawAdv(MaskID, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None);
-          e_Colors.R := 255;
-          e_Colors.G := 255;
-          e_Colors.B := 255;
-        end;
-
-  if gCorpses <> nil then
-    for i := 0 to High(gCorpses) do
-      if gCorpses[i] <> nil then
-        gCorpses[i].Draw();
-end;
-
-procedure g_Player_DrawShells();
-var
-  i, fX, fY: Integer;
-  a: TDFPoint;
-begin
-  if gShells <> nil then
-    for i := 0 to High(gShells) do
-      if gShells[i].alive then
-        with gShells[i] do
-        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, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None);
-        end;
-end;
-
 procedure g_Player_RemoveAllCorpses();
 var
   i: Integer;
@@ -1925,25 +1820,25 @@ procedure g_Player_Corpses_SaveState (st: TStream);
 var
   count, i: Integer;
 begin
-  // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðóïîâ
+  // Считаем количество существующих трупов
   count := 0;
   for i := 0 to High(gCorpses) do if (gCorpses[i] <> nil) then Inc(count);
 
-  // Êîëè÷åñòâî òðóïîâ
+  // Количество трупов
   utils.writeInt(st, LongInt(count));
 
   if (count = 0) then exit;
 
-  // Ñîõðàíÿåì òðóïû
+  // Сохраняем трупы
   for i := 0 to High(gCorpses) do
   begin
     if gCorpses[i] <> nil then
     begin
-      // Íàçâàíèå ìîäåëè
+      // Название модели
       utils.writeStr(st, gCorpses[i].FModelName);
-      // Òèï ñìåðòè
+      // Тип смерти
       utils.writeBool(st, gCorpses[i].Mess);
-      // Ñîõðàíÿåì äàííûå òðóïà:
+      // Сохраняем данные трупа:
       gCorpses[i].SaveState(st);
     end;
   end;
@@ -1960,22 +1855,22 @@ begin
 
   g_Player_RemoveAllCorpses();
 
-  // Êîëè÷åñòâî òðóïîâ:
+  // Количество трупов:
   count := utils.readLongInt(st);
   if (count < 0) or (count > Length(gCorpses)) then raise XStreamError.Create('invalid number of corpses');
 
   if (count = 0) then exit;
 
-  // Çàãðóæàåì òðóïû
+  // Загружаем трупы
   for i := 0 to count-1 do
   begin
-    // Íàçâàíèå ìîäåëè:
+    // Название модели:
     str := utils.readStr(st);
-    // Òèï ñìåðòè
+    // Тип смерти
     b := utils.readBool(st);
-    // Ñîçäàåì òðóï
+    // Создаем труп
     gCorpses[i] := TCorpse.Create(0, 0, str, b);
-    // Çàãðóæàåì äàííûå òðóïà
+    // Загружаем данные трупа
     gCorpses[i].LoadState(st);
   end;
 end;
@@ -2174,7 +2069,7 @@ begin
      g_Sound_PlayExAt('SOUND_ITEM_GETMED', FObj.X, FObj.Y)
     else g_Sound_PlayExAt('SOUND_ITEM_GETITEM', FObj.X, FObj.Y);
 
-    // Íàäî óáðàòü ñ êàðòû, åñëè ýòî íå êëþ÷, êîòîðûì íóæíî ïîäåëèòñÿ ñ äðóãèì èãðîêîì:
+    // Надо убрать с карты, если это не ключ, которым нужно поделится с другим игроком:
     if r and not ((ItemType in [ITEM_KEY_RED, ITEM_KEY_GREEN, ITEM_KEY_BLUE]) and
                   (gGameSettings.GameType = GT_SINGLE) and
                   (g_Player_GetCount() > 1)) then
@@ -2271,7 +2166,7 @@ begin
 
   FLastHit := t;
 
-// Íåóÿçâèìîñòü íå ñïàñàåò îò ëîâóøåê:
+// Неуязвимость не спасает от ловушек:
   if ((t = HIT_TRAP) or (t = HIT_SELF)) and (not FGodMode) then
   begin
     if not g_Game_IsClient then
@@ -2279,18 +2174,18 @@ begin
       FArmor := 0;
       if t = HIT_TRAP then
       begin
-        // Ëîâóøêà óáèâàåò ñðàçó:
+        // Ловушка убивает сразу:
         FHealth := -100;
         Kill(K_EXTRAHARDKILL, SpawnerUID, t);
       end;
       if t = HIT_SELF then
       begin
-        // Ñàìîóáèéñòâî:
+        // Самоубийство:
         FHealth := 0;
         Kill(K_SIMPLEKILL, SpawnerUID, t);
       end;
     end;
-    // Îáíóëèòü äåéñòâèÿ ïðèìî÷åê, ÷òîáû ôîí ïðîïàë
+    // Обнулить действия примочек, чтобы фон пропал
     FMegaRulez[MR_SUIT] := 0;
     FMegaRulez[MR_INVUL] := 0;
     FMegaRulez[MR_INVIS] := 0;
@@ -2298,22 +2193,22 @@ begin
     FBerserk := 0;
   end;
 
-// Íî îò îñòàëüíîãî ñïàñàåò:
+// Но от остального спасает:
   if FMegaRulez[MR_INVUL] >= gTime then
     Exit;
 
-// ×èò-êîä "ÃÎÐÅÖ":
+// Чит-код "ГОРЕЦ":
   if FGodMode then
     Exit;
 
-// Åñëè åñòü óðîí ñâîèì, èëè ðàíèë ñàì ñåáÿ, èëè òåáÿ ðàíèë ïðîòèâíèê:
+// Если есть урон своим, или ранил сам себя, или тебя ранил противник:
   if LongBool(gGameSettings.Options and GAME_OPTION_TEAMDAMAGE) or
      (SpawnerUID = FUID) or
      (not SameTeam(FUID, SpawnerUID)) then
   begin
     FLastSpawnerUID := SpawnerUID;
 
-  // Êðîâü (ïóçûðüêè, åñëè â âîäå):
+  // Кровь (пузырьки, если в воде):
     if gBloodCount > 0 then
     begin
       c := Min(value, 200)*gBloodCount + Random(Min(value, 200) div 2);
@@ -2340,11 +2235,11 @@ begin
       end;
     end;
 
-  // Áóôåð óðîíà:
+  // Буфер урона:
     if FAlive then
       Inc(FDamageBuffer, value);
 
-  // Âñïûøêà áîëè:
+  // Вспышка боли:
     if gFlash <> 0 then
       FPain := FPain + value;
   end;
@@ -2404,640 +2299,6 @@ begin
   inherited;
 end;
 
-procedure TPlayer.DrawIndicator(Color: TRGB);
-var
-  indX, indY, fX, fY, fSlope: Integer;
-  indW, indH: Word;
-  indA: Single;
-  a: TDFPoint;
-  nW, nH: Byte;
-  ID: DWORD;
-  c: TRGB;
-begin
-  if FAlive then
-  begin
-    FObj.lerp(gLerpFactor, fX, fY);
-    fSlope := nlerp(FSlopeOld, FObj.slopeUpLeft, gLerpFactor);
-
-    case gPlayerIndicatorStyle of
-      0:
-        begin
-          if g_Texture_Get('TEXTURE_PLAYER_INDICATOR', ID) then
-          begin
-            e_GetTextureSize(ID, @indW, @indH);
-            a.X := indW div 2;
-            a.Y := indH div 2;
-
-            if (FObj.X + FObj.Rect.X) < 0 then
-            begin
-              indA := 90;
-              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 := fX + FObj.Rect.X - indH;
-              indY := fY + FObj.Rect.Y + (FObj.Rect.Height - indW) div 2;
-            end
-
-            else if (FObj.Y - indH) < 0 then
-            begin
-              indA := 180;
-              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 := fX + FObj.Rect.X + (FObj.Rect.Width - indW) div 2;
-              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);
-
-            c := e_Colors;
-            e_Colors := Color;
-            e_DrawAdv(ID, indX, indY, 0, True, False, indA, @a);
-            e_Colors := c;
-          end;
-        end;
-
-      1:
-        begin
-          e_TextureFontGetSize(gStdFont, nW, nH);
-          indX := fX + FObj.Rect.X + (FObj.Rect.Width - Length(FName) * nW) div 2;
-          indY := fY - nH + fSlope;
-          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, fX, fY: Integer;
-  ID: LongWord;
-  Rb, Gb, Bb,
-  Rw, Gw, Bw: SmallInt;
-  Dot: Byte;
-  CObj: TObj;
-begin
-  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;
-  Gb := 64;
-  Bb := 64;
-  Rw := 240;
-  Gw := 240;
-  Bw := 240;
-  case gChatBubble of
-    1: // simple textual non-bubble
-    begin
-      bubX := fX+FObj.Rect.X - 11;
-      bubY := fY+FObj.Rect.Y - 17;
-      e_TextureFontPrint(bubX, bubY, '[...]', gStdFont);
-      Exit;
-    end;
-    2: // advanced pixel-perfect bubble
-    begin
-      if FTeam = TEAM_RED then
-        Rb := 255
-      else
-        if FTeam = TEAM_BLUE then
-          Bb := 255;
-    end;
-    3: // colored bubble
-    begin
-      Rb := FModel.Color.R;
-      Gb := FModel.Color.G;
-      Bb := FModel.Color.B;
-      Rw := Min(Rb * 2 + 64, 255);
-      Gw := Min(Gb * 2 + 64, 255);
-      Bw := Min(Bb * 2 + 64, 255);
-      if (Abs(Rw - Rb) < 32)
-      or (Abs(Gw - Gb) < 32)
-      or (Abs(Bw - Bb) < 32) then
-      begin
-        Rb := Max(Rw div 2 - 16, 0);
-        Gb := Max(Gw div 2 - 16, 0);
-        Bb := Max(Bw div 2 - 16, 0);
-      end;
-    end;
-    4: // custom textured bubble
-    begin
-      if g_Texture_Get('TEXTURE_PLAYER_TALKBUBBLE', ID) then
-        if FDirection = TDirection.D_RIGHT then
-          e_Draw(ID, bubX - 6, bubY - 7, 0, True, False)
-        else
-          e_Draw(ID, bubX - 6, bubY - 7, 0, True, False, TMirrorType.Horizontal);
-      Exit;
-    end;
-  end;
-
-  // Outer borders
-  e_DrawQuad(bubX + 1, bubY    , bubX + 18, bubY + 13, Rb, Gb, Bb);
-  e_DrawQuad(bubX    , bubY + 1, bubX + 19, bubY + 12, Rb, Gb, Bb);
-  // Inner box
-  e_DrawFillQuad(bubX + 1, bubY + 1, bubX + 18, bubY + 12, Rw, Gw, Bw, 0);
-
-  // Tail
-  Dot := IfThen(FDirection = TDirection.D_LEFT, 14, 5);
-  e_DrawLine(1, bubX + Dot, bubY + 14, bubX + Dot, bubY + 16, Rb, Gb, Bb);
-  e_DrawLine(1, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 1, Dot + 1), bubY + 13, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 1, Dot + 1), bubY + 15, Rw, Gw, Bw);
-  e_DrawLine(1, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 2, Dot + 2), bubY + 13, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 2, Dot + 2), bubY + 14, Rw, Gw, Bw);
-  e_DrawLine(1, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 3, Dot + 3), bubY + 13, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 3, Dot + 3), bubY + 13, Rw, Gw, Bw);
-  e_DrawLine(1, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 3, Dot + 3), bubY + 14, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 1, Dot + 1), bubY + 16, Rb, Gb, Bb);
-
-  // Dots
-  Dot := 6;
-  e_DrawFillQuad(bubX + Dot,     bubY + 8, bubX + Dot + 1, bubY + 9, Rb, Gb, Bb, 0);
-  e_DrawFillQuad(bubX + Dot + 3, bubY + 8, bubX + Dot + 4, bubY + 9, Rb, Gb, Bb, 0);
-  e_DrawFillQuad(bubX + Dot + 6, bubY + 8, bubX + Dot + 7, bubY + 9, Rb, Gb, Bb, 0);
-end;
-
-procedure TPlayer.Draw();
-var
-  ID: DWORD;
-  w, h: Word;
-  dr: Boolean;
-  Mirror: TMirrorType;
-  fX, fY, fSlope: Integer;
-begin
-  FObj.lerp(gLerpFactor, fX, fY);
-  fSlope := nlerp(FSlopeOld, FObj.slopeUpLeft, gLerpFactor);
-
-  if FAlive then
-  begin
-    if Direction = TDirection.D_RIGHT then
-      Mirror := TMirrorType.None
-    else
-      Mirror := TMirrorType.Horizontal;
-
-    if FPunchAnim <> nil then
-    begin
-      FPunchAnim.Draw(fX+IfThen(Direction = TDirection.D_LEFT, 15-FObj.Rect.X, FObj.Rect.X-15),
-                      fY+fSlope+FObj.Rect.Y-11, Mirror);
-      if FPunchAnim.played then
-      begin
-        FPunchAnim.Free;
-        FPunchAnim := nil;
-      end;
-    end;
-
-    if (FMegaRulez[MR_INVUL] > gTime) and ((gPlayerDrawn <> Self) or (FSpawnInvul >= gTime)) then
-      if g_Texture_Get('TEXTURE_PLAYER_INVULPENTA', ID) then
-      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+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+fSlope, 0, True, False);
-      end;
-
-    if FMegaRulez[MR_INVIS] > gTime then
-    begin
-      if (gPlayerDrawn <> nil) and ((Self = gPlayerDrawn) or
-         ((FTeam = gPlayerDrawn.Team) and (gGameSettings.GameMode <> GM_DM))) then
-      begin
-        if (FMegaRulez[MR_INVIS] - gTime) <= 2100 then
-          dr := not Odd((FMegaRulez[MR_INVIS] - gTime) div 300)
-        else
-          dr := True;
-        if dr then
-          FModel.Draw(fX, fY+fSlope, 200)
-        else
-          FModel.Draw(fX, fY+fSlope);
-      end
-      else
-        FModel.Draw(fX, fY+fSlope, 254);
-    end
-    else
-      FModel.Draw(fX, fY+fSlope);
-  end;
-
-  if g_debug_Frames then
-  begin
-    e_DrawQuad(FObj.X+FObj.Rect.X,
-               FObj.Y+FObj.Rect.Y,
-               FObj.X+FObj.Rect.X+FObj.Rect.Width-1,
-               FObj.Y+FObj.Rect.Y+FObj.Rect.Height-1,
-               0, 255, 0);
-  end;
-
-  if (gChatBubble > 0) and (FKeys[KEY_CHAT].Pressed) and not FGhost then
-    if (FMegaRulez[MR_INVIS] <= gTime) or ((gPlayerDrawn <> nil) and ((Self = gPlayerDrawn) or
-       ((FTeam = gPlayerDrawn.Team) and (gGameSettings.GameMode <> GM_DM)))) then
-      DrawBubble();
- // e_DrawPoint(5, 335, 288, 255, 0, 0); // DL, UR, DL, UR
-  if gAimLine and alive and
-  ((Self = gPlayer1) or (Self = gPlayer2)) then
-    DrawAim();
-end;
-
-
-procedure TPlayer.DrawAim();
-  procedure drawCast (sz: Integer; ax0, ay0, ax1, ay1: Integer);
-  var
-    ex, ey: Integer;
-  begin
-
-{$IFDEF ENABLE_HOLMES}
-    if isValidViewPort and (self = gPlayer1) then
-    begin
-      g_Holmes_plrLaser(ax0, ay0, ax1, ay1);
-    end;
-{$ENDIF}
-
-    e_DrawLine(sz, ax0, ay0, ax1, ay1, 255, 0, 0, 96);
-    if (g_Map_traceToNearestWall(ax0, ay0, ax1, ay1, @ex, @ey) <> nil) then
-    begin
-      e_DrawLine(sz, ax0, ay0, ex, ey, 0, 255, 0, 96);
-    end
-    else
-    begin
-      e_DrawLine(sz, ax0, ay0, ex, ey, 0, 0, 255, 96);
-    end;
-  end;
-
-var
-  wx, wy, xx, yy: Integer;
-  angle: SmallInt;
-  sz, len: Word;
-begin
-  wx := FObj.X + WEAPONPOINT[FDirection].X + IfThen(FDirection = TDirection.D_LEFT, 7, -7);
-  wy := FObj.Y + WEAPONPOINT[FDirection].Y;
-  angle := FAngle;
-  len := 1024;
-  sz := 2;
-  case FCurrWeap of
-    0: begin // Punch
-      len := 12;
-      sz := 4;
-    end;
-    1: begin // Chainsaw
-      len := 24;
-      sz := 6;
-    end;
-    2: begin // Pistol
-      len := 1024;
-      sz := 2;
-      if angle = ANGLE_RIGHTUP then Dec(angle, 2);
-      if angle = ANGLE_RIGHTDOWN then Inc(angle, 4);
-      if angle = ANGLE_LEFTUP then Inc(angle, 2);
-      if angle = ANGLE_LEFTDOWN then Dec(angle, 4);
-    end;
-    3: begin // Shotgun
-      len := 1024;
-      sz := 3;
-      if angle = ANGLE_RIGHTUP then Dec(angle, 2);
-      if angle = ANGLE_RIGHTDOWN then Inc(angle, 4);
-      if angle = ANGLE_LEFTUP then Inc(angle, 2);
-      if angle = ANGLE_LEFTDOWN then Dec(angle, 4);
-    end;
-    4: begin // Double Shotgun
-      len := 1024;
-      sz := 4;
-      if angle = ANGLE_RIGHTUP then Dec(angle, 2);
-      if angle = ANGLE_RIGHTDOWN then Inc(angle, 4);
-      if angle = ANGLE_LEFTUP then Inc(angle, 2);
-      if angle = ANGLE_LEFTDOWN then Dec(angle, 4);
-    end;
-    5: begin // Chaingun
-      len := 1024;
-      sz := 3;
-      if angle = ANGLE_RIGHTUP then Dec(angle, 2);
-      if angle = ANGLE_RIGHTDOWN then Inc(angle, 4);
-      if angle = ANGLE_LEFTUP then Inc(angle, 2);
-      if angle = ANGLE_LEFTDOWN then Dec(angle, 4);
-    end;
-    6: begin // Rocket Launcher
-      len := 1024;
-      sz := 7;
-      if angle = ANGLE_RIGHTUP then Inc(angle, 2);
-      if angle = ANGLE_RIGHTDOWN then Inc(angle, 4);
-      if angle = ANGLE_LEFTUP then Dec(angle, 2);
-      if angle = ANGLE_LEFTDOWN then Dec(angle, 4);
-    end;
-    7: begin // Plasmagun
-      len := 1024;
-      sz := 5;
-      if angle = ANGLE_RIGHTUP then Inc(angle);
-      if angle = ANGLE_RIGHTDOWN then Inc(angle, 3);
-      if angle = ANGLE_LEFTUP then Dec(angle);
-      if angle = ANGLE_LEFTDOWN then Dec(angle, 3);
-    end;
-    8: begin // BFG
-      len := 1024;
-      sz := 12;
-      if angle = ANGLE_RIGHTUP then Inc(angle, 1);
-      if angle = ANGLE_RIGHTDOWN then Inc(angle, 2);
-      if angle = ANGLE_LEFTUP then Dec(angle, 1);
-      if angle = ANGLE_LEFTDOWN then Dec(angle, 2);
-    end;
-    9: begin // Super Chaingun
-      len := 1024;
-      sz := 4;
-      if angle = ANGLE_RIGHTUP then Dec(angle, 2);
-      if angle = ANGLE_RIGHTDOWN then Inc(angle, 4);
-      if angle = ANGLE_LEFTUP then Inc(angle, 2);
-      if angle = ANGLE_LEFTDOWN then Dec(angle, 4);
-    end;
-  end;
-  xx := Trunc(Cos(-DegToRad(angle)) * len) + wx;
-  yy := Trunc(Sin(-DegToRad(angle)) * len) + wy;
-  {$IF DEFINED(D2F_DEBUG)}
-  drawCast(sz, wx, wy, xx, yy);
-  {$ELSE}
-  e_DrawLine(sz, wx, wy, xx, yy, 255, 0, 0, 96);
-  {$ENDIF}
-end;
-
-procedure TPlayer.DrawGUI();
-var
-  ID: DWORD;
-  X, Y, SY, a, p, m: Integer;
-  tw, th: Word;
-  cw, ch: Byte;
-  s: string;
-  stat: TPlayerStatArray;
-begin
-  X := gPlayerScreenSize.X;
-  SY := gPlayerScreenSize.Y;
-  Y := 0;
-
-  if gShowScore and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
-  begin
-    if gGameSettings.GameMode = GM_CTF then
-      a := 32 + 8
-    else
-      a := 0;
-    if gGameSettings.GameMode = GM_CTF then
-    begin
-      s := 'TEXTURE_PLAYER_REDFLAG';
-      if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
-        s := 'TEXTURE_PLAYER_REDFLAG_S';
-      if gFlags[FLAG_RED].State = FLAG_STATE_DROPPED then
-        s := 'TEXTURE_PLAYER_REDFLAG_D';
-      if g_Texture_Get(s, ID) then
-        e_Draw(ID, X-16-32, 240-72-4, 0, True, False);
-    end;
-
-    s := IntToStr(gTeamStat[TEAM_RED].Score);
-    e_CharFont_GetSize(gMenuFont, s, tw, th);
-    e_CharFont_PrintEx(gMenuFont, X-16-a-tw, 240-72-4, s, TEAMCOLOR[TEAM_RED]);
-
-    if gGameSettings.GameMode = GM_CTF then
-    begin
-      s := 'TEXTURE_PLAYER_BLUEFLAG';
-      if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
-        s := 'TEXTURE_PLAYER_BLUEFLAG_S';
-      if gFlags[FLAG_BLUE].State = FLAG_STATE_DROPPED then
-        s := 'TEXTURE_PLAYER_BLUEFLAG_D';
-      if g_Texture_Get(s, ID) then
-        e_Draw(ID,  X-16-32, 240-32-4, 0, True, False);
-    end;
-
-    s := IntToStr(gTeamStat[TEAM_BLUE].Score);
-    e_CharFont_GetSize(gMenuFont, s, tw, th);
-    e_CharFont_PrintEx(gMenuFont, X-16-a-tw, 240-32-4, s, TEAMCOLOR[TEAM_BLUE]);
-  end;
-
-  if g_Texture_Get('TEXTURE_PLAYER_HUDBG', ID) then
-    e_DrawFill(ID, X, 0, 1, (gPlayerScreenSize.Y div 256)+IfThen(gPlayerScreenSize.Y mod 256 > 0, 1, 0),
-               0, False, False);
-
-  if g_Texture_Get('TEXTURE_PLAYER_HUD', ID) then
-    e_Draw(ID, X+2, Y, 0, True, False);
-
-  if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
-  begin
-    if gShowStat then
-    begin
-      s := IntToStr(Frags);
-      e_CharFont_GetSize(gMenuFont, s, tw, th);
-      e_CharFont_PrintEx(gMenuFont, X-16-tw, Y, s, _RGB(255, 0, 0));
-
-      s := '';
-      p := 1;
-      m := 0;
-      stat := g_Player_GetStats();
-      if stat <> nil then
-      begin
-        p := 1;
-
-        for a := 0 to High(stat) do
-          if stat[a].Name <> Name then
-          begin
-            if stat[a].Frags > m then m := stat[a].Frags;
-            if stat[a].Frags > Frags then p := p+1;
-          end;
-      end;
-
-      s := IntToStr(p)+' / '+IntToStr(Length(stat))+' ';
-      if Frags >= m then s := s+'+' else s := s+'-';
-      s := s+IntToStr(Abs(Frags-m));
-
-      e_CharFont_GetSize(gMenuSmallFont, s, tw, th);
-      e_CharFont_PrintEx(gMenuSmallFont, X-16-tw, Y+32, s, _RGB(255, 0, 0));
-    end;
-
-    if gLMSRespawn > LMS_RESPAWN_NONE then
-    begin
-      s := _lc[I_GAME_WARMUP];
-      e_CharFont_GetSize(gMenuFont, s, tw, th);
-      s := s + ': ' + IntToStr((gLMSRespawnTime - gTime) div 1000);
-      e_CharFont_PrintEx(gMenuFont, X-64-tw, SY-32, s, _RGB(0, 255, 0));
-    end
-    else if gShowLives and (gGameSettings.MaxLives > 0) then
-    begin
-      s := IntToStr(Lives);
-      e_CharFont_GetSize(gMenuFont, s, tw, th);
-      e_CharFont_PrintEx(gMenuFont, X-16-tw, SY-32, s, _RGB(0, 255, 0));
-    end;
-  end;
-
-  e_CharFont_GetSize(gMenuSmallFont, FName, tw, th);
-  e_CharFont_PrintEx(gMenuSmallFont, X+98-(tw div 2), Y+8, FName, _RGB(255, 0, 0));
-
-  if R_BERSERK in FRulez then
-    e_Draw(gItemsTexturesID[ITEM_MEDKIT_BLACK], X+37, Y+45, 0, True, False)
-  else
-    e_Draw(gItemsTexturesID[ITEM_MEDKIT_LARGE], X+37, Y+45, 0, True, False);
-
-  if g_Texture_Get('TEXTURE_PLAYER_ARMORHUD', ID) then
-    e_Draw(ID, X+36, Y+77, 0, True, False);
-
-  s := IntToStr(IfThen(FHealth > 0, FHealth, 0));
-  e_CharFont_GetSize(gMenuFont, s, tw, th);
-  e_CharFont_PrintEx(gMenuFont, X+178-tw, Y+40, s, _RGB(255, 0, 0));
-
-  s := IntToStr(FArmor);
-  e_CharFont_GetSize(gMenuFont, s, tw, th);
-  e_CharFont_PrintEx(gMenuFont, X+178-tw, Y+68, s, _RGB(255, 0, 0));
-
-  s := IntToStr(GetAmmoByWeapon(FCurrWeap));
-
-  case FCurrWeap of
-    WEAPON_KASTET:
-    begin
-      s := '--';
-      ID := gItemsTexturesID[ITEM_WEAPON_KASTET];
-    end;
-    WEAPON_SAW:
-    begin
-      s := '--';
-      ID := gItemsTexturesID[ITEM_WEAPON_SAW];
-    end;
-    WEAPON_PISTOL: ID := gItemsTexturesID[ITEM_WEAPON_PISTOL];
-    WEAPON_CHAINGUN: ID := gItemsTexturesID[ITEM_WEAPON_CHAINGUN];
-    WEAPON_SHOTGUN1: ID := gItemsTexturesID[ITEM_WEAPON_SHOTGUN1];
-    WEAPON_SHOTGUN2: ID := gItemsTexturesID[ITEM_WEAPON_SHOTGUN2];
-    WEAPON_SUPERPULEMET: ID := gItemsTexturesID[ITEM_WEAPON_SUPERPULEMET];
-    WEAPON_ROCKETLAUNCHER: ID := gItemsTexturesID[ITEM_WEAPON_ROCKETLAUNCHER];
-    WEAPON_PLASMA: ID := gItemsTexturesID[ITEM_WEAPON_PLASMA];
-    WEAPON_BFG: ID := gItemsTexturesID[ITEM_WEAPON_BFG];
-    WEAPON_FLAMETHROWER: ID := gItemsTexturesID[ITEM_WEAPON_FLAMETHROWER];
-  end;
-
-  e_CharFont_GetSize(gMenuFont, s, tw, th);
-  e_CharFont_PrintEx(gMenuFont, X+178-tw, Y+158, s, _RGB(255, 0, 0));
-  e_Draw(ID, X+20, Y+160, 0, True, False);
-
-  if R_KEY_RED in FRulez then
-    e_Draw(gItemsTexturesID[ITEM_KEY_RED], X+78, Y+214, 0, True, False);
-
-  if R_KEY_GREEN in FRulez then
-    e_Draw(gItemsTexturesID[ITEM_KEY_GREEN], X+95, Y+214, 0, True, False);
-
-  if R_KEY_BLUE in FRulez then
-    e_Draw(gItemsTexturesID[ITEM_KEY_BLUE], X+112, Y+214, 0, True, False);
-
-  if FJetFuel > 0 then
-  begin
-    if g_Texture_Get('TEXTURE_PLAYER_HUDAIR', ID) then
-      e_Draw(ID, X+2, Y+116, 0, True, False);
-    if g_Texture_Get('TEXTURE_PLAYER_HUDJET', ID) then
-      e_Draw(ID, X+2, Y+126, 0, True, False);
-    e_DrawLine(4, X+16, Y+122, X+16+Trunc(168*IfThen(FAir > 0, FAir, 0)/AIR_MAX), Y+122, 0, 0, 196);
-    e_DrawLine(4, X+16, Y+132, X+16+Trunc(168*FJetFuel/JET_MAX), Y+132, 208, 0, 0);
-  end
-  else
-  begin
-    if g_Texture_Get('TEXTURE_PLAYER_HUDAIR', ID) then
-      e_Draw(ID, X+2, Y+124, 0, True, False);
-    e_DrawLine(4, X+16, Y+130, X+16+Trunc(168*IfThen(FAir > 0, FAir, 0)/AIR_MAX), Y+130, 0, 0, 196);
-  end;
-
-  if gShowPing and g_Game_IsClient then
-  begin
-    s := _lc[I_GAME_PING_HUD] + IntToStr(NetPeer.lastRoundTripTime) + _lc[I_NET_SLIST_PING_MS];
-    e_TextureFontPrint(X + 4, Y + 242, s, gStdFont);
-    Y := Y + 16;
-  end;
-
-  if FSpectator then
-  begin
-    e_TextureFontPrint(X + 4, Y + 242, _lc[I_PLAYER_SPECT], gStdFont);
-    e_TextureFontPrint(X + 4, Y + 258, _lc[I_PLAYER_SPECT2], gStdFont);
-    e_TextureFontPrint(X + 4, Y + 274, _lc[I_PLAYER_SPECT1], gStdFont);
-    if FNoRespawn then
-    begin
-      e_TextureFontGetSize(gStdFont, cw, ch);
-      s := _lc[I_PLAYER_SPECT4];
-      e_TextureFontPrintEx(gScreenWidth div 2 - cw*(Length(s) div 2),
-                         gScreenHeight-4-ch, s, gStdFont, 255, 255, 255, 1, True);
-      e_TextureFontPrint(X + 4, Y + 290, _lc[I_PLAYER_SPECT1S], gStdFont);
-    end;
-
-  end;
-end;
-
-procedure TPlayer.DrawRulez();
-var
-  dr: Boolean;
-begin
-  // Ïðè âçÿòèè íåóÿçâèìîñòè ðèñóåòñÿ èíâåðñèîííûé áåëûé ôîí
-  if (FMegaRulez[MR_INVUL] >= gTime) and (FSpawnInvul < gTime) then
-  begin
-    if (FMegaRulez[MR_INVUL]-gTime) <= 2100 then
-      dr := not Odd((FMegaRulez[MR_INVUL]-gTime) div 300)
-    else
-      dr := True;
-
-    if dr then
-      e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1,
-                     191, 191, 191, 0, TBlending.Invert);
-  end;
-
-  // Ïðè âçÿòèè çàùèòíîãî êîñòþìà ðèñóåòñÿ çåëåíîâàòûé ôîí
-  if FMegaRulez[MR_SUIT] >= gTime then
-  begin
-    if (FMegaRulez[MR_SUIT]-gTime) <= 2100 then
-      dr := not Odd((FMegaRulez[MR_SUIT]-gTime) div 300)
-    else
-      dr := True;
-
-    if dr then
-      e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1,
-                     0, 96, 0, 200, TBlending.None);
-  end;
-
-  // Ïðè âçÿòèè áåðñåðêà ðèñóåòñÿ êðàñíîâàòûé ôîí
-  if (FBerserk >= 0) and (LongWord(FBerserk) >= gTime) and (gFlash = 2) then
-  begin
-    e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1,
-                     255, 0, 0, 200, TBlending.None);
-  end;
-end;
-
-procedure TPlayer.DrawPain();
-var
-  a, h: Integer;
-begin
-  if FPain = 0 then Exit;
-
-  a := FPain;
-
-  if a < 15 then h := 0
-  else if a < 35 then h := 1
-  else if a < 55 then h := 2
-  else if a < 75 then h := 3
-  else if a < 95 then h := 4
-  else h := 5;
-
-  //if a > 255 then a := 255;
-
-  e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 255, 0, 0, 255-h*50);
-  //e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 255-min(128, a), 255-a, 255-a, 0, B_FILTER);
-end;
-
-procedure TPlayer.DrawPickup();
-var
-  a, h: Integer;
-begin
-  if FPickup = 0 then Exit;
-
-  a := FPickup;
-
-  if a < 15 then h := 1
-  else if a < 35 then h := 2
-  else if a < 55 then h := 3
-  else if a < 75 then h := 4
-  else h := 5;
-
-  e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 150, 200, 150, 255-h*50);
-end;
-
 procedure TPlayer.DoPunch();
 var
   id: DWORD;
@@ -3066,8 +2327,8 @@ var
   locobj: TObj;
 begin
   if g_Game_IsClient then Exit;
-// FBFGFireCounter - âðåìÿ ïåðåä âûñòðåëîì (äëÿ BFG)
-// FReloading - âðåìÿ ïîñëå âûñòðåëà (äëÿ âñåãî)
+// FBFGFireCounter - время перед выстрелом (для BFG)
+// FReloading - время после выстрела (для всего)
 
   if FSpectator then
   begin
@@ -3348,9 +2609,9 @@ begin
   if Timeout <= 0 then
     exit;
   if (FMegaRulez[MR_SUIT] > gTime) or (FMegaRulez[MR_INVUL] > gTime) then
-    exit; // Íå çàãîðàåìñÿ êîãäà åñòü çàùèòà
+    exit; // Не загораемся когда есть защита
   if g_Obj_CollidePanel(@FObj, 0, 0, PANEL_WATER or PANEL_ACID1 or PANEL_ACID2) then
-    exit; // Íå ïîäãîðàåì â âîäå íà âñÿêèé ñëó÷àé
+    exit; // Не подгораем в воде на всякий случай
   if FFireTime <= 0 then
     g_Sound_PlayExAt('SOUND_IGNITE', FObj.X, FObj.Y);
   FFireTime := Timeout;
@@ -3363,7 +2624,7 @@ procedure TPlayer.Jump();
 begin
   if gFly or FJetpack then
   begin
-    // Ïîëåò (÷èò-êîä èëè äæåòïàê):
+    // Полет (чит-код или джетпак):
     if FObj.Vel.Y > -VEL_FLY then
       FObj.Vel.Y := FObj.Vel.Y - 3;
     if FJetpack then
@@ -3381,15 +2642,15 @@ begin
     Exit;
   end;
 
-// Íå âêëþ÷àòü äæåòïàê â ðåæèìå ïðîõîæäåíèÿ ñêâîçü ñòåíû
+// Не включать джетпак в режиме прохождения сквозь стены
   if FGhost then
     FCanJetpack := False;
 
-// Ïðûãàåì èëè âñïëûâàåì:
+// Прыгаем или всплываем:
   if (CollideLevel(0, 1) or
       g_Map_CollidePanel(FObj.X+PLAYER_RECT.X, FObj.Y+PLAYER_RECT.Y+36, PLAYER_RECT.Width,
                        PLAYER_RECT.Height-33, PANEL_STEP, False)
-     ) and (FObj.Accel.Y = 0) then // Íå ïðûãàòü, åñëè åñòü âåðòèêàëüíîå óñêîðåíèå
+     ) and (FObj.Accel.Y = 0) then // Не прыгать, если есть вертикальное ускорение
   begin
     FObj.Vel.Y := -VEL_JUMP;
     FCanJetpack := False;
@@ -3473,7 +2734,7 @@ begin
     if FLives = 0 then FNoRespawn := True;
   end;
 
-// Íîìåð òèïà ñìåðòè:
+// Номер типа смерти:
   a := 1;
   case KillType of
     K_SIMPLEKILL:    a := 1;
@@ -3482,13 +2743,13 @@ begin
     K_FALLKILL:      a := 4;
   end;
 
-// Çâóê ñìåðòè:
+// Звук смерти:
   if not FModel.PlaySound(MODELSOUND_DIE, a, FObj.X, FObj.Y) then
     for i := 1 to 3 do
       if FModel.PlaySound(MODELSOUND_DIE, i, FObj.X, FObj.Y) then
         Break;
 
-// Âðåìÿ ðåñïàóíà:
+// Время респауна:
   if Srv then
     case KillType of
       K_SIMPLEKILL:
@@ -3499,7 +2760,7 @@ begin
         FTime[T_RESPAWN] := gTime + TIME_RESPAWN3;
     end;
 
-// Ïåðåêëþ÷àåì ñîñòîÿíèå:
+// Переключаем состояние:
   case KillType of
     K_SIMPLEKILL:
       SetAction(A_DIE1);
@@ -3507,12 +2768,12 @@ begin
       SetAction(A_DIE2);
   end;
 
-// Ðåàêöèÿ ìîíñòðîâ íà ñìåðòü èãðîêà:
+// Ð ÐµÐ°ÐºÑ\86иÑ\8f Ð¼Ð¾Ð½Ñ\81Ñ\82Ñ\80ов Ð½Ð° Ñ\81меÑ\80Ñ\82Ñ\8c Ð¸Ð³Ñ\80ока:
   if (KillType <> K_FALLKILL) and (Srv) then
     g_Monsters_killedp();
 
   if SpawnerUID = FUID then
-    begin // Ñàìîóáèëñÿ
+    begin // Самоубился
       if Srv then
       begin
         if gGameSettings.GameMode = GM_TDM then
@@ -3527,7 +2788,7 @@ begin
     end
   else
     if g_GetUIDType(SpawnerUID) = UID_PLAYER then
-      begin // Óáèò äðóãèì èãðîêîì
+      begin // Убит другим игроком
         KP := g_Player_Get(SpawnerUID);
         if (KP <> nil) and Srv then
         begin
@@ -3571,7 +2832,7 @@ begin
         end;
       end
     else if g_GetUIDType(SpawnerUID) = UID_MONSTER then
-      begin // Óáèò ìîíñòðîì
+      begin // Убит монстром
         mon := g_Monsters_ByUID(SpawnerUID);
         if mon = nil then
           s := '?'
@@ -3593,7 +2854,7 @@ begin
                           gShowKillMsg);
         end;
       end
-    else // Îñîáûå òèïû ñìåðòè
+    else // Особые типы смерти
       case t of
         HIT_DISCON: ;
         HIT_SELF: g_Console_Add(Format(_lc[I_PLAYER_KILL_SELF], [FName]), True);
@@ -3606,7 +2867,7 @@ begin
 
   if Srv then
   begin
-// Âûáðîñ îðóæèÿ:
+// Выброс оружия:
     for a := WP_FIRST to WP_LAST do
       if FWeapon[a] then
       begin
@@ -3627,15 +2888,15 @@ begin
           PushItem(i);
       end;
 
-// Âûáðîñ ðþêçàêà:
+// Выброс рюкзака:
     if R_ITEM_BACKPACK in FRulez then
       PushItem(ITEM_AMMO_BACKPACK);
 
-// Âûáðîñ ðàêåòíîãî ðàíöà:
+// Выброс ракетного ранца:
     if FJetFuel > 0 then
       PushItem(ITEM_JETPACK);
 
-// Âûáðîñ êëþ÷åé:
+// Выброс ключей:
     if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) or
        (not LongBool(gGameSettings.Options and GAME_OPTION_DMKEYS)) then
     begin
@@ -3649,7 +2910,7 @@ begin
         PushItem(ITEM_KEY_BLUE);
     end;
 
-// Âûáðîñ ôëàãà:
+// Выброс флага:
     DropFlag(KillType = K_FALLKILL);
   end;
 
@@ -4044,7 +3305,7 @@ begin
   Result := False;
   if g_Game_IsClient then Exit;
 
-  // a = true - ìåñòî ñïàâíà ïðåäìåòà:
+  // a = true - место спавна предмета:
   a := LongBool(gGameSettings.Options and GAME_OPTION_WEAPONSTAY) and arespawn;
   remove := not a;
   case ItemType of
@@ -4123,7 +3384,7 @@ begin
     ITEM_WEAPON_SHOTGUN1:
       if (FAmmo[A_SHELLS] < FMaxAmmo[A_SHELLS]) or not FWeapon[WEAPON_SHOTGUN1] then
       begin
-        // Íóæíî, ÷òîáû íå âçÿòü âñå ïóëè ñðàçó:
+        // Нужно, чтобы не взять все пули сразу:
         if a and FWeapon[WEAPON_SHOTGUN1] then Exit;
         hadWeapon := FWeapon[WEAPON_SHOTGUN1];
         switchWeapon := WEAPON_SHOTGUN1;
@@ -4476,7 +3737,7 @@ begin
   //FModel.PlaySound(MODELSOUND_PAIN, 1, FObj.X, FObj.Y);
   if FIamBot then
   begin
-  // Áðîñèòü ôëàã òîâàðèùó:
+  // Бросить флаг товарищу:
     if gGameSettings.GameMode = GM_CTF then
       DropFlag();
   end;
@@ -4550,9 +3811,9 @@ var
   c: Byte;
 begin
   Result := 255;
-  // Íà áóäóùåå: FSpawn - èãðîê óæå èãðàë è ïåðåðîæäàåòñÿ
+  // На будущее: FSpawn - игрок уже играл и перерождается
 
-  // Îäèíî÷íàÿ èãðà/êîîïåðàòèâ
+  // Одиночная игра/кооператив
   if gGameSettings.GameMode in [GM_COOP, GM_SINGLE] then
   begin
     if Self = gPlayer1 then
@@ -4584,7 +3845,7 @@ begin
     end;
   end;
 
-  // Ìÿñîïîâàë
+  // Мясоповал
   if gGameSettings.GameMode = GM_DM then
   begin
     // try DM points first
@@ -4592,7 +3853,7 @@ begin
       Exit(RESPAWNPOINT_DM);
   end;
 
-  // Êîìàíäíûå
+  // Командные
   if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
   begin
     // try team points first
@@ -4641,11 +3902,11 @@ begin
   // if server changes MaxLives we gotta be ready
   if gGameSettings.MaxLives = 0 then FNoRespawn := False;
 
-// Åùå íåëüçÿ âîçðîäèòüñÿ:
+// Еще нельзя возродиться:
   if FTime[T_RESPAWN] > gTime then
     Exit;
 
-// Ïðîñðàë âñå æèçíè:
+// Просрал все жизни:
   if FNoRespawn then
   begin
     if not FSpectator then Spectate(True);
@@ -4654,23 +3915,23 @@ begin
   end;
 
   if (gGameSettings.GameType <> GT_SINGLE) and (gGameSettings.GameMode <> GM_COOP) then
-    begin // "Ñâîÿ èãðà"
-    // Áåðñåðê íå ñîõðàíÿåòñÿ ìåæäó óðîâíÿìè:
+    begin // "Своя игра"
+    // Берсерк не сохраняется между уровнями:
       FRulez := FRulez-[R_BERSERK];
     end
-  else // "Îäèíî÷íàÿ èãðà"/"Êîîï"
+  else // "Одиночная игра"/"Кооп"
     begin
-    // Áåðñåðê è êëþ÷è íå ñîõðàíÿþòñÿ ìåæäó óðîâíÿìè:
+    // Берсерк и ключи не сохраняются между уровнями:
       FRulez := FRulez-[R_KEY_RED, R_KEY_GREEN, R_KEY_BLUE, R_BERSERK];
     end;
 
-// Ïîëó÷àåì òî÷êó ñïàóíà èãðîêà:
+// Получаем точку спауна игрока:
   c := GetRespawnPoint();
 
   ReleaseKeys();
   SetFlag(FLAG_NONE);
 
-// Âîñêðåøåíèå áåç îðóæèÿ:
+// Воскрешение без оружия:
   if not FAlive then
   begin
     FHealth := Round(PLAYER_HP_SOFT * (FHandicap / 100));
@@ -4710,14 +3971,14 @@ begin
       FRulez := [];
   end;
 
-// Ïîëó÷àåì êîîðäèíàòû òî÷êè âîçðîæäåíèÿ:
+// Получаем координаты точки возрождения:
   if not g_Map_GetPoint(c, RespawnPoint) then
   begin
     g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
     Exit;
   end;
 
-// Óñòàíîâêà êîîðäèíàò è ñáðîñ âñåõ ïàðàìåòðîâ:
+// Установка координат и сброс всех параметров:
   FObj.X := RespawnPoint.X-PLAYER_RECT.X;
   FObj.Y := RespawnPoint.Y-PLAYER_RECT.Y;
   FObj.oldX := FObj.X; // don't interpolate after respawn
@@ -4757,7 +4018,7 @@ begin
   FFirePainTime := 0;
   FFireAttacker := 0;
 
-// Àíèìàöèÿ âîçðîæäåíèÿ:
+// Анимация возрождения:
   if (not gLoadGameMode) and (not Silent) then
     if g_Frames_Get(ID, 'FRAMES_TELEPORT') then
     begin
@@ -4851,7 +4112,7 @@ begin
   if MAX_RUNVEL > 8 then
     FlySmoke();
 
-// Áåæèì:
+// Бежим:
   if Direction = TDirection.D_LEFT then
     begin
       if FObj.Vel.X > -MAX_RUNVEL then
@@ -4861,7 +4122,7 @@ begin
     if FObj.Vel.X < MAX_RUNVEL then
       FObj.Vel.X := FObj.Vel.X + (MAX_RUNVEL shr 3);
 
-// Âîçìîæíî, ïèíàåì êóñêè:
+// Возможно, пинаем куски:
   if (FObj.Vel.X <> 0) and (gGibs <> nil) then
   begin
     b := Abs(FObj.Vel.X);
@@ -4872,14 +4133,14 @@ begin
          g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4,
                        FObj.Rect.Width, 8, @gGibs[a].Obj) and (Random(3) = 0) then
       begin
-        // Ïèíàåì êóñêè
+        // Пинаем куски
         if FObj.Vel.X < 0 then
         begin
-          g_Obj_PushA(@gGibs[a].Obj, b, Random(61)+120) // íàëåâî
+          g_Obj_PushA(@gGibs[a].Obj, b, Random(61)+120) // налево
         end
         else
         begin
-          g_Obj_PushA(@gGibs[a].Obj, b, Random(61));    // íàïðàâî
+          g_Obj_PushA(@gGibs[a].Obj, b, Random(61));    // направо
         end;
         gGibs[a].positionChanged(); // this updates spatial accelerators
       end;
@@ -5003,7 +4264,7 @@ begin
       end
       else
         if dir = 3 then
-        begin // îáðàòíîå
+        begin // обратное
           if FDirection = TDirection.D_RIGHT then
           begin
             SetDirection(TDirection.D_LEFT);
@@ -5309,7 +4570,7 @@ begin
                                  PANEL_BLOCKMON, True);
   headwater := HeadInLiquid(0, 0);
 
-// Ñîïðîòèâëåíèå âîçäóõà:
+// Сопротивление воздуха:
   if (not FAlive) or not (FKeys[KEY_LEFT].Pressed or FKeys[KEY_RIGHT].Pressed) then
     if FObj.Vel.X <> 0 then
       FObj.Vel.X := z_dec(FObj.Vel.X, 1);
@@ -5320,7 +4581,7 @@ begin
 
   if FAlive and (FObj.Y > Integer(gMapInfo.Height)+128) and AnyServer then
   begin
-    // Îáíóëèòü äåéñòâèÿ ïðèìî÷åê, ÷òîáû ôîí ïðîïàë
+    // Обнулить действия примочек, чтобы фон пропал
     FMegaRulez[MR_SUIT] := 0;
     FMegaRulez[MR_INVUL] := 0;
     FMegaRulez[MR_INVIS] := 0;
@@ -5805,7 +5066,7 @@ begin
 
   if not g_Game_IsServer then Exit;
 
-// Ïðèíåñ ÷óæîé ôëàã íà ñâîþ áàçó:
+// Принес чужой флаг на свою базу:
   if (Flag = FTeam) and
      (gFlags[Flag].State = FLAG_STATE_NORMAL) and
      (FFlag <> FLAG_NONE) then
@@ -5848,7 +5109,7 @@ begin
     Exit;
   end;
 
-// Ïîäîáðàë ñâîé ôëàã - âåðíóë åãî íà áàçó:
+// Подобрал свой флаг - вернул его на базу:
   if (Flag = FTeam) and
      (gFlags[Flag].State = FLAG_STATE_DROPPED) then
   begin
@@ -5884,7 +5145,7 @@ begin
     Exit;
   end;
 
-// Ïîäîáðàë ÷óæîé ôëàã:
+// Подобрал чужой флаг:
   if (Flag <> FTeam) and (FTime[T_FLAGCAP] <= gTime) then
   begin
     SetFlag(Flag);
@@ -6110,95 +5371,95 @@ var
   i: Integer;
   b: Byte;
 begin
-  // Ñèãíàòóðà èãðîêà
+  // Сигнатура игрока
   utils.writeSign(st, 'PLYR');
   utils.writeInt(st, Byte(PLR_SAVE_VERSION)); // version
-  // Áîò èëè ÷åëîâåê
+  // Бот или человек
   utils.writeBool(st, FIamBot);
-  // UID èãðîêà
+  // UID игрока
   utils.writeInt(st, Word(FUID));
-  // Èìÿ èãðîêà
+  // Имя игрока
   utils.writeStr(st, FName);
-  // Êîìàíäà
+  // Команда
   utils.writeInt(st, Byte(FTeam));
-  // Æèâ ëè
+  // Жив ли
   utils.writeBool(st, FAlive);
-  // Èçðàñõîäîâàë ëè âñå æèçíè
+  // Израсходовал ли все жизни
   utils.writeBool(st, FNoRespawn);
-  // Íàïðàâëåíèå
+  // Направление
   if FDirection = TDirection.D_LEFT then b := 1 else b := 2; // D_RIGHT
   utils.writeInt(st, Byte(b));
-  // Çäîðîâüå
+  // Здоровье
   utils.writeInt(st, LongInt(FHealth));
-  // Êîýôôèöèåíò èíâàëèäíîñòè
+  // Коэффициент инвалидности
   utils.writeInt(st, LongInt(FHandicap));
-  // Æèçíè
+  // Жизни
   utils.writeInt(st, Byte(FLives));
-  // Áðîíÿ
+  // Броня
   utils.writeInt(st, LongInt(FArmor));
-  // Çàïàñ âîçäóõà
+  // Запас воздуха
   utils.writeInt(st, LongInt(FAir));
-  // Çàïàñ ãîðþ÷åãî
+  // Запас горючего
   utils.writeInt(st, LongInt(FJetFuel));
-  // Áîëü
+  // Боль
   utils.writeInt(st, LongInt(FPain));
-  // Óáèë
+  // Убил
   utils.writeInt(st, LongInt(FKills));
-  // Óáèë ìîíñòðîâ
+  // Убил монстров
   utils.writeInt(st, LongInt(FMonsterKills));
-  // Ôðàãîâ
+  // Фрагов
   utils.writeInt(st, LongInt(FFrags));
-  // Ôðàãîâ ïîäðÿä
+  // Фрагов подряд
   utils.writeInt(st, Byte(FFragCombo));
-  // Âðåìÿ ïîñëåäíåãî ôðàãà
+  // Время последнего фрага
   utils.writeInt(st, LongWord(FLastFrag));
-  // Ñìåðòåé
+  // Смертей
   utils.writeInt(st, LongInt(FDeath));
-  // Êàêîé ôëàã íåñåò
+  // Какой флаг несет
   utils.writeInt(st, Byte(FFlag));
-  // Íàøåë ñåêðåòîâ
+  // Нашел секретов
   utils.writeInt(st, LongInt(FSecrets));
-  // Òåêóùåå îðóæèå
+  // Текущее оружие
   utils.writeInt(st, Byte(FCurrWeap));
-  // Æåëàåìîå îðóæèå
+  // Желаемое оружие
   utils.writeInt(st, Word(FNextWeap));
-  // ...è ïàóçà
+  // ...и пауза
   utils.writeInt(st, Byte(FNextWeapDelay));
-  // Âðåìÿ çàðÿäêè BFG
+  // Время зарядки BFG
   utils.writeInt(st, SmallInt(FBFGFireCounter));
-  // Áóôåð óðîíà
+  // Буфер урона
   utils.writeInt(st, LongInt(FDamageBuffer));
-  // Ïîñëåäíèé óäàðèâøèé
+  // Последний ударивший
   utils.writeInt(st, Word(FLastSpawnerUID));
-  // Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà
+  // Тип последнего полученного урона
   utils.writeInt(st, Byte(FLastHit));
-  // Îáúåêò èãðîêà
+  // Объект игрока
   Obj_SaveState(st, @FObj);
-  // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ
+  // Текущее количество патронов
   for i := A_BULLETS to A_HIGH do utils.writeInt(st, Word(FAmmo[i]));
-  // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ
+  // Максимальное количество патронов
   for i := A_BULLETS to A_HIGH do utils.writeInt(st, Word(FMaxAmmo[i]));
-  // Íàëè÷èå îðóæèÿ
+  // Наличие оружия
   for i := WP_FIRST to WP_LAST do utils.writeBool(st, FWeapon[i]);
-  // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ
+  // Время перезарядки оружия
   for i := WP_FIRST to WP_LAST do utils.writeInt(st, Word(FReloading[i]));
-  // Íàëè÷èå ðþêçàêà
+  // Наличие рюкзака
   utils.writeBool(st, (R_ITEM_BACKPACK in FRulez));
-  // Íàëè÷èå êðàñíîãî êëþ÷à
+  // Наличие красного ключа
   utils.writeBool(st, (R_KEY_RED in FRulez));
-  // Íàëè÷èå çåëåíîãî êëþ÷à
+  // Наличие зеленого ключа
   utils.writeBool(st, (R_KEY_GREEN in FRulez));
-  // Íàëè÷èå ñèíåãî êëþ÷à
+  // Наличие синего ключа
   utils.writeBool(st, (R_KEY_BLUE in FRulez));
-  // Íàëè÷èå áåðñåðêà
+  // Наличие берсерка
   utils.writeBool(st, (R_BERSERK in FRulez));
-  // Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ
+  // Время действия специальных предметов
   for i := MR_SUIT to MR_MAX do utils.writeInt(st, LongWord(FMegaRulez[i]));
-  // Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà
+  // Время до повторного респауна, смены оружия, исользования, захвата флага
   for i := T_RESPAWN to T_FLAGCAP do utils.writeInt(st, LongWord(FTime[i]));
-  // Íàçâàíèå ìîäåëè
+  // Название модели
   utils.writeStr(st, FModel.Name);
-  // Öâåò ìîäåëè
+  // Цвет модели
   utils.writeInt(st, Byte(FColor.R));
   utils.writeInt(st, Byte(FColor.G));
   utils.writeInt(st, Byte(FColor.B));
@@ -6213,96 +5474,96 @@ var
 begin
   assert(st <> nil);
 
-  // Ñèãíàòóðà èãðîêà
+  // Сигнатура игрока
   if not utils.checkSign(st, 'PLYR') then raise XStreamError.Create('invalid player signature');
   if (utils.readByte(st) <> PLR_SAVE_VERSION) then raise XStreamError.Create('invalid player version');
-  // Áîò èëè ÷åëîâåê:
+  // Бот или человек:
   FIamBot := utils.readBool(st);
-  // UID èãðîêà
+  // UID игрока
   FUID := utils.readWord(st);
-  // Èìÿ èãðîêà
+  // Имя игрока
   str := utils.readStr(st);
   if (self <> gPlayer1) and (self <> gPlayer2) then FName := str;
-  // Êîìàíäà
+  // Команда
   FTeam := utils.readByte(st);
-  // Æèâ ëè
+  // Жив ли
   FAlive := utils.readBool(st);
-  // Èçðàñõîäîâàë ëè âñå æèçíè
+  // Израсходовал ли все жизни
   FNoRespawn := utils.readBool(st);
-  // Íàïðàâëåíèå
+  // Направление
   b := utils.readByte(st);
   if b = 1 then FDirection := TDirection.D_LEFT else FDirection := TDirection.D_RIGHT; // b = 2
-  // Çäîðîâüå
+  // Здоровье
   FHealth := utils.readLongInt(st);
-  // Êîýôôèöèåíò èíâàëèäíîñòè
+  // Коэффициент инвалидности
   FHandicap := utils.readLongInt(st);
-  // Æèçíè
+  // Жизни
   FLives := utils.readByte(st);
-  // Áðîíÿ
+  // Броня
   FArmor := utils.readLongInt(st);
-  // Çàïàñ âîçäóõà
+  // Запас воздуха
   FAir := utils.readLongInt(st);
-  // Çàïàñ ãîðþ÷åãî
+  // Запас горючего
   FJetFuel := utils.readLongInt(st);
-  // Áîëü
+  // Боль
   FPain := utils.readLongInt(st);
-  // Óáèë
+  // Убил
   FKills := utils.readLongInt(st);
-  // Óáèë ìîíñòðîâ
+  // Убил монстров
   FMonsterKills := utils.readLongInt(st);
-  // Ôðàãîâ
+  // Фрагов
   FFrags := utils.readLongInt(st);
-  // Ôðàãîâ ïîäðÿä
+  // Фрагов подряд
   FFragCombo := utils.readByte(st);
-  // Âðåìÿ ïîñëåäíåãî ôðàãà
+  // Время последнего фрага
   FLastFrag := utils.readLongWord(st);
-  // Ñìåðòåé
+  // Смертей
   FDeath := utils.readLongInt(st);
-  // Êàêîé ôëàã íåñåò
+  // Какой флаг несет
   FFlag := utils.readByte(st);
-  // Íàøåë ñåêðåòîâ
+  // Нашел секретов
   FSecrets := utils.readLongInt(st);
-  // Òåêóùåå îðóæèå
+  // Текущее оружие
   FCurrWeap := utils.readByte(st);
-  // Æåëàåìîå îðóæèå
+  // Желаемое оружие
   FNextWeap := utils.readWord(st);
-  // ...è ïàóçà
+  // ...и пауза
   FNextWeapDelay := utils.readByte(st);
-  // Âðåìÿ çàðÿäêè BFG
+  // Время зарядки BFG
   FBFGFireCounter := utils.readSmallInt(st);
-  // Áóôåð óðîíà
+  // Буфер урона
   FDamageBuffer := utils.readLongInt(st);
-  // Ïîñëåäíèé óäàðèâøèé
+  // Последний ударивший
   FLastSpawnerUID := utils.readWord(st);
-  // Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà
+  // Тип последнего полученного урона
   FLastHit := utils.readByte(st);
-  // Îáúåêò èãðîêà
+  // Объект игрока
   Obj_LoadState(@FObj, st);
-  // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ
+  // Текущее количество патронов
   for i := A_BULLETS to A_HIGH do FAmmo[i] := utils.readWord(st);
-  // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ
+  // Максимальное количество патронов
   for i := A_BULLETS to A_HIGH do FMaxAmmo[i] := utils.readWord(st);
-  // Íàëè÷èå îðóæèÿ
+  // Наличие оружия
   for i := WP_FIRST to WP_LAST do FWeapon[i] := utils.readBool(st);
-  // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ
+  // Время перезарядки оружия
   for i := WP_FIRST to WP_LAST do FReloading[i] := utils.readWord(st);
-  // Íàëè÷èå ðþêçàêà
+  // Наличие рюкзака
   if utils.readBool(st) then Include(FRulez, R_ITEM_BACKPACK);
-  // Íàëè÷èå êðàñíîãî êëþ÷à
+  // Наличие красного ключа
   if utils.readBool(st) then Include(FRulez, R_KEY_RED);
-  // Íàëè÷èå çåëåíîãî êëþ÷à
+  // Наличие зеленого ключа
   if utils.readBool(st) then Include(FRulez, R_KEY_GREEN);
-  // Íàëè÷èå ñèíåãî êëþ÷à
+  // Наличие синего ключа
   if utils.readBool(st) then Include(FRulez, R_KEY_BLUE);
-  // Íàëè÷èå áåðñåðêà
+  // Наличие берсерка
   if utils.readBool(st) then Include(FRulez, R_BERSERK);
-  // Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ
+  // Время действия специальных предметов
   for i := MR_SUIT to MR_MAX do FMegaRulez[i] := utils.readLongWord(st);
-  // Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà
+  // Время до повторного респауна, смены оружия, исользования, захвата флага
   for i := T_RESPAWN to T_FLAGCAP do FTime[i] := utils.readLongWord(st);
-  // Íàçâàíèå ìîäåëè
+  // Название модели
   str := utils.readStr(st);
-  // Öâåò ìîäåëè
+  // Цвет модели
   FColor.R := utils.readByte(st);
   FColor.G := utils.readByte(st);
   FColor.B := utils.readByte(st);
@@ -6316,7 +5577,7 @@ begin
     str := gPlayer2Settings.Model;
     FColor := gPlayer2Settings.Color;
   end;
-  // Îáíîâëÿåì ìîäåëü èãðîêà
+  // Обновляем модель игрока
   SetModel(str);
   if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
     FModel.Color := TEAMCOLOR[FTeam]
@@ -6643,12 +5904,12 @@ begin
         g_Player_CreateGibs(FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2),
                             FObj.Y+FObj.Rect.Y+(FObj.Rect.Height div 2),
                             FModelName, FColor);
-        // Çâóê ìÿñà îò òðóïà:
+        // Звук мяса от трупа:
         pm := g_PlayerModel_Get(FModelName);
         pm.PlaySound(MODELSOUND_DIE, 5, FObj.X, FObj.Y);
         pm.Free;
 
-        // Çëîâåùèé ñìåõ:
+        // Зловещий смех:
         if (gBodyKillEvent <> -1)
         and gDelayedEvents[gBodyKillEvent].Pending then
           gDelayedEvents[gBodyKillEvent].Pending := False;
@@ -6667,28 +5928,6 @@ begin
     end;
 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(fX, fY, TMirrorType.None);
-
-  if FAnimationMask <> nil then
-  begin
-    e_Colors := FColor;
-    FAnimationMask.Draw(fX, fY, TMirrorType.None);
-    e_Colors.R := 255;
-    e_Colors.G := 255;
-    e_Colors.B := 255;
-  end;
-end;
-
 procedure TCorpse.Update();
 var
   st: Word;
@@ -6706,7 +5945,7 @@ begin
     Exit;
   end;
 
-// Ñîïðîòèâëåíèå âîçäóõà äëÿ òðóïà:
+// Сопротивление воздуха для трупа:
   FObj.Vel.X := z_dec(FObj.Vel.X, 1);
 
   st := g_Obj_Move(@FObj, True, True, True);
@@ -6731,29 +5970,29 @@ var
 begin
   assert(st <> nil);
 
-  // Ñèãíàòóðà òðóïà
+  // Сигнатура трупа
   utils.writeSign(st, 'CORP');
   utils.writeInt(st, Byte(0));
-  // Ñîñòîÿíèå
+  // Состояние
   utils.writeInt(st, Byte(FState));
-  // Íàêîïëåííûé óðîí
+  // Накопленный урон
   utils.writeInt(st, Byte(FDamage));
-  // Öâåò
+  // Цвет
   utils.writeInt(st, Byte(FColor.R));
   utils.writeInt(st, Byte(FColor.G));
   utils.writeInt(st, Byte(FColor.B));
-  // Îáúåêò òðóïà
+  // Объект трупа
   Obj_SaveState(st, @FObj);
   utils.writeInt(st, Word(FPlayerUID));
-  // Åñòü ëè àíèìàöèÿ
+  // Есть ли анимация
   anim := (FAnimation <> nil);
   utils.writeBool(st, anim);
-  // Åñëè åñòü - ñîõðàíÿåì
+  // Если есть - сохраняем
   if anim then FAnimation.SaveState(st);
-  // Åñòü ëè ìàñêà àíèìàöèè
+  // Есть ли маска анимации
   anim := (FAnimationMask <> nil);
   utils.writeBool(st, anim);
-  // Åñëè åñòü - ñîõðàíÿåì
+  // Если есть - сохраняем
   if anim then FAnimationMask.SaveState(st);
 end;
 
@@ -6764,31 +6003,31 @@ var
 begin
   assert(st <> nil);
 
-  // Ñèãíàòóðà òðóïà
+  // Сигнатура трупа
   if not utils.checkSign(st, 'CORP') then raise XStreamError.Create('invalid corpse signature');
   if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid corpse version');
-  // Ñîñòîÿíèå
+  // Состояние
   FState := utils.readByte(st);
-  // Íàêîïëåííûé óðîí
+  // Накопленный урон
   FDamage := utils.readByte(st);
-  // Öâåò
+  // Цвет
   FColor.R := utils.readByte(st);
   FColor.G := utils.readByte(st);
   FColor.B := utils.readByte(st);
-  // Îáúåêò òðóïà
+  // Объект трупа
   Obj_LoadState(@FObj, st);
   FPlayerUID := utils.readWord(st);
-  // Åñòü ëè àíèìàöèÿ
+  // Есть ли анимация
   anim := utils.readBool(st);
-  // Åñëè åñòü - çàãðóæàåì
+  // Если есть - загружаем
   if anim then
   begin
     Assert(FAnimation <> nil, 'TCorpse.LoadState: no FAnimation');
     FAnimation.LoadState(st);
   end;
-  // Åñòü ëè ìàñêà àíèìàöèè
+  // Есть ли маска анимации
   anim := utils.readBool(st);
-  // Åñëè åñòü - çàãðóæàåì
+  // Если есть - загружаем
   if anim then
   begin
     Assert(FAnimationMask <> nil, 'TCorpse.LoadState: no FAnimationMask');
@@ -6826,14 +6065,6 @@ begin
   inherited Destroy();
 end;
 
-procedure TBot.Draw();
-begin
-  inherited Draw();
-
-  //if FTargetUID <> 0 then e_DrawLine(1, FObj.X, FObj.Y, g_Player_Get(FTargetUID).FObj.X,
-  //                                   g_Player_Get(FTargetUID).FObj.Y, 255, 0, 0);
-end;
-
 procedure TBot.Respawn(Silent: Boolean; Force: Boolean = False);
 begin
   inherited Respawn(Silent, Force);
@@ -6861,20 +6092,20 @@ type
 
   function Compare(a, b: TTarget): Integer;
   begin
-    if a.Line and not b.Line then // A íà ëèíèè îãíÿ
+    if a.Line and not b.Line then // A на линии огня
       Result := -1
    else
-     if not a.Line and b.Line then // B íà ëèíèè îãíÿ
+     if not a.Line and b.Line then // B на линии огня
        Result := 1
-     else // È A, è B íà ëèíèè èëè íå íà ëèíèè îãíÿ
+     else // И A, и B на линии или не на линии огня
        if (a.Line and b.Line) or ((not a.Line) and (not b.Line)) then
          begin
-           if a.Dist > b.Dist then // B áëèæå
+           if a.Dist > b.Dist then // B ближе
              Result := 1
-           else // A áëèæå èëè ðàâíîóäàëåííî ñ B
+           else // A ближе или равноудаленно с B
              Result := -1;
          end
-       else // Ñòðàííî -> A
+       else // Странно -> A
          Result := -1;
   end;
 
@@ -6900,10 +6131,10 @@ var
       x2 := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2);
       y2 := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2);
 
-      // Åñëè ìîíñòð íà ýêðàíå è íå ïðèêðûò ñòåíîé
+      // Если монстр на экране и не прикрыт стеной
       if g_TraceVector(x1, y1, x2, y2) then
       begin
-        // Äîáàâëÿåì ê ñïèñêó âîçìîæíûõ öåëåé
+        // Добавляем к списку возможных целей
         SetLength(targets, Length(targets)+1);
         with targets[High(targets)] do
         begin
@@ -6927,11 +6158,11 @@ begin
   vsPlayer := LongBool(gGameSettings.Options and GAME_OPTION_BOTVSPLAYER);
   vsMonster := LongBool(gGameSettings.Options and GAME_OPTION_BOTVSMONSTER);
 
-// Åñëè òåêóùåå îðóæèå íå òî, ÷òî íóæíî, òî ìåíÿåì:
+// Если текущее оружие не то, что нужно, то меняем:
   if FCurrWeap <> FSelectedWeapon then
     NextWeapon();
 
-// Åñëè íóæíî ñòðåëÿòü è íóæíîå îðóæèå, òî íàæàòü "Ñòðåëÿòü":
+// Если нужно стрелять и нужное оружие, то нажать "Стрелять":
   if (GetAIFlag('NEEDFIRE') <> '') and (FCurrWeap = FSelectedWeapon) then
     begin
       RemoveAIFlag('NEEDFIRE');
@@ -6943,7 +6174,7 @@ begin
       end;
     end;
 
-// Êîîðäèíàòû ñòâîëà:
+// Координаты ствола:
   x1 := FObj.X + WEAPONPOINT[FDirection].X;
   y1 := FObj.Y + WEAPONPOINT[FDirection].Y;
 
@@ -6951,10 +6182,10 @@ begin
 
   ok := False;
   if Target.UID <> 0 then
-    begin // Öåëü åñòü - íàñòðàèâàåì
+    begin // Цель есть - настраиваем
       if (g_GetUIDType(Target.UID) = UID_PLAYER) and
           vsPlayer then
-        begin // Èãðîê
+        begin // Игрок
           tpla := g_Player_Get(Target.UID);
           if tpla <> nil then
             with tpla do
@@ -6978,7 +6209,7 @@ begin
       else
         if (g_GetUIDType(Target.UID) = UID_MONSTER) and
             vsMonster then
-          begin // Ìîíñòð
+          begin // Монстр
             mon := g_Monsters_ByUID(Target.UID);
             if mon <> nil then
               begin
@@ -6998,7 +6229,7 @@ begin
     end;
 
   if not ok then
-    begin // Öåëè íåò - îáíóëÿåì
+    begin // Цели нет - обнуляем
       Target.X := 0;
       Target.Y := 0;
       Target.cX := 0;
@@ -7010,10 +6241,10 @@ begin
 
   targets := nil;
 
-// Åñëè öåëü íå âèäèìà èëè íå íà ëèíèè îãíÿ, òî èùåì âñå âîçìîæíûå öåëè:
+// Если цель не видима или не на линии огня, то ищем все возможные цели:
   if (not Target.Line) or (not Target.Visible) then
   begin
-  // Èãðîêè:
+  // Игроки:
     if vsPlayer then
       for a := 0 to High(gPlayers) do
         if (gPlayers[a] <> nil) and (gPlayers[a].alive) and
@@ -7029,10 +6260,10 @@ begin
             x2 := gPlayers[a].FObj.X + PLAYER_RECT_CX;
             y2 := gPlayers[a].FObj.Y + PLAYER_RECT_CY;
 
-          // Åñëè èãðîê íà ýêðàíå è íå ïðèêðûò ñòåíîé:
+          // Если игрок на экране и не прикрыт стеной:
             if g_TraceVector(x1, y1, x2, y2) then
               begin
-              // Äîáàâëÿåì ê ñïèñêó âîçìîæíûõ öåëåé:
+              // Добавляем к списку возможных целей:
                 SetLength(targets, Length(targets)+1);
                 with targets[High(targets)] do
                   begin
@@ -7051,64 +6282,64 @@ begin
               end;
           end;
 
-  // Ìîíñòðû:
+  // Монстры:
     if vsMonster then g_Mons_ForEach(monsUpdate);
   end;
 
-// Åñëè åñòü âîçìîæíûå öåëè:
-// (Âûáèðàåì ëó÷øóþ, ìåíÿåì îðóæèå è áåæèì ê íåé/îò íåå)
+// Если есть возможные цели:
+// (Выбираем лучшую, меняем оружие и бежим к ней/от нее)
   if targets <> nil then
   begin
-  // Âûáèðàåì íàèëó÷øóþ öåëü:
+  // Выбираем наилучшую цель:
     BestTarget := targets[0];
     if Length(targets) > 1 then
       for a := 1 to High(targets) do
         if Compare(BestTarget, targets[a]) = 1 then
           BestTarget := targets[a];
 
-  // Åñëè ëó÷øàÿ öåëü "âèäíåå" òåêóùåé, òî òåêóùàÿ := ëó÷øàÿ:
+  // Если лучшая цель "виднее" текущей, то текущая := лучшая:
     if ((not Target.Visible) and BestTarget.Visible and (Target.UID <> BestTarget.UID)) or
         ((not Target.Line) and BestTarget.Line and BestTarget.Visible) then
       begin
         Target := BestTarget;
 
         if (Healthy() = 3) or ((Healthy() = 2)) then
-          begin // Åñëè çäîðîâû - äîãîíÿåì
+          begin // Если здоровы - догоняем
             if ((RunDirection() = TDirection.D_LEFT) and (Target.X > FObj.X)) then
               SetAIFlag('GORIGHT', '1');
             if ((RunDirection() = TDirection.D_RIGHT) and (Target.X < FObj.X)) then
               SetAIFlag('GOLEFT', '1');
           end
         else
-          begin // Åñëè ïîáèòû - óáåãàåì
+          begin // Если побиты - убегаем
             if ((RunDirection() = TDirection.D_LEFT) and (Target.X < FObj.X)) then
               SetAIFlag('GORIGHT', '1');
             if ((RunDirection() = TDirection.D_RIGHT) and (Target.X > FObj.X)) then
               SetAIFlag('GOLEFT', '1');
           end;
 
-      // Âûáèðàåì îðóæèå íà îñíîâå ðàññòîÿíèÿ è ïðèîðèòåòîâ:
+      // Выбираем оружие на основе расстояния и приоритетов:
         SelectWeapon(Abs(x1-Target.cX));
       end;
   end;
 
-// Åñëè åñòü öåëü:
-// (Äîãîíÿåì/óáåãàåì, ñòðåëÿåì ïî íàïðàâëåíèþ ê öåëè)
-// (Åñëè öåëü äàëåêî, òî õâàòèò ñëåäèòü çà íåé)
+// Если есть цель:
+// (Догоняем/убегаем, стреляем по направлению к цели)
+// (Если цель далеко, то хватит следить за ней)
   if Target.UID <> 0 then
   begin
     if not TargetOnScreen(Target.X + Target.Rect.X,
                           Target.Y + Target.Rect.Y) then
-      begin // Öåëü ñáåæàëà ñ "ýêðàíà"
+      begin // Цель сбежала с "экрана"
         if (Healthy() = 3) or ((Healthy() = 2)) then
-          begin // Åñëè çäîðîâû - äîãîíÿåì
+          begin // Если здоровы - догоняем
             if ((RunDirection() = TDirection.D_LEFT) and (Target.X > FObj.X)) then
               SetAIFlag('GORIGHT', '1');
             if ((RunDirection() = TDirection.D_RIGHT) and (Target.X < FObj.X)) then
               SetAIFlag('GOLEFT', '1');
           end
         else
-          begin // Åñëè ïîáèòû - çàáûâàåì î öåëè è óáåãàåì
+          begin // Если побиты - забываем о цели и убегаем
             Target.UID := 0;
             if ((RunDirection() = TDirection.D_LEFT) and (Target.X < FObj.X)) then
               SetAIFlag('GORIGHT', '1');
@@ -7117,11 +6348,11 @@ begin
           end;
       end
     else
-      begin // Öåëü ïîêà íà "ýêðàíå"
-      // Åñëè öåëü íå çàãîðîæåíà ñòåíîé, òî îòìå÷àåì, êîãäà åå âèäåëè:
+      begin // Цель пока на "экране"
+      // Если цель не загорожена стеной, то отмечаем, когда ее видели:
         if g_TraceVector(x1, y1, Target.cX, Target.cY) then
           FLastVisible := gTime;
-      // Åñëè ðàçíèöà âûñîò íå âåëèêà, òî äîãîíÿåì:
+      // Если разница высот не велика, то догоняем:
         if (Abs(FObj.Y-Target.Y) <= 128) then
           begin
             if ((RunDirection() = TDirection.D_LEFT) and (Target.X > FObj.X)) then
@@ -7131,7 +6362,7 @@ begin
           end;
       end;
 
-  // Âûáèðàåì óãîë ââåðõ:
+  // Выбираем угол вверх:
     if FDirection = TDirection.D_LEFT then
       angle := ANGLE_LEFTUP
     else
@@ -7140,18 +6371,18 @@ begin
     firew := Trunc(Cos(DegToRad(-angle))*gPlayerScreenSize.X*0.6);
     fireh := Trunc(Sin(DegToRad(-angle))*gPlayerScreenSize.X*0.6);
 
-  // Åñëè ïðè óãëå ââåðõ ìîæíî ïîïàñòü â ïðèáëèçèòåëüíîå ïîëîæåíèå öåëè:
+  // Если при угле вверх можно попасть в приблизительное положение цели:
     if g_CollideLine(x1, y1, x1+firew, y1+fireh,
           Target.X+Target.Rect.X+GetInterval(FDifficult.DiagPrecision, 128), //96
           Target.Y+Target.Rect.Y+GetInterval(FDifficult.DiagPrecision, 128),
           Target.Rect.Width, Target.Rect.Height) and
         g_TraceVector(x1, y1, Target.cX, Target.cY) then
-      begin // òî íóæíî ñòðåëÿòü ââåðõ
+      begin // то нужно стрелять вверх
         SetAIFlag('NEEDFIRE', '1');
         SetAIFlag('NEEDSEEUP', '1');
       end;
 
-  // Âûáèðàåì óãîë âíèç:
+  // Выбираем угол вниз:
     if FDirection = TDirection.D_LEFT then
       angle := ANGLE_LEFTDOWN
     else
@@ -7160,34 +6391,34 @@ begin
     firew := Trunc(Cos(DegToRad(-angle))*gPlayerScreenSize.X*0.6);
     fireh := Trunc(Sin(DegToRad(-angle))*gPlayerScreenSize.X*0.6);
 
-  // Åñëè ïðè óãëå âíèç ìîæíî ïîïàñòü â ïðèáëèçèòåëüíîå ïîëîæåíèå öåëè:
+  // Если при угле вниз можно попасть в приблизительное положение цели:
     if g_CollideLine(x1, y1, x1+firew, y1+fireh,
           Target.X+Target.Rect.X+GetInterval(FDifficult.DiagPrecision, 128),
           Target.Y+Target.Rect.Y+GetInterval(FDifficult.DiagPrecision, 128),
           Target.Rect.Width, Target.Rect.Height) and
         g_TraceVector(x1, y1, Target.cX, Target.cY) then
-      begin // òî íóæíî ñòðåëÿòü âíèç
+      begin // то нужно стрелять вниз
         SetAIFlag('NEEDFIRE', '1');
         SetAIFlag('NEEDSEEDOWN', '1');
       end;
 
-  // Åñëè öåëü âèäíî è îíà íà òàêîé æå âûñîòå:
+  // Если цель видно и она на такой же высоте:
     if Target.Visible and
         (y1+4 < Target.Y+Target.Rect.Y+Target.Rect.Height) and
         (y1-4 > Target.Y+Target.Rect.Y) then
       begin
-      // Åñëè èäåì â ñòîðîíó öåëè, òî íàäî ñòðåëÿòü:
+      // Если идем в сторону цели, то надо стрелять:
         if ((FDirection = TDirection.D_LEFT) and (Target.X < FObj.X)) or
             ((FDirection = TDirection.D_RIGHT) and (Target.X > FObj.X)) then
-        begin // òî íóæíî ñòðåëÿòü âïåðåä
+        begin // то нужно стрелять вперед
           SetAIFlag('NEEDFIRE', '1');
           SetAIFlag('NEEDSEEDOWN', '');
           SetAIFlag('NEEDSEEUP', '');
         end;
-      // Åñëè öåëü â ïðåäåëàõ "ýêðàíà" è ñëîæíîñòü ïîçâîëÿåò ïðûæêè ñáëèæåíèÿ:
+      // Если цель в пределах "экрана" и сложность позволяет прыжки сближения:
         if Abs(FObj.X-Target.X) < Trunc(gPlayerScreenSize.X*0.75) then
           if GetRnd(FDifficult.CloseJump) then
-            begin // òî åñëè ïîâåçåò - ïðûãàåì (îñîáåííî, åñëè áëèçêî)
+            begin // то если повезет - прыгаем (особенно, если близко)
               if Abs(FObj.X-Target.X) < 128 then
                 a := 4
               else
@@ -7197,75 +6428,75 @@ begin
             end;
       end;
 
-  // Åñëè öåëü âñå åùå åñòü:
+  // Если цель все еще есть:
     if Target.UID <> 0 then
-      if gTime-FLastVisible > 2000 then // Åñëè âèäåëè äàâíî
-        Target.UID := 0 // òî çàáûòü öåëü
-      else // Åñëè âèäåëè íåäàâíî
-        begin // íî öåëü óáèëè
+      if gTime-FLastVisible > 2000 then // Если видели давно
+        Target.UID := 0 // то забыть цель
+      else // Если видели недавно
+        begin // но цель убили
           if Target.IsPlayer then
-            begin // Öåëü - èãðîê
+            begin // Цель - игрок
               pla := g_Player_Get(Target.UID);
               if (pla = nil) or (not pla.alive) or pla.NoTarget or
                  (pla.FMegaRulez[MR_INVIS] >= gTime) then
-                Target.UID := 0; // òî çàáûòü öåëü
+                Target.UID := 0; // то забыть цель
             end
           else
-            begin // Öåëü - ìîíñòð
+            begin // Цель - монстр
               mon := g_Monsters_ByUID(Target.UID);
               if (mon = nil) or (not mon.alive) then
-                Target.UID := 0; // òî çàáûòü öåëü
+                Target.UID := 0; // то забыть цель
             end;
         end;
   end; // if Target.UID <> 0
 
   FTargetUID := Target.UID;
 
-// Åñëè âîçìîæíûõ öåëåé íåò:
-// (Àòàêà ÷åãî-íèáóäü ñëåâà èëè ñïðàâà)
+// Если возможных целей нет:
+// (Атака чего-нибудь слева или справа)
   if targets = nil then
     if GetAIFlag('ATTACKLEFT') <> '' then
-      begin // Åñëè íóæíî àòàêîâàòü íàëåâî
+      begin // Если нужно атаковать налево
         RemoveAIFlag('ATTACKLEFT');
 
         SetAIFlag('NEEDJUMP', '1');
 
         if RunDirection() = TDirection.D_RIGHT then
-          begin // Èäåì íå â òó ñòîðîíó
+          begin // Идем не в ту сторону
             if (Healthy() > 1) and GetRnd(FDifficult.InvisFire) then
-              begin // Åñëè çäîðîâû, òî, âîçìîæíî, ñòðåëÿåì áåæèì âëåâî è ñòðåëÿåì
+              begin // Если здоровы, то, возможно, стреляем бежим влево и стреляем
                 SetAIFlag('NEEDFIRE', '1');
                 SetAIFlag('GOLEFT', '1');
               end;
           end
         else
-          begin // Èäåì â íóæíóþ ñòîðîíó
-            if GetRnd(FDifficult.InvisFire) then // Âîçìîæíî, ñòðåëÿåì âñëåïóþ
+          begin // Идем в нужную сторону
+            if GetRnd(FDifficult.InvisFire) then // Возможно, стреляем вслепую
               SetAIFlag('NEEDFIRE', '1');
-            if Healthy() <= 1 then // Ïîáèòû - óáåãàåì
+            if Healthy() <= 1 then // Побиты - убегаем
               SetAIFlag('GORIGHT', '1');
           end;
       end
     else
       if GetAIFlag('ATTACKRIGHT') <> '' then
-        begin // Åñëè íóæíî àòàêîâàòü íàïðàâî
+        begin // Если нужно атаковать направо
           RemoveAIFlag('ATTACKRIGHT');
 
           SetAIFlag('NEEDJUMP', '1');
 
           if RunDirection() = TDirection.D_LEFT then
-            begin // Èäåì íå â òó ñòîðîíó
+            begin // Идем не в ту сторону
               if (Healthy() > 1) and GetRnd(FDifficult.InvisFire) then
-                begin // Åñëè çäîðîâû, òî, âîçìîæíî, áåæèì âïðàâî è ñòðåëÿåì
+                begin // Если здоровы, то, возможно, бежим вправо и стреляем
                   SetAIFlag('NEEDFIRE', '1');
                   SetAIFlag('GORIGHT', '1');
                 end;
             end
           else
             begin
-              if GetRnd(FDifficult.InvisFire) then // Âîçìîæíî, ñòðåëÿåì âñëåïóþ
+              if GetRnd(FDifficult.InvisFire) then // Возможно, стреляем вслепую
                 SetAIFlag('NEEDFIRE', '1');
-              if Healthy() <= 1 then // Ïîáèòû - óáåãàåì
+              if Healthy() <= 1 then // Побиты - убегаем
                 SetAIFlag('GOLEFT', '1');
             end;
         end;
@@ -7273,15 +6504,15 @@ begin
   //HACK! (does it belongs there?)
   RealizeCurrentWeapon();
 
-// Åñëè åñòü âîçìîæíûå öåëè:
-// (Ñòðåëÿåì ïî íàïðàâëåíèþ ê öåëÿì)
+// Если есть возможные цели:
+// (Стреляем по направлению к целям)
   if (targets <> nil) and (GetAIFlag('NEEDFIRE') <> '') then
     for a := 0 to High(targets) do
       begin
-      // Åñëè ìîæåì ñòðåëÿòü ïî äèàãîíàëè:
+      // Если можем стрелять по диагонали:
         if GetRnd(FDifficult.DiagFire) then
           begin
-          // Èùåì öåëü ñâåðõó è ñòðåëÿåì, åñëè åñòü:
+          // Ищем цель сверху и стреляем, если есть:
             if FDirection = TDirection.D_LEFT then
               angle := ANGLE_LEFTUP
             else
@@ -7300,7 +6531,7 @@ begin
                 SetAIFlag('NEEDSEEUP', '1');
               end;
 
-          // Èùåì öåëü ñíèçó è ñòðåëÿåì, åñëè åñòü:
+          // Ищем цель снизу и стреляем, если есть:
             if FDirection = TDirection.D_LEFT then
               angle := ANGLE_LEFTDOWN
             else
@@ -7320,7 +6551,7 @@ begin
               end;
           end;
 
-      // Åñëè öåëü "ïåðåä íîñîì", òî ñòðåëÿåì:
+      // Если цель "перед носом", то стреляем:
         if targets[a].Line and targets[a].Visible and
             (((FDirection = TDirection.D_LEFT) and (targets[a].X < FObj.X)) or
             ((FDirection = TDirection.D_RIGHT) and (targets[a].X > FObj.X))) then
@@ -7330,20 +6561,20 @@ begin
         end;
       end;
 
-// Åñëè ëåòèò ïóëÿ, òî, âîçìîæíî, ïîäïðûãèâàåì:
+// Если летит пуля, то, возможно, подпрыгиваем:
   if g_Weapon_Danger(FUID, FObj.X+PLAYER_RECT.X, FObj.Y+PLAYER_RECT.Y,
                     PLAYER_RECT.Width, PLAYER_RECT.Height,
                     40+GetInterval(FDifficult.Cover, 40)) then
     SetAIFlag('NEEDJUMP', '1');
 
-// Åñëè êîí÷èëèñü ïàòîðíû, òî íóæíî ñìåíèòü îðóæèå:
+// Если кончились паторны, то нужно сменить оружие:
   ammo := GetAmmoByWeapon(FCurrWeap);
   if ((FCurrWeap = WEAPON_SHOTGUN2) and (ammo < 2)) or
       ((FCurrWeap = WEAPON_BFG) and (ammo < 40)) or
       (ammo = 0) then
     SetAIFlag('SELECTWEAPON', '1');
 
-// Åñëè íóæíî ñìåíèòü îðóæèå, òî âûáèðàåì íóæíîå:
+// Если нужно сменить оружие, то выбираем нужное:
   if GetAIFlag('SELECTWEAPON') = '1' then
   begin
     SelectWeapon(-1);
@@ -7364,7 +6595,7 @@ begin
   begin
     EnableAI := True;
 
-    // Ïðîâåðÿåì, îòêëþ÷¸í ëè AI áîòîâ
+    // Проверяем, отключён ли AI ботов
     if (g_debug_BotAIOff = 1) and (Team = TEAM_RED) then
       EnableAI := False;
     if (g_debug_BotAIOff = 2) and (Team = TEAM_BLUE) then
@@ -7525,7 +6756,7 @@ procedure TBot.UpdateMove;
   var
     x, sx: Integer;
   begin
-    { TODO 5 : Ëåñòíèöû }
+    { TODO 5 : Лестницы }
     sx := IfThen(RunDirection() = TDirection.D_LEFT, -1, 1);
     for x := 1 to PLAYER_RECT.Width do
       if (not StayOnStep(x*sx, 0)) and
@@ -7543,7 +6774,7 @@ procedure TBot.UpdateMove;
   var
     x, sx, xx: Integer;
   begin
-    { TODO 5 : Ëåñòíèöû }
+    { TODO 5 : Лестницы }
     sx := IfThen(RunDirection() = TDirection.D_LEFT, -1, 1);
     for x := 1 to PLAYER_RECT.Width do
       if (not StayOnStep(x*sx, 0)) and
@@ -7716,11 +6947,11 @@ procedure TBot.UpdateMove;
   end;
 
 begin
-// Âîçìîæíî, íàæèìàåì êíîïêó:
+// Возможно, нажимаем кнопку:
   if Rnd(16) and IsSafeTrigger() then
     PressKey(KEY_OPEN);
 
-// Åñëè ïîä ëèôòîì èëè ñòóïåíüêàìè, òî, âîçìîæíî, ïðûãàåì:
+// Если под лифтом или ступеньками, то, возможно, прыгаем:
   if OnLadder() or ((BelowLadder() or BelowLiftUp()) and Rnd(8)) then
     begin
       ReleaseKey(KEY_LEFT);
@@ -7728,7 +6959,7 @@ begin
       Jump();
     end;
 
-// Èäåì âëåâî, åñëè íàäî áûëî:
+// Идем влево, если надо было:
   if GetAIFlag('GOLEFT') <> '' then
     begin
       RemoveAIFlag('GOLEFT');
@@ -7736,7 +6967,7 @@ begin
         GoLeft(360);
     end;
 
-// Èäåì âïðàâî, åñëè íàäî áûëî:
+// Идем вправо, если надо было:
   if GetAIFlag('GORIGHT') <> '' then
     begin
       RemoveAIFlag('GORIGHT');
@@ -7744,21 +6975,21 @@ begin
         GoRight(360);
     end;
 
-// Åñëè âûëåòåëè çà êàðòó, òî ïðîáóåì âåðíóòüñÿ:
+// Если вылетели за карту, то пробуем вернуться:
   if FObj.X < -32 then
     GoRight(360)
   else
     if FObj.X+32 > gMapInfo.Width then
       GoLeft(360);
 
-// Ïðûãàåì, åñëè íàäî áûëî:
+// Прыгаем, если надо было:
   if GetAIFlag('NEEDJUMP') <> '' then
     begin
       Jump(0);
       RemoveAIFlag('NEEDJUMP');
     end;
 
-// Ñìîòðèì ââåðõ, åñëè íàäî áûëî:
+// Смотрим вверх, если надо было:
   if GetAIFlag('NEEDSEEUP') <> '' then
     begin
       ReleaseKey(KEY_UP);
@@ -7767,7 +6998,7 @@ begin
       RemoveAIFlag('NEEDSEEUP');
     end;
 
-// Ñìîòðèì âíèç, åñëè íàäî áûëî:
+// Смотрим вниз, если надо было:
   if GetAIFlag('NEEDSEEDOWN') <> '' then
     begin
       ReleaseKey(KEY_UP);
@@ -7776,7 +7007,7 @@ begin
       RemoveAIFlag('NEEDSEEDOWN');
     end;
 
-// Åñëè íóæíî áûëî â äûðó è ìû íå íà çåìëå, òî ïîêîðíî ëåòèì:
+// Если нужно было в дыру и мы не на земле, то покорно летим:
   if GetAIFlag('GOINHOLE') <> '' then
     if not OnGround() then
       begin
@@ -7786,12 +7017,12 @@ begin
         SetAIFlag('FALLINHOLE', '1');
       end;
 
-// Åñëè ïàäàëè è äîñòèãëè çåìëè, òî õâàòèò ïàäàòü:
+// Если падали и достигли земли, то хватит падать:
   if GetAIFlag('FALLINHOLE') <> '' then
     if OnGround() then
       RemoveAIFlag('FALLINHOLE');
 
-// Åñëè ëåòåëè ïðÿìî è ñåé÷àñ íå íà ëåñòíèöå èëè íà âåðøèíå ëèôòà, òî îòõîäèì â ñòîðîíó:
+// Если летели прямо и сейчас не на лестнице или на вершине лифта, то отходим в сторону:
   if not (KeyPressed(KEY_LEFT) or KeyPressed(KEY_RIGHT)) then
     if GetAIFlag('FALLINHOLE') = '' then
       if (not OnLadder()) or (FObj.Vel.Y >= 0) or (OnTopLift()) then
@@ -7800,40 +7031,40 @@ begin
         else
           GoRight(360);
 
-// Åñëè íà çåìëå è ìîæíî ïîäïðûãíóòü, òî, âîçìîæíî, ïðûãàåì:
+// Если на земле и можно подпрыгнуть, то, возможно, прыгаем:
   if OnGround() and
       CanJumpUp(IfThen(RunDirection() = TDirection.D_LEFT, -1, 1)*32) and
       Rnd(8) then
     Jump();
 
-// Åñëè íà çåìëå è âîçëå äûðû (ãëóáèíà > 2 ðîñòîâ èãðîêà):
+// Если на земле и возле дыры (глубина > 2 ростов игрока):
   if OnGround() and NearHole() then
-    if NearDeepHole() then // Åñëè ýòî áåçäíà
+    if NearDeepHole() then // Если это бездна
       case Random(6) of
-        0..3: Turn(); // Áåæèì îáðàòíî
-        4: Jump(); // Ïðûãàåì
-        5: begin // Ïðûãàåì îáðàòíî
+        0..3: Turn(); // Бежим обратно
+        4: Jump(); // Прыгаем
+        5: begin // Прыгаем обратно
              Turn();
              Jump();
            end;
       end
-    else // Ýòî íå áåçäíà è ìû åùå íå ëåòèì òóäà
+    else // Это не бездна и мы еще не летим туда
       if GetAIFlag('GOINHOLE') = '' then
         case Random(6) of
-          0: Turn(); // Íå íóæíî òóäà
-          1: Jump(); // Âäðóã ïîâåçåò - ïðûãàåì
-          else // Åñëè ÿìà ñ ãðàíèöåé, òî ïðè ñëó÷àå ìîæíî òóäà ïðûãíóòü
+          0: Turn(); // Не нужно туда
+          1: Jump(); // Вдруг повезет - прыгаем
+          else // Если яма с границей, то при случае можно туда прыгнуть
             if BorderHole() then
               SetAIFlag('GOINHOLE', '1');
    end;
 
-// Åñëè íà çåìëå, íî íåêóäà èäòè:
+// Если на земле, но некуда идти:
   if (not CanRun()) and OnGround() then
     begin
-    // Åñëè ìû íà ëåñòíèöå èëè ìîæíî ïåðåïðûãíóòü, òî ïðûãàåì:
+    // Если мы на лестнице или можно перепрыгнуть, то прыгаем:
       if CanJumpOver() or OnLadder() then
         Jump()
-      else // èíà÷å ïîïûòàåìñÿ â äðóãóþ ñòîðîíó
+      else // иначе попытаемся в другую сторону
         if Random(2) = 0 then
         begin
           if IsSafeTrigger() then
@@ -7842,11 +7073,11 @@ begin
           Turn();
     end;
 
-// Îñòàëîñü ìàëî âîçäóõà:
+// Осталось мало воздуха:
   if FAir < 36 * 2 then
     Jump(20);
 
-// Âûáèðàåìñÿ èç êèñëîòû, åñëè íåò êîñòþìà, îáîæãëèñü, èëè ìàëî çäîðîâüÿ:
+// Выбираемся из кислоты, если нет костюма, обожглись, или мало здоровья:
   if (FMegaRulez[MR_SUIT] < gTime) and ((FLastHit = HIT_ACID) or (Healthy() <= 1)) then
     if BodyInAcid(0, 0) then
       Jump();
@@ -7887,7 +7118,7 @@ begin
   if Dist = -1 then Dist := BOT_LONGDIST;
 
   if Dist > BOT_LONGDIST then
-  begin // Äàëüíèé áîé
+  begin // Дальний бой
     for a := 0 to 9 do
       if FWeapon[FDifficult.WeaponPrior[a]] and HaveAmmo(FDifficult.WeaponPrior[a]) then
       begin
@@ -7896,7 +7127,7 @@ begin
       end;
   end
   else //if Dist > BOT_UNSAFEDIST then
-  begin // Áëèæíèé áîé
+  begin // Ближний бой
     for a := 0 to 9 do
       if FWeapon[FDifficult.CloseWeaponPrior[a]] and HaveAmmo(FDifficult.CloseWeaponPrior[a]) then
       begin
@@ -7955,7 +7186,7 @@ begin
       ok := False;
       if (g_GetUIDType(FLastSpawnerUID) = UID_PLAYER) and
           LongBool(gGameSettings.Options and GAME_OPTION_BOTVSPLAYER) then
-        begin // Èãðîê
+        begin // Игрок
           pla := g_Player_Get(FLastSpawnerUID);
           ok := not TargetOnScreen(pla.FObj.X + PLAYER_RECT.X,
                                    pla.FObj.Y + PLAYER_RECT.Y);
@@ -7963,7 +7194,7 @@ begin
       else
         if (g_GetUIDType(FLastSpawnerUID) = UID_MONSTER) and
            LongBool(gGameSettings.Options and GAME_OPTION_BOTVSMONSTER) then
-        begin // Ìîíñòð
+        begin // Монстр
           mon := g_Monsters_ByUID(FLastSpawnerUID);
           ok := not TargetOnScreen(mon.Obj.X + mon.Obj.Rect.X,
                                    mon.Obj.Y + mon.Obj.Rect.Y);
@@ -8031,22 +7262,22 @@ var
 begin
   inherited SaveState(st);
   utils.writeSign(st, 'BOT0');
-  // Âûáðàííîå îðóæèå
+  // Выбранное оружие
   utils.writeInt(st, Byte(FSelectedWeapon));
-  // UID öåëè
+  // UID цели
   utils.writeInt(st, Word(FTargetUID));
-  // Âðåìÿ ïîòåðè öåëè
+  // Время потери цели
   utils.writeInt(st, LongWord(FLastVisible));
-  // Êîëè÷åñòâî ôëàãîâ ÈÈ
+  // Количество флагов ИИ
   dw := Length(FAIFlags);
   utils.writeInt(st, LongInt(dw));
-  // Ôëàãè ÈÈ
+  // Флаги ИИ
   for i := 0 to dw-1 do
   begin
     utils.writeStr(st, FAIFlags[i].Name, 20);
     utils.writeStr(st, FAIFlags[i].Value, 20);
   end;
-  // Íàñòðîéêè ñëîæíîñòè
+  // Настройки сложности
   FDifficult.save(st);
 end;
 
@@ -8058,23 +7289,23 @@ var
 begin
   inherited LoadState(st);
   if not utils.checkSign(st, 'BOT0') then raise XStreamError.Create('invalid bot signature');
-  // Âûáðàííîå îðóæèå
+  // Выбранное оружие
   FSelectedWeapon := utils.readByte(st);
-  // UID öåëè
+  // UID цели
   FTargetUID := utils.readWord(st);
-  // Âðåìÿ ïîòåðè öåëè
+  // Время потери цели
   FLastVisible := utils.readLongWord(st);
-  // Êîëè÷åñòâî ôëàãîâ ÈÈ
+  // Количество флагов ИИ
   dw := utils.readLongInt(st);
   if (dw < 0) or (dw > 16384) then raise XStreamError.Create('invalid number of bot AI flags');
   SetLength(FAIFlags, dw);
-  // Ôëàãè ÈÈ
+  // Флаги ИИ
   for i := 0 to dw-1 do
   begin
     FAIFlags[i].Name := utils.readStr(st, 20);
     FAIFlags[i].Value := utils.readStr(st, 20);
   end;
-  // Íàñòðîéêè ñëîæíîñòè
+  // Настройки сложности
   FDifficult.load(st);
 end;
 
index d752fdc5ec818c284faf23aa38a2bdd0ebe86ca2..caa60c44930d765bb8f94022c63df56564699f84 100644 (file)
@@ -32,7 +32,7 @@ implementation
     g_textures, e_input, e_sound,
     g_language, g_console, g_menu, g_triggers, g_player, g_options, g_monsters, g_map, g_panel, g_window,
     g_items, g_weapons, g_gfx, g_phys, g_net, g_gui, g_netmaster,
-    g_game, r_console, r_gfx, r_items, r_map, r_panel, r_monsters, r_weapons, r_netmaster
+    g_game, r_console, r_gfx, r_items, r_map, r_panel, r_monsters, r_weapons, r_netmaster, r_player
   ;
 
   var
@@ -1240,9 +1240,9 @@ begin
   drawPanelType('*step', PANEL_STEP, g_rlayer_step);
   drawOther('items', @r_Items_Draw);
   drawOther('weapons', @r_Weapon_Draw);
-  drawOther('shells', @g_Player_DrawShells);
-  drawOther('drawall', @g_Player_DrawAll);
-  drawOther('corpses', @g_Player_DrawCorpses);
+  drawOther('shells', @r_Player_DrawShells);
+  drawOther('drawall', @r_Player_DrawAll);
+  drawOther('corpses', @r_Player_DrawCorpses);
   drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
   drawOther('monsters', @r_Monsters_Draw);
   drawOther('itemdrop', @r_Items_DrawDrop);
@@ -1266,7 +1266,7 @@ begin
   if g_debug_HealthBar then
   begin
     r_Monsters_DrawHealth();
-    g_Player_DrawHealth();
+    r_Player_DrawHealth();
   end;
 
   if (profileFrameDraw <> nil) then profileFrameDraw.mainEnd(); // map rendering
@@ -1436,16 +1436,18 @@ begin
   if (gGameSettings.GameMode <> GM_SINGLE) and (gPlayerIndicator > 0) then
     case gPlayerIndicator of
       1:
-        p.DrawIndicator(_RGB(255, 255, 255));
+        r_Player_DrawIndicator(p, _RGB(255, 255, 255));
 
       2:
         for i := 0 to High(gPlayers) do
           if gPlayers[i] <> nil then
-            if gPlayers[i] = p then p.DrawIndicator(_RGB(255, 255, 255))
+            if gPlayers[i] = p then
+              r_Player_DrawIndicator(p, _RGB(255, 255, 255))
             else if (gPlayers[i].Team = p.Team) and (gPlayers[i].Team <> TEAM_NONE) then
               if gPlayerIndicatorStyle = 1 then
-                gPlayers[i].DrawIndicator(_RGB(192, 192, 192))
-              else gPlayers[i].DrawIndicator(gPlayers[i].GetColor);
+                r_Player_DrawIndicator(gPlayers[i], _RGB(192, 192, 192))
+              else
+                r_Player_DrawIndicator(gPlayers[i], gPlayers[i].GetColor);
     end;
 
   {
@@ -1468,13 +1470,13 @@ begin
 
   glPopMatrix();
 
-  p.DrawPain();
-  p.DrawPickup();
-  p.DrawRulez();
+  r_Player_DrawPain(p);
+  r_Player_DrawPickup(p);
+  r_Player_DrawRulez(p);
   if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
   if g_Debug_Player then
-    g_Player_DrawDebug(p);
-  p.DrawGUI();
+    r_Player_DrawDebug(p);
+  r_Player_DrawGUI(p);
 end;
 
 procedure drawProfilers ();
diff --git a/src/game/opengl/r_player.pas b/src/game/opengl/r_player.pas
new file mode 100644 (file)
index 0000000..c054d06
--- /dev/null
@@ -0,0 +1,806 @@
+(* Copyright (C)  Doom 2D: Forever Developers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License ONLY.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *)
+{$INCLUDE ../../shared/a_modes.inc}
+unit r_player;
+
+interface
+
+  uses g_player, e_graphics; // TPlayer, TRGB
+
+  procedure r_Player_DrawAll;
+  procedure r_Player_DrawDebug (p: TPlayer);
+  procedure r_Player_DrawHealth;
+
+  procedure r_Player_DrawCorpses;
+  procedure r_Player_DrawShells;
+
+  procedure r_Player_Draw (p: TPlayer);
+  procedure r_Player_DrawIndicator (p: TPlayer; Color: TRGB);
+  procedure r_Player_DrawBubble (p: TPlayer);
+  procedure r_Player_DrawAim (p: TPlayer);
+  procedure r_Player_DrawGUI (pl: TPlayer);
+  procedure r_Player_DrawRulez (p: TPlayer);
+  procedure r_Player_DrawPain (p: TPlayer);
+  procedure r_Player_DrawPickup (p: TPlayer);
+
+  procedure r_Player_DrawCorpse (p: TCorpse);
+
+implementation
+
+  uses
+    SysUtils, Classes, Math,
+    MAPDEF, utils,
+    g_basic, g_game, g_phys, g_map, g_textures, g_menu, g_language, g_weapons, g_items, g_net, g_options
+  ;
+
+  procedure r_Player_DrawAll;
+    var i: Integer;
+  begin
+    if gPlayers <> nil then
+      for i := 0 to High(gPlayers) do
+        if gPlayers[i] <> nil then
+          r_Player_Draw(gPlayers[i])
+  end;
+
+procedure r_Player_DrawDebug (p: TPlayer);
+var
+  fW, fH: Byte;
+begin
+  if p = nil then Exit;
+  if (@p.Obj) = nil then Exit;
+
+  e_TextureFontGetSize(gStdFont, fW, fH);
+
+  e_TextureFontPrint(0, 0     , 'Pos X: ' + IntToStr(p.Obj.X), gStdFont);
+  e_TextureFontPrint(0, fH    , 'Pos Y: ' + IntToStr(p.Obj.Y), gStdFont);
+  e_TextureFontPrint(0, fH * 2, 'Vel X: ' + IntToStr(p.Obj.Vel.X), gStdFont);
+  e_TextureFontPrint(0, fH * 3, 'Vel Y: ' + IntToStr(p.Obj.Vel.Y), gStdFont);
+  e_TextureFontPrint(0, fH * 4, 'Acc X: ' + IntToStr(p.Obj.Accel.X), gStdFont);
+  e_TextureFontPrint(0, fH * 5, 'Acc Y: ' + IntToStr(p.Obj.Accel.Y), gStdFont);
+  e_TextureFontPrint(0, fH * 6, 'Old X: ' + IntToStr(p.Obj.oldX), gStdFont);
+  e_TextureFontPrint(0, fH * 7, 'Old Y: ' + IntToStr(p.Obj.oldY), gStdFont);
+end;
+
+procedure r_Player_DrawHealth;
+var
+  i: Integer;
+  fW, fH: Byte;
+begin
+  if gPlayers = nil then Exit;
+  e_TextureFontGetSize(gStdFont, fW, fH);
+
+  for i := 0 to High(gPlayers) do
+    if gPlayers[i] <> nil then
+    begin
+      e_TextureFontPrint(gPlayers[i].Obj.X + gPlayers[i].Obj.Rect.X,
+      gPlayers[i].Obj.Y + gPlayers[i].Obj.Rect.Y + gPlayers[i].Obj.Rect.Height - fH * 2,
+      IntToStr(gPlayers[i].Health), gStdFont);
+      e_TextureFontPrint(gPlayers[i].Obj.X + gPlayers[i].Obj.Rect.X,
+      gPlayers[i].Obj.Y + gPlayers[i].Obj.Rect.Y + gPlayers[i].Obj.Rect.Height - fH,
+      IntToStr(gPlayers[i].Armor), gStdFont);
+    end;
+end;
+
+procedure r_Player_DrawCorpses;
+var
+  i, fX, fY: Integer;
+  a: TDFPoint;
+begin
+  if gGibs <> nil then
+    for i := 0 to High(gGibs) do
+      if gGibs[i].alive then
+        with gGibs[i] do
+        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, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None);
+
+          e_Colors := Color;
+          e_DrawAdv(MaskID, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None);
+          e_Colors.R := 255;
+          e_Colors.G := 255;
+          e_Colors.B := 255;
+        end;
+
+  if gCorpses <> nil then
+    for i := 0 to High(gCorpses) do
+      if gCorpses[i] <> nil then
+        r_Player_DrawCorpse(gCorpses[i])
+end;
+
+procedure r_Player_DrawShells;
+var
+  i, fX, fY: Integer;
+  a: TDFPoint;
+begin
+  if gShells <> nil then
+    for i := 0 to High(gShells) do
+      if gShells[i].alive then
+        with gShells[i] do
+        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, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None);
+        end;
+end;
+
+procedure r_Player_DrawIndicator (p: TPlayer; Color: TRGB);
+var
+  indX, indY, fX, fY, fSlope: Integer;
+  indW, indH: Word;
+  indA: Single;
+  a: TDFPoint;
+  nW, nH: Byte;
+  ID: DWORD;
+  c: TRGB;
+begin
+  if p.Alive then
+  begin
+    p.Obj.lerp(gLerpFactor, fX, fY);
+    fSlope := nlerp(p.SlopeOld, p.Obj.slopeUpLeft, gLerpFactor);
+
+    case gPlayerIndicatorStyle of
+      0:
+        begin
+          if g_Texture_Get('TEXTURE_PLAYER_INDICATOR', ID) then
+          begin
+            e_GetTextureSize(ID, @indW, @indH);
+            a.X := indW div 2;
+            a.Y := indH div 2;
+
+            if (p.Obj.X + p.Obj.Rect.X) < 0 then
+            begin
+              indA := 90;
+              indX := fX + p.Obj.Rect.X + p.Obj.Rect.Width;
+              indY := fY + p.Obj.Rect.Y + (p.Obj.Rect.Height - indW) div 2;
+            end
+
+            else if (p.Obj.X + p.Obj.Rect.X + p.Obj.Rect.Width) > Max(gMapInfo.Width, gPlayerScreenSize.X) then
+            begin
+              indA := 270;
+              indX := fX + p.Obj.Rect.X - indH;
+              indY := fY + p.Obj.Rect.Y + (p.Obj.Rect.Height - indW) div 2;
+            end
+
+            else if (p.Obj.Y - indH) < 0 then
+            begin
+              indA := 180;
+              indX := fX + p.Obj.Rect.X + (p.Obj.Rect.Width - indW) div 2;
+              indY := fY + p.Obj.Rect.Y + p.Obj.Rect.Height;
+            end
+
+            else
+            begin
+              indA := 0;
+              indX := fX + p.Obj.Rect.X + (p.Obj.Rect.Width - indW) div 2;
+              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);
+
+            c := e_Colors;
+            e_Colors := Color;
+            e_DrawAdv(ID, indX, indY, 0, True, False, indA, @a);
+            e_Colors := c;
+          end;
+        end;
+
+      1:
+        begin
+          e_TextureFontGetSize(gStdFont, nW, nH);
+          indX := fX + p.Obj.Rect.X + (p.Obj.Rect.Width - Length(p.Name) * nW) div 2;
+          indY := fY - nH + fSlope;
+          e_TextureFontPrintEx(indX, indY, p.Name, gStdFont, Color.R, Color.G, Color.B, 1.0, True);
+        end;
+    end;
+  end
+end;
+
+procedure r_Player_DrawBubble (p: TPlayer);
+var
+  bubX, bubY, fX, fY: Integer;
+  ID: LongWord;
+  Rb, Gb, Bb,
+  Rw, Gw, Bw: SmallInt;
+  Dot: Byte;
+  CObj: TObj;
+begin
+  CObj := p.getCameraObj();
+  CObj.lerp(gLerpFactor, fX, fY);
+  // NB: _F_Obj.Rect is used to keep the bubble higher; this is not a mistake
+  bubX := fX + p.Obj.Rect.X + IfThen(p.Direction = TDirection.D_LEFT, -4, 18);
+  bubY := fY + p.Obj.Rect.Y - 18;
+  Rb := 64;
+  Gb := 64;
+  Bb := 64;
+  Rw := 240;
+  Gw := 240;
+  Bw := 240;
+  case gChatBubble of
+    1: // simple textual non-bubble
+    begin
+      bubX := fX + p.Obj.Rect.X - 11;
+      bubY := fY + p.Obj.Rect.Y - 17;
+      e_TextureFontPrint(bubX, bubY, '[...]', gStdFont);
+      Exit;
+    end;
+    2: // advanced pixel-perfect bubble
+    begin
+      if p.Team = TEAM_RED then
+        Rb := 255
+      else
+        if p.Team = TEAM_BLUE then
+          Bb := 255;
+    end;
+    3: // colored bubble
+    begin
+      Rb := p.Model.Color.R;
+      Gb := p.Model.Color.G;
+      Bb := p.Model.Color.B;
+      Rw := Min(Rb * 2 + 64, 255);
+      Gw := Min(Gb * 2 + 64, 255);
+      Bw := Min(Bb * 2 + 64, 255);
+      if (Abs(Rw - Rb) < 32)
+      or (Abs(Gw - Gb) < 32)
+      or (Abs(Bw - Bb) < 32) then
+      begin
+        Rb := Max(Rw div 2 - 16, 0);
+        Gb := Max(Gw div 2 - 16, 0);
+        Bb := Max(Bw div 2 - 16, 0);
+      end;
+    end;
+    4: // custom textured bubble
+    begin
+      if g_Texture_Get('TEXTURE_PLAYER_TALKBUBBLE', ID) then
+        if p.Direction = TDirection.D_RIGHT then
+          e_Draw(ID, bubX - 6, bubY - 7, 0, True, False)
+        else
+          e_Draw(ID, bubX - 6, bubY - 7, 0, True, False, TMirrorType.Horizontal);
+      Exit;
+    end;
+  end;
+
+  // Outer borders
+  e_DrawQuad(bubX + 1, bubY    , bubX + 18, bubY + 13, Rb, Gb, Bb);
+  e_DrawQuad(bubX    , bubY + 1, bubX + 19, bubY + 12, Rb, Gb, Bb);
+  // Inner box
+  e_DrawFillQuad(bubX + 1, bubY + 1, bubX + 18, bubY + 12, Rw, Gw, Bw, 0);
+
+  // Tail
+  Dot := IfThen(p.Direction = TDirection.D_LEFT, 14, 5);
+  e_DrawLine(1, bubX + Dot, bubY + 14, bubX + Dot, bubY + 16, Rb, Gb, Bb);
+  e_DrawLine(1, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 1, Dot + 1), bubY + 13, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 1, Dot + 1), bubY + 15, Rw, Gw, Bw);
+  e_DrawLine(1, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 2, Dot + 2), bubY + 13, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 2, Dot + 2), bubY + 14, Rw, Gw, Bw);
+  e_DrawLine(1, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 3, Dot + 3), bubY + 13, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 3, Dot + 3), bubY + 13, Rw, Gw, Bw);
+  e_DrawLine(1, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 3, Dot + 3), bubY + 14, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 1, Dot + 1), bubY + 16, Rb, Gb, Bb);
+
+  // Dots
+  Dot := 6;
+  e_DrawFillQuad(bubX + Dot,     bubY + 8, bubX + Dot + 1, bubY + 9, Rb, Gb, Bb, 0);
+  e_DrawFillQuad(bubX + Dot + 3, bubY + 8, bubX + Dot + 4, bubY + 9, Rb, Gb, Bb, 0);
+  e_DrawFillQuad(bubX + Dot + 6, bubY + 8, bubX + Dot + 7, bubY + 9, Rb, Gb, Bb, 0);
+end;
+
+procedure r_Player_Draw (p: TPlayer);
+var
+  ID: DWORD;
+  w, h: Word;
+  dr: Boolean;
+  Mirror: TMirrorType;
+  fX, fY, fSlope: Integer;
+begin
+  p.Obj.lerp(gLerpFactor, fX, fY);
+  fSlope := nlerp(p.SlopeOld, p.Obj.slopeUpLeft, gLerpFactor);
+
+  if p.Alive then
+  begin
+    if p.Direction = TDirection.D_RIGHT then
+      Mirror := TMirrorType.None
+    else
+      Mirror := TMirrorType.Horizontal;
+
+    if p.PunchAnim <> nil then
+    begin
+      p.PunchAnim.Draw(fX + IfThen(p.Direction = TDirection.D_LEFT, 15 - p.Obj.Rect.X, p.Obj.Rect.X - 15), fY + fSlope + p.Obj.Rect.Y - 11, Mirror);
+      if p.PunchAnim.played then
+      begin
+        p.PunchAnim.Free;
+        p.PunchAnim := nil;
+      end;
+    end;
+
+    if (p.FMegaRulez[MR_INVUL] > gTime) and ((gPlayerDrawn <> p) or (p.SpawnInvul >= gTime)) then
+      if g_Texture_Get('TEXTURE_PLAYER_INVULPENTA', ID) then
+      begin
+        e_GetTextureSize(ID, @w, @h);
+        if p.Direction = TDirection.D_LEFT then
+          e_Draw(ID, fX + p.Obj.Rect.X + (p.Obj.Rect.Width div 2) - (w div 2) + 4,
+                     fY + p.Obj.Rect.Y + (p.Obj.Rect.Height div 2) - (h div 2) - 7 + fSlope, 0, True, False)
+        else
+          e_Draw(ID, fX + p.Obj.Rect.X + (p.Obj.Rect.Width div 2) - (w div 2) - 2,
+                     fY + p.Obj.Rect.Y + (p.Obj.Rect.Height div 2) - (h div 2) - 7 + fSlope, 0, True, False)
+      end;
+
+    if p.FMegaRulez[MR_INVIS] > gTime then
+    begin
+      if (gPlayerDrawn <> nil) and ((p = gPlayerDrawn) or
+         ((p.Team = gPlayerDrawn.Team) and (gGameSettings.GameMode <> GM_DM))) then
+      begin
+        if (p.FMegaRulez[MR_INVIS] - gTime) <= 2100 then
+          dr := not Odd((p.FMegaRulez[MR_INVIS] - gTime) div 300)
+        else
+          dr := True;
+        if dr then
+          p.Model.Draw(fX, fY + fSlope, 200)
+        else
+          p.Model.Draw(fX, fY + fSlope);
+      end
+      else
+        p.Model.Draw(fX, fY + fSlope, 254);
+    end
+    else
+      p.Model.Draw(fX, fY + fSlope);
+  end;
+
+  if g_debug_Frames then
+  begin
+    e_DrawQuad(p.Obj.X + p.Obj.Rect.X,
+               p.Obj.Y + p.Obj.Rect.Y,
+               p.Obj.X + p.Obj.Rect.X + p.Obj.Rect.Width - 1,
+               p.Obj.Y + p.Obj.Rect.Y + p.Obj.Rect.Height - 1,
+               0, 255, 0);
+  end;
+
+  if (gChatBubble > 0) and (p.FKeys[KEY_CHAT].Pressed) and not p.Ghost then
+    if (p.FMegaRulez[MR_INVIS] <= gTime) or ((gPlayerDrawn <> nil) and ((p = gPlayerDrawn) or
+       ((p.Team = gPlayerDrawn.Team) and (gGameSettings.GameMode <> GM_DM)))) then
+      r_Player_DrawBubble(p);
+
+ // e_DrawPoint(5, 335, 288, 255, 0, 0); // DL, UR, DL, UR
+  if gAimLine and p.Alive and ((p = gPlayer1) or (p = gPlayer2)) then
+    r_Player_DrawAim(p);
+end;
+
+procedure r_Player_DrawAim (p: TPlayer);
+
+  procedure drawCast (sz: Integer; ax0, ay0, ax1, ay1: Integer);
+  var
+    ex, ey: Integer;
+  begin
+
+{$IFDEF ENABLE_HOLMES}
+    if isValidViewPort and (self = gPlayer1) then
+    begin
+      g_Holmes_plrLaser(ax0, ay0, ax1, ay1);
+    end;
+{$ENDIF}
+
+    e_DrawLine(sz, ax0, ay0, ax1, ay1, 255, 0, 0, 96);
+    if (g_Map_traceToNearestWall(ax0, ay0, ax1, ay1, @ex, @ey) <> nil) then
+    begin
+      e_DrawLine(sz, ax0, ay0, ex, ey, 0, 255, 0, 96);
+    end
+    else
+    begin
+      e_DrawLine(sz, ax0, ay0, ex, ey, 0, 0, 255, 96);
+    end;
+  end;
+
+var
+  wx, wy, xx, yy: Integer;
+  angle: SmallInt;
+  sz, len: Word;
+begin
+  wx := p.Obj.X + WEAPONPOINT[p.Direction].X + IfThen(p.Direction = TDirection.D_LEFT, 7, -7);
+  wy := p.Obj.Y + WEAPONPOINT[p.Direction].Y;
+  angle := p.Angle_;
+  len := 1024;
+  sz := 2;
+  case p.CurrWeap of
+    0: begin // Punch
+      len := 12;
+      sz := 4;
+    end;
+    1: begin // Chainsaw
+      len := 24;
+      sz := 6;
+    end;
+    2: begin // Pistol
+      len := 1024;
+      sz := 2;
+      if angle = ANGLE_RIGHTUP then Dec(angle, 2);
+      if angle = ANGLE_RIGHTDOWN then Inc(angle, 4);
+      if angle = ANGLE_LEFTUP then Inc(angle, 2);
+      if angle = ANGLE_LEFTDOWN then Dec(angle, 4);
+    end;
+    3: begin // Shotgun
+      len := 1024;
+      sz := 3;
+      if angle = ANGLE_RIGHTUP then Dec(angle, 2);
+      if angle = ANGLE_RIGHTDOWN then Inc(angle, 4);
+      if angle = ANGLE_LEFTUP then Inc(angle, 2);
+      if angle = ANGLE_LEFTDOWN then Dec(angle, 4);
+    end;
+    4: begin // Double Shotgun
+      len := 1024;
+      sz := 4;
+      if angle = ANGLE_RIGHTUP then Dec(angle, 2);
+      if angle = ANGLE_RIGHTDOWN then Inc(angle, 4);
+      if angle = ANGLE_LEFTUP then Inc(angle, 2);
+      if angle = ANGLE_LEFTDOWN then Dec(angle, 4);
+    end;
+    5: begin // Chaingun
+      len := 1024;
+      sz := 3;
+      if angle = ANGLE_RIGHTUP then Dec(angle, 2);
+      if angle = ANGLE_RIGHTDOWN then Inc(angle, 4);
+      if angle = ANGLE_LEFTUP then Inc(angle, 2);
+      if angle = ANGLE_LEFTDOWN then Dec(angle, 4);
+    end;
+    6: begin // Rocket Launcher
+      len := 1024;
+      sz := 7;
+      if angle = ANGLE_RIGHTUP then Inc(angle, 2);
+      if angle = ANGLE_RIGHTDOWN then Inc(angle, 4);
+      if angle = ANGLE_LEFTUP then Dec(angle, 2);
+      if angle = ANGLE_LEFTDOWN then Dec(angle, 4);
+    end;
+    7: begin // Plasmagun
+      len := 1024;
+      sz := 5;
+      if angle = ANGLE_RIGHTUP then Inc(angle);
+      if angle = ANGLE_RIGHTDOWN then Inc(angle, 3);
+      if angle = ANGLE_LEFTUP then Dec(angle);
+      if angle = ANGLE_LEFTDOWN then Dec(angle, 3);
+    end;
+    8: begin // BFG
+      len := 1024;
+      sz := 12;
+      if angle = ANGLE_RIGHTUP then Inc(angle, 1);
+      if angle = ANGLE_RIGHTDOWN then Inc(angle, 2);
+      if angle = ANGLE_LEFTUP then Dec(angle, 1);
+      if angle = ANGLE_LEFTDOWN then Dec(angle, 2);
+    end;
+    9: begin // Super Chaingun
+      len := 1024;
+      sz := 4;
+      if angle = ANGLE_RIGHTUP then Dec(angle, 2);
+      if angle = ANGLE_RIGHTDOWN then Inc(angle, 4);
+      if angle = ANGLE_LEFTUP then Inc(angle, 2);
+      if angle = ANGLE_LEFTDOWN then Dec(angle, 4);
+    end;
+  end;
+  xx := Trunc(Cos(-DegToRad(angle)) * len) + wx;
+  yy := Trunc(Sin(-DegToRad(angle)) * len) + wy;
+  {$IF DEFINED(D2F_DEBUG)}
+  drawCast(sz, wx, wy, xx, yy);
+  {$ELSE}
+  e_DrawLine(sz, wx, wy, xx, yy, 255, 0, 0, 96);
+  {$ENDIF}
+end;
+
+procedure r_Player_DrawGUI (pl: TPlayer);
+var
+  ID: DWORD;
+  X, Y, SY, a, p, m: Integer;
+  tw, th: Word;
+  cw, ch: Byte;
+  s: string;
+  stat: TPlayerStatArray;
+begin
+  X := gPlayerScreenSize.X;
+  SY := gPlayerScreenSize.Y;
+  Y := 0;
+
+  if gShowScore and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
+  begin
+    if gGameSettings.GameMode = GM_CTF then
+      a := 32 + 8
+    else
+      a := 0;
+    if gGameSettings.GameMode = GM_CTF then
+    begin
+      s := 'TEXTURE_PLAYER_REDFLAG';
+      if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
+        s := 'TEXTURE_PLAYER_REDFLAG_S';
+      if gFlags[FLAG_RED].State = FLAG_STATE_DROPPED then
+        s := 'TEXTURE_PLAYER_REDFLAG_D';
+      if g_Texture_Get(s, ID) then
+        e_Draw(ID, X-16-32, 240-72-4, 0, True, False);
+    end;
+
+    s := IntToStr(gTeamStat[TEAM_RED].Score);
+    e_CharFont_GetSize(gMenuFont, s, tw, th);
+    e_CharFont_PrintEx(gMenuFont, X-16-a-tw, 240-72-4, s, TEAMCOLOR[TEAM_RED]);
+
+    if gGameSettings.GameMode = GM_CTF then
+    begin
+      s := 'TEXTURE_PLAYER_BLUEFLAG';
+      if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
+        s := 'TEXTURE_PLAYER_BLUEFLAG_S';
+      if gFlags[FLAG_BLUE].State = FLAG_STATE_DROPPED then
+        s := 'TEXTURE_PLAYER_BLUEFLAG_D';
+      if g_Texture_Get(s, ID) then
+        e_Draw(ID,  X-16-32, 240-32-4, 0, True, False);
+    end;
+
+    s := IntToStr(gTeamStat[TEAM_BLUE].Score);
+    e_CharFont_GetSize(gMenuFont, s, tw, th);
+    e_CharFont_PrintEx(gMenuFont, X-16-a-tw, 240-32-4, s, TEAMCOLOR[TEAM_BLUE]);
+  end;
+
+  if g_Texture_Get('TEXTURE_PLAYER_HUDBG', ID) then
+    e_DrawFill(ID, X, 0, 1, (gPlayerScreenSize.Y div 256)+IfThen(gPlayerScreenSize.Y mod 256 > 0, 1, 0),
+               0, False, False);
+
+  if g_Texture_Get('TEXTURE_PLAYER_HUD', ID) then
+    e_Draw(ID, X+2, Y, 0, True, False);
+
+  if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
+  begin
+    if gShowStat then
+    begin
+      s := IntToStr(pl.Frags);
+      e_CharFont_GetSize(gMenuFont, s, tw, th);
+      e_CharFont_PrintEx(gMenuFont, X-16-tw, Y, s, _RGB(255, 0, 0));
+
+      s := '';
+      p := 1;
+      m := 0;
+      stat := g_Player_GetStats();
+      if stat <> nil then
+      begin
+        p := 1;
+
+        for a := 0 to High(stat) do
+          if stat[a].Name <> pl.Name then
+          begin
+            if stat[a].Frags > m then m := stat[a].Frags;
+            if stat[a].Frags > pl.Frags then p := p+1;
+          end;
+      end;
+
+      s := IntToStr(p)+' / '+IntToStr(Length(stat))+' ';
+      if pl.Frags >= m then s := s+'+' else s := s+'-';
+      s := s+IntToStr(Abs(pl.Frags-m));
+
+      e_CharFont_GetSize(gMenuSmallFont, s, tw, th);
+      e_CharFont_PrintEx(gMenuSmallFont, X-16-tw, Y+32, s, _RGB(255, 0, 0));
+    end;
+
+    if gLMSRespawn > LMS_RESPAWN_NONE then
+    begin
+      s := _lc[I_GAME_WARMUP];
+      e_CharFont_GetSize(gMenuFont, s, tw, th);
+      s := s + ': ' + IntToStr((gLMSRespawnTime - gTime) div 1000);
+      e_CharFont_PrintEx(gMenuFont, X-64-tw, SY-32, s, _RGB(0, 255, 0));
+    end
+    else if gShowLives and (gGameSettings.MaxLives > 0) then
+    begin
+      s := IntToStr(pl.Lives);
+      e_CharFont_GetSize(gMenuFont, s, tw, th);
+      e_CharFont_PrintEx(gMenuFont, X-16-tw, SY-32, s, _RGB(0, 255, 0));
+    end;
+  end;
+
+  e_CharFont_GetSize(gMenuSmallFont, pl.Name, tw, th);
+  e_CharFont_PrintEx(gMenuSmallFont, X+98-(tw div 2), Y+8, pl.Name, _RGB(255, 0, 0));
+
+  if R_BERSERK in pl.FRulez then
+    e_Draw(gItemsTexturesID[ITEM_MEDKIT_BLACK], X+37, Y+45, 0, True, False)
+  else
+    e_Draw(gItemsTexturesID[ITEM_MEDKIT_LARGE], X+37, Y+45, 0, True, False);
+
+  if g_Texture_Get('TEXTURE_PLAYER_ARMORHUD', ID) then
+    e_Draw(ID, X+36, Y+77, 0, True, False);
+
+  s := IntToStr(IfThen(pl.Health > 0, pl.Health, 0));
+  e_CharFont_GetSize(gMenuFont, s, tw, th);
+  e_CharFont_PrintEx(gMenuFont, X+178-tw, Y+40, s, _RGB(255, 0, 0));
+
+  s := IntToStr(pl.Armor);
+  e_CharFont_GetSize(gMenuFont, s, tw, th);
+  e_CharFont_PrintEx(gMenuFont, X+178-tw, Y+68, s, _RGB(255, 0, 0));
+
+  s := IntToStr(pl.GetAmmoByWeapon(pl.CurrWeap));
+
+  case pl.CurrWeap of
+    WEAPON_KASTET:
+    begin
+      s := '--';
+      ID := gItemsTexturesID[ITEM_WEAPON_KASTET];
+    end;
+    WEAPON_SAW:
+    begin
+      s := '--';
+      ID := gItemsTexturesID[ITEM_WEAPON_SAW];
+    end;
+    WEAPON_PISTOL: ID := gItemsTexturesID[ITEM_WEAPON_PISTOL];
+    WEAPON_CHAINGUN: ID := gItemsTexturesID[ITEM_WEAPON_CHAINGUN];
+    WEAPON_SHOTGUN1: ID := gItemsTexturesID[ITEM_WEAPON_SHOTGUN1];
+    WEAPON_SHOTGUN2: ID := gItemsTexturesID[ITEM_WEAPON_SHOTGUN2];
+    WEAPON_SUPERPULEMET: ID := gItemsTexturesID[ITEM_WEAPON_SUPERPULEMET];
+    WEAPON_ROCKETLAUNCHER: ID := gItemsTexturesID[ITEM_WEAPON_ROCKETLAUNCHER];
+    WEAPON_PLASMA: ID := gItemsTexturesID[ITEM_WEAPON_PLASMA];
+    WEAPON_BFG: ID := gItemsTexturesID[ITEM_WEAPON_BFG];
+    WEAPON_FLAMETHROWER: ID := gItemsTexturesID[ITEM_WEAPON_FLAMETHROWER];
+  end;
+
+  e_CharFont_GetSize(gMenuFont, s, tw, th);
+  e_CharFont_PrintEx(gMenuFont, X+178-tw, Y+158, s, _RGB(255, 0, 0));
+  e_Draw(ID, X+20, Y+160, 0, True, False);
+
+  if R_KEY_RED in pl.FRulez then
+    e_Draw(gItemsTexturesID[ITEM_KEY_RED], X+78, Y+214, 0, True, False);
+
+  if R_KEY_GREEN in pl.FRulez then
+    e_Draw(gItemsTexturesID[ITEM_KEY_GREEN], X+95, Y+214, 0, True, False);
+
+  if R_KEY_BLUE in pl.FRulez then
+    e_Draw(gItemsTexturesID[ITEM_KEY_BLUE], X+112, Y+214, 0, True, False);
+
+  if pl.JetFuel > 0 then
+  begin
+    if g_Texture_Get('TEXTURE_PLAYER_HUDAIR', ID) then
+      e_Draw(ID, X+2, Y+116, 0, True, False);
+    if g_Texture_Get('TEXTURE_PLAYER_HUDJET', ID) then
+      e_Draw(ID, X+2, Y+126, 0, True, False);
+    e_DrawLine(4, X+16, Y+122, X+16+Trunc(168*IfThen(pl.Air > 0, pl.Air, 0)/AIR_MAX), Y+122, 0, 0, 196);
+    e_DrawLine(4, X+16, Y+132, X+16+Trunc(168*pl.JetFuel/JET_MAX), Y+132, 208, 0, 0);
+  end
+  else
+  begin
+    if g_Texture_Get('TEXTURE_PLAYER_HUDAIR', ID) then
+      e_Draw(ID, X+2, Y+124, 0, True, False);
+    e_DrawLine(4, X+16, Y+130, X+16+Trunc(168*IfThen(pl.Air > 0, pl.Air, 0)/AIR_MAX), Y+130, 0, 0, 196);
+  end;
+
+  if gShowPing and g_Game_IsClient then
+  begin
+    s := _lc[I_GAME_PING_HUD] + IntToStr(NetPeer.lastRoundTripTime) + _lc[I_NET_SLIST_PING_MS];
+    e_TextureFontPrint(X + 4, Y + 242, s, gStdFont);
+    Y := Y + 16;
+  end;
+
+  if pl.Spectator then
+  begin
+    e_TextureFontPrint(X + 4, Y + 242, _lc[I_PLAYER_SPECT], gStdFont);
+    e_TextureFontPrint(X + 4, Y + 258, _lc[I_PLAYER_SPECT2], gStdFont);
+    e_TextureFontPrint(X + 4, Y + 274, _lc[I_PLAYER_SPECT1], gStdFont);
+    if pl.NoRespawn then
+    begin
+      e_TextureFontGetSize(gStdFont, cw, ch);
+      s := _lc[I_PLAYER_SPECT4];
+      e_TextureFontPrintEx(gScreenWidth div 2 - cw*(Length(s) div 2),
+                         gScreenHeight-4-ch, s, gStdFont, 255, 255, 255, 1, True);
+      e_TextureFontPrint(X + 4, Y + 290, _lc[I_PLAYER_SPECT1S], gStdFont);
+    end;
+
+  end;
+end;
+
+procedure r_Player_DrawRulez (p: TPlayer);
+var
+  dr: Boolean;
+begin
+  // При взятии неуязвимости рисуется инверсионный белый фон
+  if (p.FMegaRulez[MR_INVUL] >= gTime) and (p.SpawnInvul < gTime) then
+  begin
+    if (p.FMegaRulez[MR_INVUL]-gTime) <= 2100 then
+      dr := not Odd((p.FMegaRulez[MR_INVUL]-gTime) div 300)
+    else
+      dr := True;
+
+    if dr then
+      e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1,
+                     191, 191, 191, 0, TBlending.Invert);
+  end;
+
+  // При взятии защитного костюма рисуется зеленоватый фон
+  if p.FMegaRulez[MR_SUIT] >= gTime then
+  begin
+    if (p.FMegaRulez[MR_SUIT]-gTime) <= 2100 then
+      dr := not Odd((p.FMegaRulez[MR_SUIT]-gTime) div 300)
+    else
+      dr := True;
+
+    if dr then
+      e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1,
+                     0, 96, 0, 200, TBlending.None);
+  end;
+
+  // При взятии берсерка рисуется красноватый фон
+  if (p.Berserk >= 0) and (LongWord(p.Berserk) >= gTime) and (gFlash = 2) then
+  begin
+    e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1,
+                     255, 0, 0, 200, TBlending.None);
+  end;
+end;
+
+procedure r_Player_DrawPain (p: TPlayer);
+var
+  a, h: Integer;
+begin
+  if p.Pain = 0 then Exit;
+
+  a := p.Pain;
+
+  if a < 15 then h := 0
+  else if a < 35 then h := 1
+  else if a < 55 then h := 2
+  else if a < 75 then h := 3
+  else if a < 95 then h := 4
+  else h := 5;
+
+  //if a > 255 then a := 255;
+
+  e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 255, 0, 0, 255-h*50);
+  //e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 255-min(128, a), 255-a, 255-a, 0, B_FILTER);
+end;
+
+procedure r_Player_DrawPickup (p: TPlayer);
+var
+  a, h: Integer;
+begin
+  if p.Pickup = 0 then Exit;
+
+  a := p.Pickup;
+
+  if a < 15 then h := 1
+  else if a < 35 then h := 2
+  else if a < 55 then h := 3
+  else if a < 75 then h := 4
+  else h := 5;
+
+  e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 150, 200, 150, 255-h*50);
+end;
+
+procedure r_Player_DrawCorpse (p: TCorpse);
+var
+  fX, fY: Integer;
+begin
+  if p.State = CORPSE_STATE_REMOVEME then
+    Exit;
+
+  p.Obj.lerp(gLerpFactor, fX, fY);
+
+  if p.Animation <> nil then
+    p.Animation.Draw(fX, fY, TMirrorType.None);
+
+  if p.AnimationMask <> nil then
+  begin
+    e_Colors := p.Color;
+    p.AnimationMask.Draw(fX, fY, TMirrorType.None);
+    e_Colors.R := 255;
+    e_Colors.G := 255;
+    e_Colors.B := 255;
+  end;
+end;
+
+end.