DEADSOFTWARE

added player handicap
[d2df-sdl.git] / src / game / g_game.pas
index c38572befa771c29a3d575d8ddcf940aaf6dadf6..7a70840b33b47db24b85276e7d1d63f77f1f26e1 100644 (file)
@@ -23,7 +23,7 @@ uses
   MAPDEF,
   g_basic, g_player, e_graphics, g_res_downloader,
   g_sound, g_gui, utils, md5, mempool, xprofiler,
-  g_touch;
+  g_touch, g_weapons;
 
 type
   TGameSettings = record
@@ -124,6 +124,7 @@ function  g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
 procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
 procedure g_Game_Announce_KillCombo(Param: Integer);
+procedure g_Game_Announce_BodyKill(SpawnerUID: Word);
 procedure g_Game_StartVote(Command, Initiator: string);
 procedure g_Game_CheckVote;
 procedure g_TakeScreenShot();
@@ -201,6 +202,7 @@ const
   DE_GLOBEVENT = 0;
   DE_BFGHIT    = 1;
   DE_KILLCOMBO = 2;
+  DE_BODYKILL  = 3;
 
   ANNOUNCE_NONE   = 0;
   ANNOUNCE_ME     = 1;
@@ -234,6 +236,12 @@ var
   gAnnouncer: Byte = ANNOUNCE_NONE;
   goodsnd: array[0..3] of TPlayableSound;
   killsnd: array[0..3] of TPlayableSound;
+  hahasnd: array[0..2] of TPlayableSound;
+  sound_get_flag: array[0..1] of TPlayableSound;
+  sound_lost_flag: array[0..1] of TPlayableSound;
+  sound_ret_flag: array[0..1] of TPlayableSound;
+  sound_cap_flag: array[0..1] of TPlayableSound;
+  gBodyKillEvent: Integer = -1;
   gDefInterTime: ShortInt = -1;
   gInterEndTime: LongWord = 0;
   gInterTime: LongWord = 0;
@@ -317,6 +325,8 @@ var
   gDelayedEvents: Array of TDelayedEvent;
   gUseChatSounds: Boolean = True;
   gChatSounds: Array of TChatSound;
+  gSelectWeapon: Array [0..1, WP_FIRST..WP_LAST] of Boolean; // [player, weapon]
+  gInterReadyCount: Integer = 0;
 
   g_dbg_ignore_bounds: Boolean = false;
   r_smallmap_h: Integer = 0; // 0: left; 1: center; 2: right
@@ -364,7 +374,7 @@ uses
 {$ENDIF}
   e_texture, g_textures, g_main, g_window, g_menu,
   e_input, e_log, g_console, g_items, g_map, g_panel,
-  g_playermodel, g_gfx, g_options, g_weapons, Math,
+  g_playermodel, g_gfx, g_options, Math,
   g_triggers, g_monsters, e_sound, CONFIG,
   g_language, g_net,
   ENet, e_msg, g_netmsg, g_netmaster,
@@ -559,7 +569,6 @@ var
   FPSCounter, UPSCounter: Word;
   FPSTime, UPSTime: LongWord;
   DataLoaded: Boolean = False;
-  LastScreenShot: Int64;
   IsDrawStat: Boolean = False;
   CustomStat: TEndCustomGameStat;
   SingleStat: TEndSingleGameStat;
@@ -570,6 +579,7 @@ var
   MessageLineLength: Integer = 80;
   MapList: SSArray = nil;
   MapIndex: Integer = -1;
+  InterReadyTime: Integer = -1;
   MegaWAD: record
     info: TMegaWADInfo;
     endpic: String;
@@ -970,6 +980,7 @@ begin
               SetLength(CustomStat.PlayerStat, Length(CustomStat.PlayerStat)+1);
               with CustomStat.PlayerStat[High(CustomStat.PlayerStat)] do
               begin
+                Num := a;
                 Name := gPlayers[a].Name;
                 Frags := gPlayers[a].Frags;
                 Deaths := gPlayers[a].Death;
@@ -984,6 +995,8 @@ begin
         end;
 
         g_Game_ExecuteEvent('onmapend');
