DEADSOFTWARE

Player: Add sounds for CTF game
[d2df-sdl.git] / src / game / g_game.pas
index 8e7174f31b8fd4cceea472c5fa66695c498cd220..12c9824f0ba05c9617745d5b57a47f9bfd49f12b 100644 (file)
@@ -22,7 +22,8 @@ uses
   SysUtils, Classes,
   MAPDEF,
   g_basic, g_player, e_graphics, g_res_downloader,
-  g_sound, g_gui, utils, md5, mempool, xprofiler;
+  g_sound, g_gui, utils, md5, mempool, xprofiler,
+  g_touch;
 
 type
   TGameSettings = record
@@ -123,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();
@@ -136,7 +138,7 @@ procedure GameCheats(P: SSArray);
 procedure DebugCommands(P: SSArray);
 procedure g_Game_Process_Params;
 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
-procedure g_Game_StepLoading();
+procedure g_Game_StepLoading(Value: Integer = -1);
 procedure g_Game_ClearLoading();
 procedure g_Game_SetDebugMode();
 procedure DrawLoadingStat();
@@ -200,6 +202,7 @@ const
   DE_GLOBEVENT = 0;
   DE_BFGHIT    = 1;
   DE_KILLCOMBO = 2;
+  DE_BODYKILL  = 3;
 
   ANNOUNCE_NONE   = 0;
   ANNOUNCE_ME     = 1;
@@ -233,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;
@@ -262,6 +271,10 @@ var
   gSpectViewTwo: Boolean = False;
   gSpectPID1: Integer = -1;
   gSpectPID2: Integer = -1;
+  gSpectAuto: Boolean = False;
+  gSpectAutoNext: LongWord;
+  gSpectAutoStepX: Integer;
+  gSpectAutoStepY: Integer;
   gMusic: TMusic = nil;
   gLoadGameMode: Boolean;
   gCheats: Boolean = False;
@@ -314,6 +327,8 @@ var
   gChatSounds: Array of TChatSound;
 
   g_dbg_ignore_bounds: Boolean = false;
+  r_smallmap_h: Integer = 0; // 0: left; 1: center; 2: right
+  r_smallmap_v: Integer = 2; // 0: top; 1: center; 2: bottom
 
   // move button values:
   // bits 0-1: l/r state:
@@ -351,13 +366,17 @@ function gPause (): Boolean; inline;
 implementation
 
 uses
+{$INCLUDE ../nogl/noGLuses.inc}
+{$IFDEF ENABLE_HOLMES}
+  g_holmes,
+{$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_triggers, g_monsters, e_sound, CONFIG,
   g_language, g_net,
-  ENet, e_msg, g_netmsg, g_netmaster, GL, GLExt,
-  sfs, wadreader, g_holmes;
+  ENet, e_msg, g_netmsg, g_netmaster,
+  sfs, wadreader;
 
 
 var
@@ -556,6 +575,7 @@ var
   EndingGameCounter: Byte = 0;
   MessageText: String;
   MessageTime: Word;
+  MessageLineLength: Integer = 80;
   MapList: SSArray = nil;
   MapIndex: Integer = -1;
   MegaWAD: record
@@ -923,6 +943,7 @@ begin
               end
               else
                 slWaitStr := _lc[I_NET_SLIST_ERROR];
+              g_Serverlist_GenerateTable(slCurrent, slTable);
             end;
 
             g_Game_ExecuteEvent('ongameend');
@@ -1006,6 +1027,7 @@ begin
             gMusic.SetByName('MUSIC_INTERMUS');
             gMusic.Play();
             gState := STATE_INTERSINGLE;
+            e_UnpressAllKeys();
 
             g_Game_ExecuteEvent('oninter');
           end
@@ -1426,6 +1448,59 @@ begin
     Result := ids[(Length(ids) - 1 + idx) mod Length(ids)];
 end;
 
+function GetActivePlayerID_Random(Skip: Integer = -1): Integer;
+var
+  a, idx: Integer;
+  ids: Array of Word;
+begin
+  Result := -1;
+  if gPlayers = nil then
+    Exit;
+  SetLength(ids, 0);
+  idx := -1;
+  for a := Low(gPlayers) to High(gPlayers) do
+    if IsActivePlayer(gPlayers[a]) then
+    begin
+      SetLength(ids, Length(ids) + 1);
+      ids[High(ids)] := gPlayers[a].UID;
+      if gPlayers[a].UID = Skip then
+        idx := High(ids);
+    end;
+  if Length(ids) = 0 then
+    Exit;
+  if Length(ids) = 1 then
+  begin
+    Result := ids[0];
+    Exit;
+  end;
+  Result := ids[Random(Length(ids))];
+  a := 10;
+  while (idx <> -1) and (Result = Skip) and (a > 0) do
+  begin
+    Result := ids[Random(Length(ids))];
+    Dec(a);
+  end;
+end;
+
+function GetRandomSpectMode(Current: Byte): Byte;
+label
+  retry;
+begin
+  Result := Current;
+retry:
+  case Random(7) of
+    0: Result := SPECT_STATS;
+    1: Result := SPECT_MAPVIEW;
+    2: Result := SPECT_MAPVIEW;
+    3: Result := SPECT_PLAYERS;
+    4: Result := SPECT_PLAYERS;
+    5: Result := SPECT_PLAYERS;
+    6: Result := SPECT_PLAYERS;
+  end;
+  if (Current in [SPECT_STATS, SPECT_MAPVIEW]) and (Current = Result) then
+    goto retry;
+end;
+
 function isKeyPressed (key1: Word; key2: Word): Boolean;
 begin
   if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end;
@@ -1589,7 +1664,10 @@ begin
         if (not g_Game_IsClient) and
         (
           (
-            (e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE))
+            (
+              e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or
+              e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN)
+            )
             and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
             and (g_ActiveWindow = nil)
           )