+        if not g_Game_IsClient then g_Player_ResetReady;
+        gInterReadyCount := 0;
 
       // Çàòóõàþùèé ýêðàí:
         EndingGameCounter := 255;
@@ -1307,7 +1320,6 @@ begin
     gPauseMain := false;
     gPauseHolmes := false;
     gTime := 0;
-    LastScreenShot := 0;
 
     {e_MouseInfo.Accel := 1.0;}
 
@@ -1317,7 +1329,7 @@ begin
     g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
     g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True);
     g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True);
-    g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True);
+    g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True, True);
     g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
 
     g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
@@ -1493,68 +1505,78 @@ retry:
     goto retry;
 end;
 
-function isKeyPressed (key1: Word; key2: Word): Boolean;
-begin
-  if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end;
-  if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end;
-  result := false;
-end;
-
-procedure processPlayerControls (plr: TPlayer; var ctrl: TPlayerControl; var MoveButton: Byte; p2hack: Boolean=false);
-var
-  time: Word;
-  strafeDir: Byte;
-  i: Integer;
+procedure ProcessPlayerControls (plr: TPlayer; p: Integer; var MoveButton: Byte);
+  var
+    time: Word;
+    strafeDir: Byte;
+    i: Integer;
 begin
   if (plr = nil) then exit;
-  if (p2hack) then time := 1000 else time := 1;
+  if (p = 2) then time := 1000 else time := 1;
   strafeDir := MoveButton shr 4;
   MoveButton := MoveButton and $0F;
-  with ctrl do
-  begin
-         if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
-    else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
-    else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
 
-    // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
-         if MoveButton = 1 then plr.PressKey(KEY_LEFT, time)
-    else if MoveButton = 2 then plr.PressKey(KEY_RIGHT, time);
+  if gPlayerAction[p, ACTION_MOVELEFT] and (not gPlayerAction[p, ACTION_MOVERIGHT]) then
+    MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
+  else if (not gPlayerAction[p, ACTION_MOVELEFT]) and gPlayerAction[p, ACTION_MOVERIGHT] then
+    MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
+  else if (not gPlayerAction[p, ACTION_MOVELEFT]) and (not gPlayerAction[p, ACTION_MOVERIGHT]) then
+    MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
+
+  // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
+  if MoveButton = 1 then
+    plr.PressKey(KEY_LEFT, time)
+  else if MoveButton = 2 then
+    plr.PressKey(KEY_RIGHT, time);
+
+  // if we have "strafe" key, turn off old strafe mechanics
+  if gPlayerAction[p, ACTION_STRAFE] then
+  begin
+    // new strafe mechanics
+    if (strafeDir = 0) then
+      strafeDir := MoveButton; // start strafing
+    // now set direction according to strafe (reversed)
+    if (strafeDir = 2) then
+      plr.SetDirection(TDirection.D_LEFT)
+    else if (strafeDir = 1) then
+      plr.SetDirection(TDirection.D_RIGHT)
+  end
+  else
+  begin
+    strafeDir := 0; // not strafing anymore
+    // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
+    if (MoveButton = 2) and gPlayerAction[p, ACTION_MOVELEFT] then
+      plr.SetDirection(TDirection.D_LEFT)
+    // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
+    else if (MoveButton = 1) and gPlayerAction[p, ACTION_MOVERIGHT] then
+      plr.SetDirection(TDirection.D_RIGHT)
+    // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
+    else if MoveButton <> 0 then
+      plr.SetDirection(TDirection(MoveButton-1))
+  end;
 
-    // if we have "strafe" key, turn off old strafe mechanics
-    if isKeyPressed(KeyStrafe, KeyStrafe2) then
-    begin
-      // new strafe mechanics
-      if (strafeDir = 0) then strafeDir := MoveButton; // start strafing
-      // now set direction according to strafe (reversed)
-           if (strafeDir = 2) then plr.SetDirection(TDirection.D_LEFT)
-      else if (strafeDir = 1) then plr.SetDirection(TDirection.D_RIGHT);
-    end
-    else
-    begin
-      strafeDir := 0; // not strafing anymore
-      // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
-           if (MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then plr.SetDirection(TDirection.D_LEFT)
-      // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
-      else if (MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then plr.SetDirection(TDirection.D_RIGHT)
-      // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
-      else if MoveButton <> 0 then plr.SetDirection(TDirection(MoveButton-1));
-    end;
+  // fix movebutton state
+  MoveButton := MoveButton or (strafeDir shl 4);
 
-    // fix movebutton state
-    MoveButton := MoveButton or (strafeDir shl 4);
+  // Îñòàëüíûå êëàâèøè:
+  if gPlayerAction[p, ACTION_JUMP] then plr.PressKey(KEY_JUMP, time);
+  if gPlayerAction[p, ACTION_LOOKUP] then plr.PressKey(KEY_UP, time);
+  if gPlayerAction[p, ACTION_LOOKDOWN] then plr.PressKey(KEY_DOWN, time);
+  if gPlayerAction[p, ACTION_ATTACK] then plr.PressKey(KEY_FIRE);
+  if gPlayerAction[p, ACTION_WEAPNEXT] then plr.PressKey(KEY_NEXTWEAPON);
+  if gPlayerAction[p, ACTION_WEAPPREV] then plr.PressKey(KEY_PREVWEAPON);
+  if gPlayerAction[p, ACTION_ACTIVATE] then plr.PressKey(KEY_OPEN);
 
-    // Îñòàëüíûå êëàâèøè:
-    if isKeyPressed(KeyJump, KeyJump2) then plr.PressKey(KEY_JUMP, time);
-    if isKeyPressed(KeyUp, KeyUp2) then plr.PressKey(KEY_UP, time);
-    if isKeyPressed(KeyDown, KeyDown2) then plr.PressKey(KEY_DOWN, time);
-    if isKeyPressed(KeyFire, KeyFire2) then plr.PressKey(KEY_FIRE);
-    if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then plr.PressKey(KEY_NEXTWEAPON);
-    if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then plr.PressKey(KEY_PREVWEAPON);
-    if isKeyPressed(KeyOpen, KeyOpen2) then plr.PressKey(KEY_OPEN);
+  gPlayerAction[p, ACTION_WEAPNEXT] := False; // HACK, remove after readyweaon&pendinweapon implementation
+  gPlayerAction[p, ACTION_WEAPPREV] := False; // HACK, remove after readyweaon&pendinweapon implementation
 
-    for i := 0 to High(KeyWeapon) do
-      if isKeyPressed(KeyWeapon[i], KeyWeapon2[i]) then
-        plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best
+  for i := WP_FIRST to WP_LAST do
+  begin
+    if gSelectWeapon[p, i] then
+    begin
+      plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best
+      gSelectWeapon[p, i] := False
+    end
   end;
 
   // HACK: add dynlight here
@@ -1573,6 +1595,14 @@ begin
   if gwin_has_stencil and g_playerLight then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6);
 end;
 
+// HACK: don't have a "key was pressed" function
+procedure InterReady();
+begin
+  if InterReadyTime > gTime then Exit;
+  InterReadyTime := gTime + 3000;
+  MC_SEND_CheatRequest(NET_CHEAT_READY);
+end;
+
 procedure g_Game_Update();
 var
   Msg: g_gui.TMessage;
@@ -1658,12 +1688,14 @@ begin
           (
             (
               e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or
-              e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN)
+              e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN) or
+              e_KeyPressed(JOY0_ATTACK) or e_KeyPressed(JOY1_ATTACK) or
+              e_KeyPressed(JOY2_ATTACK) or e_KeyPressed(JOY3_ATTACK)
             )
             and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
             and (g_ActiveWindow = nil)
           )
-          or (g_Game_IsNet and (gInterTime > gInterEndTime))
+          or (g_Game_IsNet and ((gInterTime > gInterEndTime) or (gInterReadyCount >= NetClientCount)))
         )
         then
         begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
@@ -1698,6 +1730,22 @@ begin
             end;
 
           Exit;