@@ -1653,12 +1731,14 @@ begin
 
                 gMusic.Play();
                 gState := STATE_INTERCUSTOM;
+                e_UnpressAllKeys();
               end
             else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
               begin
                 gMusic.SetByName('MUSIC_INTERMUS');
                 gMusic.Play();
                 gState := STATE_INTERSINGLE;
+                e_UnpressAllKeys();
               end;
             g_Game_ExecuteEvent('oninter');
           end
@@ -1676,7 +1756,7 @@ begin
       end;
 
     STATE_SLIST:
-        g_Serverlist_Control(slCurrent);
+        g_Serverlist_Control(slCurrent, slTable);
   end;
 
   if g_Game_IsNet then
@@ -1685,23 +1765,25 @@ begin
       begin
         if g_ActiveWindow = nil then
         begin
-          if e_KeyPressed(gGameControls.GameControls.Chat) then
+          if e_KeyPressed(gGameControls.GameControls.Chat) or e_KeyPressed(VK_CHAT) then
             g_Console_Chat_Switch(False)
-          else if (e_KeyPressed(gGameControls.GameControls.TeamChat)) and
+          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)) then
+             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);
+                  (e_KeyPressed(gGameControls.GameControls.Stat) or e_KeyPressed(VK_STATUS));
 
 // Èãðà èäåò:
   if gGameOn and not gPause and (gState <> STATE_FOLD) then
@@ -1792,7 +1874,8 @@ begin
     begin
       if not gSpectKeyPress then
       begin
-        if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2) then
+        if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)
+           and (not gSpectAuto) then
         begin
           // switch spect mode
           case gSpectMode of
@@ -1803,7 +1886,8 @@ begin
           end;
           gSpectKeyPress := True;
         end;
-        if gSpectMode = SPECT_MAPVIEW then
+        if (gSpectMode = SPECT_MAPVIEW)
+           and (not gSpectAuto) then
         begin
           if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
             gSpectX := Max(gSpectX - gSpectStep, 0);
@@ -1826,7 +1910,8 @@ begin
             gSpectKeyPress := True;
           end;
         end;
-        if gSpectMode = SPECT_PLAYERS then
+        if (gSpectMode = SPECT_PLAYERS)
+           and (not gSpectAuto) then
         begin
           if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
           begin
@@ -1865,9 +1950,27 @@ begin
             gSpectKeyPress := True;
           end;
         end;