+        end
+        else if g_Game_IsClient and
+        (
+          (
+            e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or
+            e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN) or
+            e_KeyPressed(JOY0_ATTACK) or e_KeyPressed(JOY1_ATTACK) or
+            e_KeyPressed(JOY2_ATTACK) or e_KeyPressed(JOY3_ATTACK)
+          )
+          and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
+          and (g_ActiveWindow = nil)
+        )
+        then
+        begin
+          // ready / unready
+          InterReady();
         end;
 
         if gState = STATE_INTERTEXT then
@@ -1712,6 +1760,7 @@ begin
           // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
             if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
               begin
+                InterReadyTime := -1;
                 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
                 begin
                   g_Game_ExecuteEvent('onwadend');
@@ -1751,31 +1800,9 @@ begin
         g_Serverlist_Control(slCurrent, slTable);
   end;
 
-  if g_Game_IsNet then
-    if not gConsoleShow then
-      if not gChatShow then
-      begin
-        if g_ActiveWindow = nil then
-        begin
-          if e_KeyPressed(gGameControls.GameControls.Chat) or e_KeyPressed(VK_CHAT) then
-            g_Console_Chat_Switch(False)
-          else if (e_KeyPressed(gGameControls.GameControls.TeamChat) or e_KeyPressed(VK_TEAM)) and
-                  (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
-            g_Console_Chat_Switch(True);
-        end;
-      end else
-        if not gChatEnter then
-          if (not e_KeyPressed(gGameControls.GameControls.Chat))
-             and (not e_KeyPressed(gGameControls.GameControls.TeamChat))
-             and (not e_KeyPressed(VK_CHAT))
-             and (not e_KeyPressed(VK_TEAM)) then
-            gChatEnter := True;
-
 // Ñòàòèñòèêà ïî Tab:
   if gGameOn then
-    IsDrawStat := (not gConsoleShow) and (not gChatShow) and
-                  (gGameSettings.GameType <> GT_SINGLE) and
-                  (e_KeyPressed(gGameControls.GameControls.Stat) or e_KeyPressed(VK_STATUS));
+    IsDrawStat := (not gConsoleShow) and (not gChatShow) and (gGameSettings.GameType <> GT_SINGLE) and g_Console_Action(ACTION_SCORES);
 
 // Èãðà èäåò:
   if gGameOn and not gPause and (gState <> STATE_FOLD) then
@@ -1850,8 +1877,8 @@ begin
       if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
       if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
       begin
-        processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
-        processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
+        ProcessPlayerControls(gPlayer1, 0, P1MoveButton);
+        ProcessPlayerControls(gPlayer2, 1, P2MoveButton);
       end  // if not console
       else
       begin
@@ -1866,8 +1893,7 @@ begin
     begin
       if not gSpectKeyPress then
       begin
-        if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)
-           and (not gSpectAuto) then
+        if gPlayerAction[0, ACTION_JUMP] and (not gSpectAuto) then
         begin
           // switch spect mode
           case gSpectMode of
@@ -1881,21 +1907,21 @@ begin
         if (gSpectMode = SPECT_MAPVIEW)
            and (not gSpectAuto) then
         begin
-          if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
+          if gPlayerAction[0, ACTION_MOVELEFT] then
             gSpectX := Max(gSpectX - gSpectStep, 0);
-          if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
+          if gPlayerAction[0, ACTION_MOVERIGHT] then
             gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
-          if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
+          if gPlayerAction[0, ACTION_LOOKUP] then
             gSpectY := Max(gSpectY - gSpectStep, 0);
-          if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
+          if gPlayerAction[0, ACTION_LOOKDOWN] then
             gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
-          if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
+          if gPlayerAction[0, ACTION_WEAPPREV] then
           begin
             // decrease step
             if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
             gSpectKeyPress := True;
           end;
-          if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
+          if gPlayerAction[0, ACTION_WEAPNEXT] then
           begin
             // increase step
             if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
@@ -1905,44 +1931,44 @@ begin
         if (gSpectMode = SPECT_PLAYERS)
            and (not gSpectAuto) then
         begin
-          if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
+          if gPlayerAction[0, ACTION_LOOKUP] then
           begin
             // add second view
             gSpectViewTwo := True;
             gSpectKeyPress := True;
           end;
-          if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
+          if gPlayerAction[0, ACTION_LOOKDOWN] then
           begin
             // remove second view
             gSpectViewTwo := False;
             gSpectKeyPress := True;
           end;
-          if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
+          if gPlayerAction[0, ACTION_MOVELEFT] then
           begin
             // prev player (view 1)
             gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
             gSpectKeyPress := True;
           end;
-          if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
+          if gPlayerAction[0, ACTION_MOVERIGHT] then
           begin
             // next player (view 1)
             gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
             gSpectKeyPress := True;
           end;
-          if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
+          if gPlayerAction[0, ACTION_WEAPPREV] then
           begin
             // prev player (view 2)
             gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
             gSpectKeyPress := True;
           end;
-          if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
+          if gPlayerAction[0, ACTION_WEAPNEXT] then
           begin
             // next player (view 2)
             gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
             gSpectKeyPress := True;
           end;
         end;
-        if isKeyPressed(gGameControls.P1Control.KeyFire, gGameControls.P1Control.KeyFire2) then
+        if gPlayerAction[0, ACTION_ATTACK] then
         begin
           if (gSpectMode = SPECT_STATS) and (not gSpectAuto) then
           begin
@@ -1961,14 +1987,14 @@ begin
         end;
       end
       else
-        if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
-           (not isKeyPressed(gGameControls.P1Control.KeyFire, gGameControls.P1Control.KeyFire2)) and
-           (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
-           (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
-           (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
-           (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
-           (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
-           (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
+        if (not gPlayerAction[0, ACTION_JUMP]) and
+           (not gPlayerAction[0, ACTION_ATTACK]) and
+           (not gPlayerAction[0, ACTION_MOVELEFT]) and
+           (not gPlayerAction[0, ACTION_MOVERIGHT]) and
+           (not gPlayerAction[0, ACTION_LOOKUP]) and
+           (not gPlayerAction[0, ACTION_LOOKDOWN]) and
+           (not gPlayerAction[0, ACTION_WEAPPREV]) and
+           (not gPlayerAction[0, ACTION_WEAPNEXT]) then
           gSpectKeyPress := False;
 
       if gSpectAuto then
@@ -2143,14 +2169,6 @@ begin
     end;
   end;
 
-// Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
-  if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) or e_KeyPressed(VK_PRINTSCR) then
-    if (GetTimer()-LastScreenShot) > 200000 div 1000 then
-    begin
-      g_TakeScreenShot();
-      LastScreenShot := GetTimer();
-    end;
-
 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
   if e_KeyPressed(IK_F10) and
      gGameOn and
@@ -2184,6 +2202,9 @@ begin
               if g_Game_IsNet and g_Game_IsServer then
                 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
             end;
+          DE_BODYKILL:
+            if gGameOn then
+              g_Game_Announce_BodyKill(gDelayedEvents[a].DENum);
         end;
         gDelayedEvents[a].Pending := False;
       end;
@@ -2286,6 +2307,7 @@ begin
   g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_B_DROP');
   g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
   g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
+  g_Texture_CreateWADEx('TEXTURE_PLAYER_INDICATOR', GameWAD+':TEXTURES\PLRIND');
 
   hasPBarGfx := true;
   if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD+':TEXTURES\LLEFT') then hasPBarGfx := false;
@@ -2318,12 +2340,14 @@ begin
   g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_DN', GameWAD+':WEAPONS\PUNCHB_DN', 64, 64, 4, False);
   g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
   g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
+  g_Sound_CreateWADEx('SOUND_GAME_SECRET', GameWAD+':SOUNDS\SECRET');
   g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
   g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
   g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
   g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
   g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
   g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
+  g_Sound_CreateWADEx('SOUND_GAME_BURNING', GameWAD+':SOUNDS\BURNING');
   g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
   g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
   g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
@@ -2335,6 +2359,17 @@ begin
   g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
   g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
   g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
+  g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA1', GameWAD+':SOUNDS\MUHAHA1');
+  g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA2', GameWAD+':SOUNDS\MUHAHA2');
+  g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA3', GameWAD+':SOUNDS\MUHAHA3');
+  g_Sound_CreateWADEx('SOUND_CTF_GET1', GameWAD+':SOUNDS\GETFLAG1');
+  g_Sound_CreateWADEx('SOUND_CTF_GET2', GameWAD+':SOUNDS\GETFLAG2');
+  g_Sound_CreateWADEx('SOUND_CTF_LOST1', GameWAD+':SOUNDS\LOSTFLG1');
+  g_Sound_CreateWADEx('SOUND_CTF_LOST2', GameWAD+':SOUNDS\LOSTFLG2');
+  g_Sound_CreateWADEx('SOUND_CTF_RETURN1', GameWAD+':SOUNDS\RETFLAG1');
+  g_Sound_CreateWADEx('SOUND_CTF_RETURN2', GameWAD+':SOUNDS\RETFLAG2');
+  g_Sound_CreateWADEx('SOUND_CTF_CAPTURE1', GameWAD+':SOUNDS\CAPFLAG1');
+  g_Sound_CreateWADEx('SOUND_CTF_CAPTURE2', GameWAD+':SOUNDS\CAPFLAG2');
 
   goodsnd[0] := TPlayableSound.Create();
   goodsnd[1] := TPlayableSound.Create();
@@ -2356,6 +2391,32 @@ begin
   killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
   killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
 
+  hahasnd[0] := TPlayableSound.Create();
+  hahasnd[1] := TPlayableSound.Create();
+  hahasnd[2] := TPlayableSound.Create();
+
+  hahasnd[0].SetByName('SOUND_ANNOUNCER_MUHAHA1');
+  hahasnd[1].SetByName('SOUND_ANNOUNCER_MUHAHA2');
+  hahasnd[2].SetByName('SOUND_ANNOUNCER_MUHAHA3');
+
+  sound_get_flag[0] := TPlayableSound.Create();
+  sound_get_flag[1] := TPlayableSound.Create();
+  sound_lost_flag[0] := TPlayableSound.Create();
+  sound_lost_flag[1] := TPlayableSound.Create();
+  sound_ret_flag[0] := TPlayableSound.Create();
+  sound_ret_flag[1] := TPlayableSound.Create();
+  sound_cap_flag[0] := TPlayableSound.Create();
+  sound_cap_flag[1] := TPlayableSound.Create();
+
+  sound_get_flag[0].SetByName('SOUND_CTF_GET1');
+  sound_get_flag[1].SetByName('SOUND_CTF_GET2');
+  sound_lost_flag[0].SetByName('SOUND_CTF_LOST1');
+  sound_lost_flag[1].SetByName('SOUND_CTF_LOST2');
+  sound_ret_flag[0].SetByName('SOUND_CTF_RETURN1');
+  sound_ret_flag[1].SetByName('SOUND_CTF_RETURN2');
+  sound_cap_flag[0].SetByName('SOUND_CTF_CAPTURE1');
+  sound_cap_flag[1].SetByName('SOUND_CTF_CAPTURE2');
+
   g_Game_LoadChatSounds(GameWAD+':CHATSND\SNDCFG');
 
   g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
@@ -2400,12 +2461,14 @@ begin
   g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_DN');
   g_Sound_Delete('SOUND_GAME_TELEPORT');
   g_Sound_Delete('SOUND_GAME_NOTELEPORT');
+  g_Sound_Delete('SOUND_GAME_SECRET');
   g_Sound_Delete('SOUND_GAME_DOOROPEN');
   g_Sound_Delete('SOUND_GAME_DOORCLOSE');
   g_Sound_Delete('SOUND_GAME_BULK1');
   g_Sound_Delete('SOUND_GAME_BULK2');
   g_Sound_Delete('SOUND_GAME_BUBBLE1');
   g_Sound_Delete('SOUND_GAME_BUBBLE2');
+  g_Sound_Delete('SOUND_GAME_BURNING');
   g_Sound_Delete('SOUND_GAME_SWITCH1');
   g_Sound_Delete('SOUND_GAME_SWITCH0');
 
@@ -2429,6 +2492,32 @@ begin
   g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
   g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
 
+  hahasnd[0].Free();
+  hahasnd[1].Free();
+  hahasnd[2].Free();
+
+  g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA1');
+  g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA2');
+  g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA3');
+
+  sound_get_flag[0].Free();
+  sound_get_flag[1].Free();
+  sound_lost_flag[0].Free();
+  sound_lost_flag[1].Free();
+  sound_ret_flag[0].Free();
+  sound_ret_flag[1].Free();
+  sound_cap_flag[0].Free();
+  sound_cap_flag[1].Free();
+
+  g_Sound_Delete('SOUND_CTF_GET1');
+  g_Sound_Delete('SOUND_CTF_GET2');
+  g_Sound_Delete('SOUND_CTF_LOST1');
+  g_Sound_Delete('SOUND_CTF_LOST2');
+  g_Sound_Delete('SOUND_CTF_RETURN1');
+  g_Sound_Delete('SOUND_CTF_RETURN2');
+  g_Sound_Delete('SOUND_CTF_CAPTURE1');
+  g_Sound_Delete('SOUND_CTF_CAPTURE2');
+
   g_Game_FreeChatSounds();
 
   DataLoaded := False;
@@ -2447,7 +2536,7 @@ begin
 
   g_ProcessMessages();
 
-  if e_KeyPressed(IK_TAB) or e_KeyPressed(VK_STATUS) then
+  if g_Console_Action(ACTION_SCORES) then
   begin
     if not gStatsPressed then
     begin
@@ -2633,9 +2722,12 @@ begin
               gg := g;
               bb := b;
             end;
-            e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
-            e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
-            e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
+            if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady) then
+              e_TextureFontPrintEx(x+16, _y, Name + ' *', gStdFont, rr, gg, bb, 1)
+            else
+              e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1);
+            e_TextureFontPrintEx(x+w1+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
+            e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
             _y := _y+24;
           end;
 
@@ -2660,7 +2752,10 @@ begin
         else
           r := 255;
 
-        e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
+        if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady) then
+          e_TextureFontPrintEx(x+8+16+8, _y+4, Name + ' *', gStdFont, r, r, r, 1, True)
+        else
+          e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
         e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
         e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
         _y := _y+24;
@@ -3007,10 +3102,10 @@ begin
             aY2 := aY + aY2 - 1;
 
             case LiftType of
-              0: e_DrawFillQuad(aX, aY, aX2, aY2, 116,  72,  36, 0);
-              1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124,  96, 0);
-              2: e_DrawFillQuad(aX, aY, aX2, aY2, 200,  80,   4, 0);
-              3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140,  56, 0);
+              LIFTTYPE_UP:    e_DrawFillQuad(aX, aY, aX2, aY2, 116,  72,  36, 0);
+              LIFTTYPE_DOWN:  e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124,  96, 0);
+              LIFTTYPE_LEFT:  e_DrawFillQuad(aX, aY, aX2, aY2, 200,  80,   4, 0);
+              LIFTTYPE_RIGHT: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140,  56, 0);
             end;
           end;
     end;