+        if isKeyPressed(gGameControls.P1Control.KeyFire, gGameControls.P1Control.KeyFire2) then
+        begin
+          if (gSpectMode = SPECT_STATS) and (not gSpectAuto) then
+          begin
+            gSpectAuto := True;
+            gSpectAutoNext := 0;
+            gSpectViewTwo := False;
+            gSpectKeyPress := True;
+          end
+          else
+            if gSpectAuto then
+            begin
+              gSpectMode := SPECT_STATS;
+              gSpectAuto := False;
+              gSpectKeyPress := True;
+            end;
+        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
@@ -1875,6 +1978,54 @@ begin
            (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
            (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
           gSpectKeyPress := False;
+
+      if gSpectAuto then
+      begin
+        if gSpectMode = SPECT_MAPVIEW then
+        begin
+          i := Min(Max(gSpectX + gSpectAutoStepX, 0), gMapInfo.Width - gScreenWidth);
+          if i = gSpectX then
+            gSpectAutoNext := gTime
+          else
+            gSpectX := i;
+          i := Min(Max(gSpectY + gSpectAutoStepY, 0), gMapInfo.Height - gScreenHeight);
+          if i = gSpectY then
+            gSpectAutoNext := gTime
+          else
+            gSpectY := i;
+        end;
+        if gSpectAutoNext <= gTime then
+        begin
+          if gSpectAutoNext > 0 then
+          begin
+            gSpectMode := GetRandomSpectMode(gSpectMode);
+            case gSpectMode of
+              SPECT_MAPVIEW:
+              begin
+                gSpectX := Random(gMapInfo.Width - gScreenWidth);
+                gSpectY := Random(gMapInfo.Height - gScreenHeight);
+                gSpectAutoStepX := Random(9) - 4;
+                gSpectAutoStepY := Random(9) - 4;
+                if ((gSpectX < 800) and (gSpectAutoStepX < 0)) or
+                   ((gSpectX > gMapInfo.Width - gScreenWidth - 800) and (gSpectAutoStepX > 0)) then
+                  gSpectAutoStepX := gSpectAutoStepX * -1;
+                if ((gSpectY < 800) and (gSpectAutoStepY < 0)) or
+                   ((gSpectY > gMapInfo.Height - gScreenHeight - 800) and (gSpectAutoStepY > 0)) then
+                  gSpectAutoStepY := gSpectAutoStepY * -1;
+              end;
+              SPECT_PLAYERS:
+              begin
+                gSpectPID1 := GetActivePlayerID_Random(gSpectPID1);
+              end;
+            end;
+          end;
+          case gSpectMode of
+            SPECT_STATS:   gSpectAutoNext := gTime + (Random(3) + 5) * 1000;
+            SPECT_MAPVIEW: gSpectAutoNext := gTime + (Random(4) + 7) * 1000;
+            SPECT_PLAYERS: gSpectAutoNext := gTime + (Random(7) + 8) * 1000;
+          end;
+        end;
+      end;
     end;
 
   // Îáíîâëÿåì âñå îñòàëüíîå:
@@ -1986,6 +2137,7 @@ begin
       e_WriteLog('Changing resolution', TMsgType.Notify);
       g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
       gResolutionChange := False;
+      g_ActiveWindow := nil;
     end;
 
   // Íóæíî ñìåíèòü ÿçûê:
@@ -2000,7 +2152,7 @@ begin
   end;
 
 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
-  if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) then
+  if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) or e_KeyPressed(VK_PRINTSCR) then
     if (GetTimer()-LastScreenShot) > 200000 div 1000 then
     begin
       g_TakeScreenShot();
@@ -2040,6 +2192,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;
@@ -2142,6 +2297,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;
@@ -2166,7 +2322,12 @@ begin
   end;
 
   g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
-  g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD+':TEXTURES\PUNCH', 64, 64, 4, False);
+  g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD+':WEAPONS\PUNCH', 64, 64, 4, False);
+  g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_UP', GameWAD+':WEAPONS\PUNCH_UP', 64, 64, 4, False);
+  g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_DN', GameWAD+':WEAPONS\PUNCH_DN', 64, 64, 4, False);
+  g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK', GameWAD+':WEAPONS\PUNCHB', 64, 64, 4, False);
+  g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_UP', GameWAD+':WEAPONS\PUNCHB_UP', 64, 64, 4, False);
+  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_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
@@ -2186,6 +2347,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();
@@ -2207,6 +2379,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);
@@ -2244,6 +2442,11 @@ begin
   g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
   g_Frames_DeleteByName('FRAMES_TELEPORT');
   g_Frames_DeleteByName('FRAMES_PUNCH');
+  g_Frames_DeleteByName('FRAMES_PUNCH_UP');
+  g_Frames_DeleteByName('FRAMES_PUNCH_DN');
+  g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK');
+  g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_UP');
+  g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_DN');
   g_Sound_Delete('SOUND_GAME_TELEPORT');
   g_Sound_Delete('SOUND_GAME_NOTELEPORT');
   g_Sound_Delete('SOUND_GAME_DOOROPEN');
@@ -2275,6 +2478,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;
@@ -2293,7 +2522,7 @@ begin
 
   g_ProcessMessages();
 
-  if e_KeyPressed(IK_TAB) then
+  if e_KeyPressed(IK_TAB) or e_KeyPressed(VK_STATUS) then
   begin
     if not gStatsPressed then
     begin