@@ -3539,6 +3634,8 @@ begin
 
   renderMapInternal(-c, -d, true);
 
+  if (gGameSettings.GameMode <> GM_SINGLE) and gPlayerIndicator then
+    p.DrawIndicator();
   if p.FSpectator then
     e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
                          p.GameY + PLAYER_RECT_CY - 4,
@@ -4588,7 +4685,8 @@ begin
 
     ProcessLoading(true);
 
-    if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) or e_KeyPressed(VK_ESCAPE) then
+    if e_KeyPressed(IK_SPACE) or e_KeyPressed(IK_ESCAPE) or e_KeyPressed(VK_ESCAPE) or
+       e_KeyPressed(JOY0_JUMP) or e_KeyPressed(JOY1_JUMP) or e_KeyPressed(JOY2_JUMP) or e_KeyPressed(JOY3_JUMP) then
     begin
       State := 0;
       break;
@@ -5102,18 +5200,7 @@ var
 begin
   stat := nil;
   cmd := LowerCase(P[0]);
-  if cmd = 'r_showfps' then
-  begin
-    if (Length(P) > 1) and
-       ((P[1] = '1') or (P[1] = '0')) then
-      gShowFPS := (P[1][1] = '1');
-
-    if gShowFPS then
-      g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
-    else
-      g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
-  end
-  else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
+  if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
   begin
     with gGameSettings do
     begin
@@ -5761,11 +5848,6 @@ begin
 
       g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
     end
-    else if (cmd = 'd_joy') then
-    begin
-      for a := 1 to 8 do
-        g_Console_Add(e_JoystickStateToString(a));
-    end
     else if (cmd = 'd_mem') then
     begin
       PrintHeapStats();
@@ -6326,7 +6408,9 @@ begin
   else if (cmd = 'addbot') or
      (cmd = 'bot_add') then
   begin
-    if Length(P) > 1 then
+    if Length(P) > 2 then
+      g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2), StrToIntDef(P[2], 100))
+    else if Length(P) > 1 then
       g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
     else
       g_Bot_Add(TEAM_NONE, 2);
@@ -6334,10 +6418,14 @@ begin
   else if cmd = 'bot_addlist' then
   begin
     if Length(P) > 1 then
+    begin
       if Length(P) = 2 then
         g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
+      else if Length(P) = 3 then
+        g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1), StrToIntDef(P[2], 100))
       else
         g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
+    end;
   end
   else if cmd = 'bot_removeall' then
     g_Bot_RemoveAll()
@@ -6534,12 +6622,13 @@ begin
           end else
           begin
             // Òàêîé êàðòû íåò, èùåì WAD ôàéë
-            P[1] := addWadExtension(P[1]);
-            g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
-            if FileExists(MapsDir + P[1]) then
+            pw := findDiskWad(MapsDir + P[1]);
+            g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, 'WAD ' + P[1]]));
+            if FileExists(pw) then
             begin
               // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
               SetLength(P, 3);