@@ -2853,10 +3082,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;
@@ -3000,7 +3229,6 @@ begin
    *     glBlendFunc(GL_DST_ALPHA, GL_ONE);
    *     draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
    *)
-
   wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
   if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
 
@@ -3106,7 +3334,7 @@ var
     tagmask: Integer;
     pan: TPanel;
   begin
-    profileFrameDraw.sectionBegin(profname);
+    if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
     if gdbg_map_use_accel_render then
     begin
       tagmask := panelTypeToTag(panType);
@@ -3122,31 +3350,31 @@ var
     begin
       if doDraw then g_Map_DrawPanels(panType, hasAmbient, ambColor);
     end;
-    profileFrameDraw.sectionEnd();
+    if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
   end;
 
   procedure drawOther (profname: AnsiString; cb: TDrawCB);
   begin
-    profileFrameDraw.sectionBegin(profname);
+    if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
     if assigned(cb) then cb();
-    profileFrameDraw.sectionEnd();
+    if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
   end;
 
 begin
-  profileFrameDraw.sectionBegin('total');
+  if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('total');
 
   // our accelerated renderer will collect all panels to gDrawPanelList
   // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
-  profileFrameDraw.sectionBegin('collect');
+  if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('collect');
   if gdbg_map_use_accel_render then
   begin
     g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
   end;
-  profileFrameDraw.sectionEnd();
+  if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
 
-  profileFrameDraw.sectionBegin('skyback');
+  if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('skyback');
   g_Map_DrawBack(backXOfs, backYOfs);
-  profileFrameDraw.sectionEnd();
+  if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
 
   if setTransMatrix then
   begin
@@ -3203,7 +3431,7 @@ begin
     g_Player_DrawHealth();
   end;
 
-  profileFrameDraw.mainEnd(); // map rendering
+  if (profileFrameDraw <> nil) then profileFrameDraw.mainEnd(); // map rendering
 end;
 
 
@@ -3243,7 +3471,7 @@ begin
   end;
 
   if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
-  profileFrameDraw.mainBegin(g_profile_frame_draw);
+  if (profileFrameDraw <> nil) then profileFrameDraw.mainBegin(g_profile_frame_draw);
 
   gPlayerDrawn := p;
 
@@ -3330,6 +3558,7 @@ begin
   //conwritefln('OLD: (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
   fixViewportForScale();
   //conwritefln('     (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
+
   if (g_dbg_scale <> 1.0) and (not g_dbg_ignore_bounds) then
   begin
     if (sX+sWidth > gMapInfo.Width) then sX := gMapInfo.Width-sWidth;
@@ -3340,19 +3569,53 @@ begin
     if (gBackSize.X <= gPlayerScreenSize.X) or (gMapInfo.Width <= sWidth) then c := 0 else c := trunc((gBackSize.X-gPlayerScreenSize.X)*sX/(gMapInfo.Width-sWidth));
     if (gBackSize.Y <= gPlayerScreenSize.Y) or (gMapInfo.Height <= sHeight) then d := 0 else d := trunc((gBackSize.Y-gPlayerScreenSize.Y)*sY/(gMapInfo.Height-sHeight));
   end;
+
+  //r_smallmap_h: 0: left; 1: center; 2: right
+  //r_smallmap_v: 0: top; 1: center; 2: bottom
+  // horiz small map?
+  if (gMapInfo.Width = sWidth) then
+  begin
+    sX := 0;
+  end
+  else if (gMapInfo.Width < sWidth) then
+  begin
+    case r_smallmap_h of
+      1: sX := -((sWidth-gMapInfo.Width) div 2); // center
+      2: sX := -(sWidth-gMapInfo.Width); // right
+      else sX := 0; // left
+    end;
+  end;
+  // vert small map?
+  if (gMapInfo.Height = sHeight) then
+  begin
+    sY := 0;
+  end
+  else if (gMapInfo.Height < sHeight) then
+  begin
+    case r_smallmap_v of
+      1: sY := -((sHeight-gMapInfo.Height) div 2); // center
+      2: sY := -(sHeight-gMapInfo.Height); // bottom
+      else sY := 0; // top
+    end;
+  end;
+
   p.viewPortX := sX;
   p.viewPortY := sY;
   p.viewPortW := sWidth;
   p.viewPortH := sHeight;
 
+{$IFDEF ENABLE_HOLMES}
   if (p = gPlayer1) then
   begin
     g_Holmes_plrViewPos(sX, sY);
     g_Holmes_plrViewSize(sWidth, sHeight);
   end;
+{$ENDIF}
 
   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,
@@ -3391,9 +3654,9 @@ var
   px: Integer = -1;
   py: Integer = -1;
 begin