+              P[1] := ExtractRelativePath(MapsDir, pw);
               P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
 
               s := P[1] + ':\' + P[2];
@@ -6759,6 +6848,29 @@ begin
       end;
     end;
   end
+  else if cmd = 'screenshot' then
+  begin
+    g_TakeScreenShot()
+  end
+  else if cmd = 'weapon' then
+  begin
+    if Length(p) = 2 then
+    begin
+      a := WP_FIRST + StrToInt(p[1]) - 1;
+      if (a >= WP_FIRST) and (a <= WP_LAST) then
+        gSelectWeapon[0, a] := True
+    end
+  end
+  else if (cmd = 'p1_weapon') or (cmd = 'p2_weapon') then
+  begin
+    if Length(p) = 2 then
+    begin
+      a := WP_FIRST + StrToInt(p[1]) - 1;
+      b := ord(cmd[2]) - ord('1');
+      if (a >= WP_FIRST) and (a <= WP_LAST) then
+        gSelectWeapon[b, a] := True
+    end
+  end
 // Êîìàíäû Ñâîåé èãðû:
   else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
   begin
@@ -7313,6 +7425,25 @@ begin
   killsnd[n].Play();
 end;
 
+procedure g_Game_Announce_BodyKill(SpawnerUID: Word);
+var
+  a: Integer;
+begin
+  case gAnnouncer of
+    ANNOUNCE_NONE:
+      Exit;
+    ANNOUNCE_ME,
+    ANNOUNCE_MEPLUS:
+      if not g_Game_IsWatchedPlayer(SpawnerUID) then
+        Exit;
+  end;
+  for a := 0 to 2 do
+    if hahasnd[a].IsPlaying() then
+      Exit;
+
+  hahasnd[Random(3)].Play();
+end;
+
 procedure g_Game_StartVote(Command, Initiator: string);
 var
   Need: Integer;
@@ -7756,4 +7887,6 @@ begin
 
   conRegVar('r_smallmap_align_h', @r_smallmap_h, 'halign: 0: left; 1: center; 2: right', 'horizontal aligning of small maps');
   conRegVar('r_smallmap_align_v', @r_smallmap_v, 'valign: 0: top; 1: center; 2: bottom', 'vertial aligning of small maps');
+
+  conRegVar('r_showfps', @gShowFPS, 'draw fps counter', 'draw fps counter');
 end.