-  if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
-  if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
-  if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
+  if g_profile_frame_draw and (profileFrameDraw <> nil) then px := px-drawProfiles(px, py, profileFrameDraw);
+  if g_profile_collision and (profMapCollision <> nil) then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
+  if g_profile_los and (profMonsLOS <> nil) then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
 end;
 
 procedure g_Game_Draw();
@@ -3539,8 +3802,10 @@ begin
         e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
     end;
 
+{$IFDEF ENABLE_HOLMES}
     // draw inspector
     if (g_holmes_enabled) then g_Holmes_Draw();
+{$ENDIF}
 
     if MessageText <> '' then
     begin
@@ -3557,7 +3822,7 @@ begin
 
     if IsDrawStat or (gSpectMode = 1) then DrawStat();
 
-    if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
+    if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) and (not gSpectAuto) then
     begin
     // Draw spectator GUI
       ww := 0;
@@ -3572,6 +3837,11 @@ begin
           e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
       end;
       e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
+      if gSpectMode = SPECT_STATS then
+      begin
+        e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2)*2, 'Autoview', gStdFont, 255, 255, 255, 1);
+        e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2), '< fire >', gStdFont, 255, 255, 255, 1);
+      end;
       if gSpectMode = SPECT_MAPVIEW then
       begin
         e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
@@ -3718,7 +3988,7 @@ begin
         //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
         e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
       end;
-      g_Serverlist_Draw(slCurrent);
+      g_Serverlist_Draw(slCurrent, slTable);
     end;
   end;
 
@@ -3752,7 +4022,11 @@ begin
 
   if gGameOn then drawProfilers();
 
+{$IFDEF ENABLE_HOLMES}
   g_Holmes_DrawUI();
+{$ENDIF}
+
+  g_Touch_Draw;
 end;
 
 procedure g_Game_Quit();
@@ -3764,7 +4038,7 @@ begin
   g_PlayerModel_FreeData();
   g_Texture_DeleteAll();
   g_Frames_DeleteAll();
-  g_Menu_Free();
+  //g_Menu_Free(); //k8: this segfaults after resolution change; who cares?
 
   if NetInitDone then g_Net_Free;
 
@@ -4391,7 +4665,7 @@ begin
 
     ProcessLoading(true);
 
-    if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
+    if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) or e_KeyPressed(VK_ESCAPE) then
     begin
       State := 0;
       break;
@@ -4480,7 +4754,7 @@ begin
       gWADHash := MD5File(MapsDir + NewWAD);
       g_Game_LoadWAD(NewWAD);
     end else
-      // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
+      // hash received in MC_RECV_GameEvent -> NET_EV_MAPSTART
       g_Game_ClientWAD(NewWAD, gWADHash);
   end else
     ResName := Map;
@@ -6974,7 +7248,8 @@ end;
 
 procedure g_Game_Message(Msg: string; Time: Word);
 begin
-  MessageText := b_Text_Format(Msg);
+  MessageLineLength := (gScreenWidth - 204) div e_CharFont_GetMaxWidth(gMenuFont);
+  MessageText := b_Text_Wrap(b_Text_Format(Msg), MessageLineLength);
   MessageTime := Time;
 end;
 
@@ -7115,6 +7390,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;
@@ -7264,13 +7558,18 @@ begin
   ProcessLoading(true);
 end;
 
-procedure g_Game_StepLoading();
+procedure g_Game_StepLoading(Value: Integer = -1);
 begin
   with LoadingStat do
   begin
-    Inc(CurValue);
-    Inc(ShowCount);
-    if (ShowCount > LOADING_SHOW_STEP) then
+    if Value = -1 then
+    begin
+      Inc(CurValue);
+      Inc(ShowCount);
+    end
+    else
+      CurValue := Value;
+    if (ShowCount > LOADING_SHOW_STEP) or (Value > -1) then
     begin
       ShowCount := 0;
       ProcessLoading();
@@ -7540,7 +7839,9 @@ begin
   conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
   conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
 
+{$IFDEF ENABLE_HOLMES}
   conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
+{$ENDIF}
 
   conRegVar('dbg_ignore_level_bounds', @g_dbg_ignore_bounds, 'ignore level bounds', '',  false);
 
@@ -7548,4 +7849,7 @@ begin
 
   conRegVar('light_enabled', @gwin_k8_enable_light_experiments, 'enable/disable dynamic lighting', 'lighting');
   conRegVar('light_player_halo', @g_playerLight, 'enable/disable player halo', 'player light halo');
+
+  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');
 end.