DEADSOFTWARE

render: use only r_render to access render
[d2df-sdl.git] / src / game / g_game.pas
index 6fc6035b7944ad839a09033261edef58ba675c59..fcd73a7bb8147add3027353f37401c4ea3791c8d 100644 (file)
@@ -20,7 +20,7 @@ interface
 uses
   SysUtils, Classes,
   MAPDEF,
-  g_base, g_basic, g_player, r_graphics, g_res_downloader,
+  g_base, g_basic, g_player, g_res_downloader,
   g_sound, g_gui, utils, md5, mempool, xprofiler,
   g_touch, g_weapons;
 
@@ -86,8 +86,6 @@ procedure g_Game_FreeData();
 procedure g_Game_Update();
 procedure g_Game_PreUpdate();
 procedure g_Game_Quit();
-procedure g_Game_SetupScreenSize();
-procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
 function  g_Game_ModeToText(Mode: Byte): string;
 function  g_Game_TextToMode(Mode: string): Byte;
 procedure g_Game_ExecuteEvent(Name: String);
@@ -127,7 +125,9 @@ 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(Filename: string = '');
+{$IFNDEF HEADLESS}
+  procedure g_TakeScreenShot(Filename: string = '');
+{$ENDIF}
 procedure g_FatalError(Text: String);
 procedure g_SimpleError(Text: String);
 function  g_Game_IsTestMap(): Boolean;
@@ -148,6 +148,9 @@ function IsActivePlayer(p: TPlayer): Boolean;
 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
 procedure SortGameStat(var stat: TPlayerStatArray);
 
+procedure KeyPress (K: Word);
+procedure CharPress (C: AnsiChar);
+
 { procedure SetWinPause(Enable: Boolean); }
 
 const
@@ -233,7 +236,6 @@ const
   STATFILE_VERSION = $03;
 
 var
-  gStdFont: DWORD;
   gGameSettings: TGameSettings;
   gPlayer1Settings: TPlayerSettings;
   gPlayer2Settings: TPlayerSettings;
@@ -372,6 +374,7 @@ var
   g_rlayer_water: Boolean = true;
   g_rlayer_fore: Boolean = true;
 
+  wNeedTimeReset: Boolean = false;
 
 procedure g_ResetDynlights ();
 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
@@ -420,7 +423,6 @@ function gPause (): Boolean; inline;
     StatShotDone: Boolean;
     StatFilename: string = ''; // used by stat screenshot to save with the same name as the csv
     SingleStat: TEndSingleGameStat;
-    hasPBarGfx: Boolean;
     LoadingStat: TLoadingStat;
     MessageText: String;
     IsDrawStat: Boolean;
@@ -429,21 +431,370 @@ function gPause (): Boolean; inline;
     g_playerLight: Boolean;
     g_dynLights: array of TDynLight = nil;
     g_dynLightCount: Integer = 0;
+    EndPicPath: AnsiString; // full path, used by render
 
 implementation
 
 uses
-{$IFDEF ENABLE_HOLMES}
-  g_holmes,
-{$ENDIF}
-  e_res, g_textures, g_window, g_menu,
-  e_input, e_log, g_console, r_console, g_items, g_map, g_panel,
+  {$IFDEF ENABLE_HOLMES}
+    g_holmes,
+  {$ENDIF}
+  {$IFNDEF HEADLESS}
+    r_render,
+  {$ENDIF}
+  e_res, g_window, g_menu,
+  e_input, e_log, g_console, g_items, g_map, g_panel,
   g_playermodel, g_gfx, g_options, Math,
   g_triggers, g_monsters, e_sound, CONFIG,
-  g_language, g_net, g_main, g_phys,
+  g_language, g_net, g_phys,
   ENet, e_msg, g_netmsg, g_netmaster,
   sfs, wadreader, g_system, r_playermodel;
 
+  var
+    charbuff: packed array [0..15] of AnsiChar = (
+      ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
+    );
+
+function Translit (const S: AnsiString): AnsiString;
+var
+  i: Integer;
+begin
+  Result := S;
+  for i := 1 to Length(Result) do
+  begin
+    case Result[i] of
+      #$C9: Result[i] := 'Q';
+      #$D6: Result[i] := 'W';
+      #$D3: Result[i] := 'E';
+      #$CA: Result[i] := 'R';
+      #$C5: Result[i] := 'T';
+      #$CD: Result[i] := 'Y';
+      #$C3: Result[i] := 'U';
+      #$D8: Result[i] := 'I';
+      #$D9: Result[i] := 'O';
+      #$C7: Result[i] := 'P';
+      #$D5: Result[i] := '['; //Chr(219);
+      #$DA: Result[i] := ']'; //Chr(221);
+      #$D4: Result[i] := 'A';
+      #$DB: Result[i] := 'S';
+      #$C2: Result[i] := 'D';
+      #$C0: Result[i] := 'F';
+      #$CF: Result[i] := 'G';
+      #$D0: Result[i] := 'H';
+      #$CE: Result[i] := 'J';
+      #$CB: Result[i] := 'K';
+      #$C4: Result[i] := 'L';
+      #$C6: Result[i] := ';'; //Chr(186);
+      #$DD: Result[i] := #39; //Chr(222);
+      #$DF: Result[i] := 'Z';
+      #$D7: Result[i] := 'X';
+      #$D1: Result[i] := 'C';
+      #$CC: Result[i] := 'V';
+      #$C8: Result[i] := 'B';
+      #$D2: Result[i] := 'N';
+      #$DC: Result[i] := 'M';
+      #$C1: Result[i] := ','; //Chr(188);
+      #$DE: Result[i] := '.'; //Chr(190);
+    end;
+  end;
+end;
+
+
+function CheckCheat (ct: TStrings_Locale; eofs: Integer=0): Boolean;
+var
+  ls1, ls2: string;
+begin
+  ls1 :=          CheatEng[ct];
+  ls2 := Translit(CheatRus[ct]);
+  if length(ls1) = 0 then ls1 := '~';
+  if length(ls2) = 0 then ls2 := '~';
+  result :=
+    (Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1)) = ls1) or
+    (Translit(Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1))) = ls1) or
+    (Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2)) = ls2) or
+    (Translit(Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2))) = ls2);
+  {
+  if ct = I_GAME_CHEAT_JETPACK then
+  begin
+    e_WriteLog('ls1: ['+ls1+']', MSG_NOTIFY);
+    e_WriteLog('ls2: ['+ls2+']', MSG_NOTIFY);
+    e_WriteLog('bf0: ['+Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1))+']', MSG_NOTIFY);
+    e_WriteLog('bf1: ['+Translit(Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1)))+']', MSG_NOTIFY);
+    e_WriteLog('bf2: ['+Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2))+']', MSG_NOTIFY);
+    e_WriteLog('bf3: ['+Translit(Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2)))+']', MSG_NOTIFY);
+  end;
+  }
+end;
+
+procedure Cheat ();
+const
+  CHEAT_DAMAGE = 500;
+label
+  Cheated;
+var
+  s, s2: string;
+  c: ShortString;
+  a: Integer;
+begin
+  {
+  if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
+    (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode))
+    or g_Game_IsNet then Exit;
+  }
+  if not gGameOn then exit;
+  if not conIsCheatsEnabled then exit;
+
+  s := 'SOUND_GAME_RADIO';
+
+  //
+  if CheckCheat(I_GAME_CHEAT_GODMODE) then
+  begin
+    if gPlayer1 <> nil then gPlayer1.GodMode := not gPlayer1.GodMode;
+    if gPlayer2 <> nil then gPlayer2.GodMode := not gPlayer2.GodMode;
+    goto Cheated;
+  end;
+  // RAMBO
+  if CheckCheat(I_GAME_CHEAT_WEAPONS) then
+  begin
+    if gPlayer1 <> nil then gPlayer1.AllRulez(False);
+    if gPlayer2 <> nil then gPlayer2.AllRulez(False);
+    goto Cheated;
+  end;
+  // TANK
+  if CheckCheat(I_GAME_CHEAT_HEALTH) then
+  begin
+    if gPlayer1 <> nil then gPlayer1.AllRulez(True);
+    if gPlayer2 <> nil then gPlayer2.AllRulez(True);
+    goto Cheated;
+  end;
+  // IDDQD
+  if CheckCheat(I_GAME_CHEAT_DEATH) then
+  begin
+    if gPlayer1 <> nil then gPlayer1.Damage(CHEAT_DAMAGE, 0, 0, 0, HIT_TRAP);
+    if gPlayer2 <> nil then gPlayer2.Damage(CHEAT_DAMAGE, 0, 0, 0, HIT_TRAP);
+    s := 'SOUND_MONSTER_HAHA';
+    goto Cheated;
+  end;
+  //
+  if CheckCheat(I_GAME_CHEAT_DOORS) then
+  begin
+    g_Triggers_OpenAll();
+    goto Cheated;
+  end;
+  // GOODBYE
+  if CheckCheat(I_GAME_CHEAT_NEXTMAP) then
+  begin
+    if gTriggers <> nil then
+      for a := 0 to High(gTriggers) do
+        if gTriggers[a].TriggerType = TRIGGER_EXIT then
+        begin
+          gExitByTrigger := True;
+          //g_Game_ExitLevel(gTriggers[a].Data.MapName);
+          g_Game_ExitLevel(gTriggers[a].tgcMap);
+          Break;
+        end;
+    goto Cheated;
+  end;
+  //
+  s2 := Copy(charbuff, 15, 2);
+  if CheckCheat(I_GAME_CHEAT_CHANGEMAP, 2) and (s2[1] >= '0') and (s2[1] <= '9') and (s2[2] >= '0') and (s2[2] <= '9') then
+  begin
+    if g_Map_Exist(gGameSettings.WAD + ':\MAP' + s2) then
+    begin
+      c := 'MAP' + s2;
+      g_Game_ExitLevel(c);
+    end;
+    goto Cheated;
+  end;
+  //
+  if CheckCheat(I_GAME_CHEAT_FLY) then
+  begin
+    gFly := not gFly;
+    goto Cheated;
+  end;
+  // BULLFROG
+  if CheckCheat(I_GAME_CHEAT_JUMPS) then
+  begin
+    VEL_JUMP := 30-VEL_JUMP;
+    goto Cheated;
+  end;
+  // FORMULA1
+  if CheckCheat(I_GAME_CHEAT_SPEED) then
+  begin
+    MAX_RUNVEL := 32-MAX_RUNVEL;
+    goto Cheated;
+  end;
+  // CONDOM
+  if CheckCheat(I_GAME_CHEAT_SUIT) then
+  begin
+    if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_SUIT);
+    if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_SUIT);
+    goto Cheated;
+  end;
+  //
+  if CheckCheat(I_GAME_CHEAT_AIR) then
+  begin
+    if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_OXYGEN);
+    if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_OXYGEN);
+    goto Cheated;
+  end;
+  // PURELOVE
+  if CheckCheat(I_GAME_CHEAT_BERSERK) then
+  begin
+    if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_MEDKIT_BLACK);
+    if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_MEDKIT_BLACK);
+    goto Cheated;
+  end;
+  //
+  if CheckCheat(I_GAME_CHEAT_JETPACK) then
+  begin
+    if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_JETPACK);
+    if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_JETPACK);
+    goto Cheated;
+  end;
+  // CASPER
+  if CheckCheat(I_GAME_CHEAT_NOCLIP) then
+  begin
+    if gPlayer1 <> nil then gPlayer1.SwitchNoClip;
+    if gPlayer2 <> nil then gPlayer2.SwitchNoClip;
+    goto Cheated;
+  end;
+  //
+  if CheckCheat(I_GAME_CHEAT_NOTARGET) then
+  begin
+    if gPlayer1 <> nil then gPlayer1.NoTarget := not gPlayer1.NoTarget;
+    if gPlayer2 <> nil then gPlayer2.NoTarget := not gPlayer2.NoTarget;
+    goto Cheated;
+  end;
+  // INFERNO
+  if CheckCheat(I_GAME_CHEAT_NORELOAD) then
+  begin
+    if gPlayer1 <> nil then gPlayer1.NoReload := not gPlayer1.NoReload;
+    if gPlayer2 <> nil then gPlayer2.NoReload := not gPlayer2.NoReload;
+    goto Cheated;
+  end;
+  if CheckCheat(I_GAME_CHEAT_AIMLINE) then
+  begin
+    gAimLine := not gAimLine;
+    goto Cheated;
+  end;
+  if CheckCheat(I_GAME_CHEAT_AUTOMAP) then
+  begin
+    gShowMap := not gShowMap;
+    goto Cheated;
+  end;
+  Exit;
+
+Cheated:
+  g_Sound_PlayEx(s);
+end;
+
+
+procedure KeyPress (K: Word);
+{$IFNDEF HEADLESS}
+var
+  Msg: g_gui.TMessage;
+{$ENDIF}
+begin
+{$IFNDEF HEADLESS}
+  case K of
+    VK_ESCAPE: // <Esc>:
+      begin
+        if (g_ActiveWindow <> nil) then
+        begin
+          Msg.Msg := WM_KEYDOWN;
+          Msg.WParam := VK_ESCAPE;
+          g_ActiveWindow.OnMessage(Msg);
+          if (not g_Game_IsNet) and (g_ActiveWindow = nil) then g_Game_Pause(false); //Fn loves to do this
+        end
+        else if (gState <> STATE_FOLD) then
+        begin
+          if gGameOn or (gState = STATE_INTERSINGLE) or (gState = STATE_INTERCUSTOM) then
+          begin
+            g_Game_InGameMenu(True);
+          end
+          else if (gExit = 0) and (gState <> STATE_SLIST) then
+          begin
+            if (gState <> STATE_MENU) then
+            begin
+              if (NetMode <> NET_NONE) then
+              begin
+                g_Game_StopAllSounds(True);
+                g_Game_Free;
+                gState := STATE_MENU;
+                Exit;
+              end;
+            end;
+            g_GUI_ShowWindow('MainMenu');
+            g_Sound_PlayEx('MENU_OPEN');
+          end;
+        end;
+      end;
+
+    IK_F2, IK_F3, IK_F4, IK_F5, IK_F6, IK_F7, IK_F10:
+      begin // <F2> .. <F6> � <F12>
+        if gGameOn and (not gConsoleShow) and (not gChatShow) then
+        begin
+          while (g_ActiveWindow <> nil) do g_GUI_HideWindow(False);
+          if (not g_Game_IsNet) then g_Game_Pause(True);
+          case K of
+            IK_F2: g_Menu_Show_SaveMenu();
+            IK_F3: g_Menu_Show_LoadMenu();
+            IK_F4: g_Menu_Show_GameSetGame();
+            IK_F5: g_Menu_Show_OptionsVideo();
+            IK_F6: g_Menu_Show_OptionsSound();
+            IK_F7: g_Menu_Show_EndGameMenu();
+            IK_F10: g_Menu_Show_QuitGameMenu();
+          end;
+        end;
+      end;
+
+    else
+      begin
+        gJustChatted := False;
+        if gConsoleShow or gChatShow then
+        begin
+          g_Console_Control(K);
+        end
+        else if (g_ActiveWindow <> nil) then
+        begin
+          Msg.Msg := WM_KEYDOWN;
+          Msg.WParam := K;
+          g_ActiveWindow.OnMessage(Msg);
+        end
+        else if (gState = STATE_MENU) then
+        begin
+          g_GUI_ShowWindow('MainMenu');
+          g_Sound_PlayEx('MENU_OPEN');
+        end;
+      end;
+  end;
+{$ENDIF}
+end;
+
+procedure CharPress (C: AnsiChar);
+var
+  Msg: g_gui.TMessage;
+  a: Integer;
+begin
+  if gConsoleShow or gChatShow then
+  begin
+    g_Console_Char(C)
+  end
+  else if (g_ActiveWindow <> nil) then
+  begin
+    Msg.Msg := WM_CHAR;
+    Msg.WParam := Ord(C);
+    g_ActiveWindow.OnMessage(Msg);
+  end
+  else
+  begin
+    for a := 0 to 14 do charbuff[a] := charbuff[a+1];
+    charbuff[15] := upcase1251(C);
+    Cheat();
+  end;
+end;
+
 
 // ////////////////////////////////////////////////////////////////////////// //
 function gPause (): Boolean; inline; begin result := gPauseMain or gPauseHolmes; end;
@@ -541,7 +892,6 @@ var
   UPSTime: LongWord;
   DataLoaded: Boolean = False;
   MessageTime: Word;
-  MessageLineLength: Integer = 80;
   MapList: SSArray = nil;
   MapIndex: Integer = -1;
   InterReadyTime: Integer = -1;
@@ -550,22 +900,7 @@ var
     info: TMegaWADInfo;
     endpic: String;
     endmus: String;
-    res: record
-      text: Array of ShortString;
-      anim: Array of ShortString;
-      pic: Array of ShortString;
-      mus: Array of ShortString;
-    end;
-    triggers: Array of record
-      event: ShortString;
-      actions: Array of record
-        action, p1, p2: Integer;
-      end;
-    end;
-    cur_trigger: Integer;
-    cur_action: Integer;
   end;
-  //InterPic: String;
   InterText: record
     lines: SSArray;
     img: String;
@@ -754,37 +1089,20 @@ begin
   FreeMem(p);
 end;
 
-procedure g_Game_FreeWAD();
-var
-  a: Integer;
-begin
-  for a := 0 to High(MegaWAD.res.pic) do
-    if MegaWAD.res.pic[a] <> '' then
-      g_Texture_Delete(MegaWAD.res.pic[a]);
-
-  for a := 0 to High(MegaWAD.res.mus) do
-    if MegaWAD.res.mus[a] <> '' then
-      g_Sound_Delete(MegaWAD.res.mus[a]);
-
-  MegaWAD.res.pic := nil;
-  MegaWAD.res.text := nil;
-  MegaWAD.res.anim := nil;
-  MegaWAD.res.mus := nil;
-  MegaWAD.triggers := nil;
-
-  g_Texture_Delete('TEXTURE_endpic');
-  g_Sound_Delete('MUSIC_endmus');
-
-  ZeroMemory(@MegaWAD, SizeOf(MegaWAD));
-  gGameSettings.WAD := '';
-end;
+  procedure g_Game_FreeWAD;
+  begin
+    EndPicPath := '';
+    g_Sound_Delete('MUSIC_endmus');
+    ZeroMemory(@MegaWAD, SizeOf(MegaWAD));
+    gGameSettings.WAD := '';
+  end;
 
 procedure g_Game_LoadWAD(WAD: string);
 var
   w: TWADFile;
   cfg: TConfig;
   p: Pointer;
-  {b, }len: Integer;
+  len: Integer;
   s: AnsiString;
 begin
   g_Game_FreeWAD();
@@ -805,38 +1123,11 @@ begin
 
   cfg := TConfig.CreateMem(p, len);
 
- {b := 1;
- while True do
- begin
-  s := cfg.ReadStr('pic', 'pic'+IntToStr(b), '');
-  if s = '' then Break;
-  b := b+1;
-
-  SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1);
-  MegaWAD.res.pic[High(MegaWAD.res.pic)] := s;
-
-  g_Texture_CreateWADEx(s, s);
- end;
-
- b := 1;
- while True do
- begin
-  s := cfg.ReadStr('mus', 'mus'+IntToStr(b), '');
-  if s = '' then Break;
-  b := b+1;
-
-  SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1);
-  MegaWAD.res.mus[High(MegaWAD.res.mus)] := s;
-
-  g_Music_CreateWADEx(s, s);
- end;}
-
+  EndPicPath := '';
   MegaWAD.endpic := cfg.ReadStr('megawad', 'endpic', '');
   if MegaWAD.endpic <> '' then
-  begin
-    s := e_GetResourcePath(WadDirs, MegaWAD.endpic, WAD);
-    g_Texture_CreateWADEx('TEXTURE_endpic', s, gTextureFilter);
-  end;
+    EndPicPath := e_GetResourcePath(WadDirs, MegaWAD.endpic, WAD);
+
   MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\КОНЕЦ');
   if MegaWAD.endmus <> '' then
   begin
@@ -913,7 +1204,7 @@ begin
   gDelayedEvents[n].DENum := Num;
   gDelayedEvents[n].DEStr := Str;
   if DEType = DE_GLOBEVENT then
-    gDelayedEvents[n].Time := (sys_GetTicks() {div 1000}) + Time
+    gDelayedEvents[n].Time := (GetTickCount64() {div 1000}) + Time
   else
     gDelayedEvents[n].Time := gTime + Time;
   Result := n;
@@ -1091,12 +1382,6 @@ begin
 end;
 
 procedure g_Game_Init();
-var
-  SR: TSearchRec;
-  knownFiles: array of AnsiString = nil;
-  found: Boolean;
-  wext, s: AnsiString;
-  f: Integer;
 begin
   gExit := 0;
   gMapToDelete := '';
@@ -1105,62 +1390,11 @@ begin
   sfsGCDisable(); // temporary disable removing of temporary volumes
 
   try
-    g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE', gTextureFilter);
-    g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER', gTextureFilter);
-    g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN', gTextureFilter);
-    g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU', gTextureFilter);
-
-    LoadStdFont('STDTXT', 'STDFONT', gStdFont);
-    LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
-    LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
-
     g_Game_ClearLoading();
     g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False);
     g_Game_SetLoadingText('', 0, False);
 
-    g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False);
-    r_Console_Init;
-    g_Console_Init();
-
-    g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
-    r_PlayerModel_Initialize;
-
-    // load models from all possible wad types, in all known directories
-    // this does a loosy job (linear search, ooph!), but meh
-    for wext in wadExtensions do
-    begin
-      for f := High(ModelDirs) downto Low(ModelDirs) do
-      begin
-        if (FindFirst(ModelDirs[f]+DirectorySeparator+'*'+wext, faAnyFile, SR) = 0) then
-        begin
-          repeat
-            found := false;
-            for s in knownFiles do
-            begin
-              if (strEquCI1251(forceFilenameExt(SR.Name, ''), forceFilenameExt(ExtractFileName(s), ''))) then
-              begin
-                found := true;
-                break;
-              end;
-            end;
-            if not found then
-            begin
-              SetLength(knownFiles, length(knownFiles)+1);
-              knownFiles[High(knownFiles)] := ModelDirs[f]+DirectorySeparator+SR.Name;
-            end;
-          until (FindNext(SR) <> 0);
-        end;
-        FindClose(SR);
-      end;
-    end;
-
-    if (length(knownFiles) = 0) then raise Exception.Create('no player models found!');
-
-    if (length(knownFiles) = 1) then e_LogWriteln('1 player model found.', TMsgType.Notify) else e_LogWritefln('%d player models found.', [Integer(length(knownFiles))], TMsgType.Notify);
-    for s in knownFiles do
-    begin
-      if not g_PlayerModel_Load(s) then e_LogWritefln('Error loading model "%s"', [s], TMsgType.Warning);
-    end;
+//    g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
 
     gGameOn := false;
     gPauseMain := false;
@@ -1178,11 +1412,6 @@ begin
     g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True, True);
     g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
 
-{$IFNDEF HEADLESS}
-    g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
-    g_Menu_Init();
-{$ENDIF}
-
     gMusic := TMusic.Create();
     gMusic.SetByName('MUSIC_MENU');
     gMusic.Play();
@@ -1497,7 +1726,6 @@ begin
   // no need to, as we'll do it in event handler
 
 // Обновляем консоль (движение и сообщения):
-  r_Console_Update;
   g_Console_Update();
 
   if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
@@ -1995,8 +2223,10 @@ begin
   // Нужно сменить разрешение:
     if gResolutionChange then
     begin
-      e_WriteLog('Changing resolution', TMsgType.Notify);
-      g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
+      {$IFNDEF HEADLESS}
+        e_WriteLog('Changing resolution', TMsgType.Notify);
+        r_Render_Apply;
+      {$ENDIF}
       gResolutionChange := False;
       g_ActiveWindow := nil;
     end;
@@ -2023,7 +2253,7 @@ begin
     KeyPress(IK_F10);
   end;
 
-  Time := sys_GetTicks() {div 1000};
+  Time := GetTickCount64() {div 1000};
 
 // Обработка отложенных событий:
   if gDelayedEvents <> nil then
@@ -2128,61 +2358,11 @@ begin
 end;
 
 procedure g_Game_LoadData();
-var
-  wl, hl: Integer;
-  wr, hr: Integer;
-  wb, hb: Integer;
-  wm, hm: Integer;
 begin
   if DataLoaded then Exit;
 
   e_WriteLog('Loading game data...', TMsgType.Notify);
 
-  g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
-  g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
-  g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
-  g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
-  g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
-  g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
-  g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_R_BASE');
-  g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_R_STOLEN');
-  g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_R_DROP');
-  g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_B_BASE');
-  g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_B_STOLEN');
-  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;
-  if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD+':TEXTURES\LMARKER') then hasPBarGfx := false;
-  if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD+':TEXTURES\LMIDDLE') then hasPBarGfx := false;
-  if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD+':TEXTURES\LRIGHT') then hasPBarGfx := false;
-
-  if hasPBarGfx then
-  begin
-    g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
-    g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
-    g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
-    g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
-    if (wl > 0) and (hl > 0) and (wr > 0) and (hr = hl) and (wb > 0) and (hb = hl) and (wm > 0) and (hm > 0) and (hm <= hl) then
-    begin
-      // yay!
-    end
-    else
-    begin
-      hasPBarGfx := false;
-    end;
-  end;
-
-  g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, 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_SECRET', GameWAD+':SOUNDS\SECRET');
@@ -2280,10 +2460,7 @@ begin
   g_Game_StopAllSounds(True);
   gMusic.Free();
   g_Game_FreeData();
-  r_PlayerModel_Finalize;
   g_PlayerModel_FreeData();
-  g_Texture_DeleteAll();
-  g_Frames_DeleteAll();
 {$IFNDEF HEADLESS}
   //g_Menu_Free(); //k8: this segfaults after resolution change; who cares?
 {$ENDIF}
@@ -2308,25 +2485,6 @@ begin
 
   e_WriteLog('Releasing game data...', TMsgType.Notify);
 
-  g_Texture_Delete('NOTEXTURE');
-  g_Texture_Delete('TEXTURE_PLAYER_HUD');
-  g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
-  g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
-  g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
-  g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
-  g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
-  g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
-  g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
-  g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
-  g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
-  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_SECRET');
@@ -2406,50 +2564,6 @@ begin
   e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning);
 end;
 
-procedure g_Game_SetupScreenSize();
-const
-  RES_FACTOR = 4.0 / 3.0;
-var
-  s: Single;
-  rf: Single;
-  bw, bh: Word;
-begin
-// Размер экранов игроков:
-  gPlayerScreenSize.X := gScreenWidth-196;
-  if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
-    gPlayerScreenSize.Y := gScreenHeight div 2
-  else
-    gPlayerScreenSize.Y := gScreenHeight;
-
-// Размер заднего плана:
-  if BackID <> DWORD(-1) then
-  begin
-    s := SKY_STRETCH;
-    if (gScreenWidth*s > gMapInfo.Width) or
-       (gScreenHeight*s > gMapInfo.Height) then
-    begin
-      gBackSize.X := gScreenWidth;
-      gBackSize.Y := gScreenHeight;
-    end
-    else
-    begin
-      e_GetTextureSize(BackID, @bw, @bh);
-      rf := Single(bw) / Single(bh);
-      if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
-      else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
-      s := Max(gScreenWidth / bw, gScreenHeight / bh);
-      if (s < 1.0) then s := 1.0;
-      gBackSize.X := Round(bw*s);
-      gBackSize.Y := Round(bh*s);
-    end;
-  end;
-end;
-
-procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
-begin
-  sys_SetDisplayMode(newWidth, newHeight, gBPP, nowFull, nowMax);
-end;
-
 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
 begin
   if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
@@ -2611,9 +2725,6 @@ begin
 
   g_Game_ExecuteEvent('ongamestart');
 
-// Установка размеров окон игроков:
-  g_Game_SetupScreenSize();
-
 // Создание первого игрока:
   gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
                                            gPlayer1Settings.Color,
@@ -2695,9 +2806,6 @@ begin
 
   g_Game_ExecuteEvent('ongamestart');
 
-// Установка размеров окон игроков:
-  g_Game_SetupScreenSize();
-
 // Режим наблюдателя:
   if nPlayers = 0 then
   begin
@@ -2799,9 +2907,6 @@ begin
 
   g_Game_ExecuteEvent('ongamestart');
 
-// Установка размеров окна игрока
-  g_Game_SetupScreenSize();
-
 // Режим наблюдателя:
   if nPlayers = 0 then
   begin
@@ -2913,9 +3018,6 @@ begin
 
   g_Game_ExecuteEvent('ongamestart');
 
-// Установка размеров окон игроков:
-  g_Game_SetupScreenSize();
-
   NetState := NET_STATE_AUTH;
 
   g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
@@ -3184,6 +3286,9 @@ begin
   begin
     //result := g_Map_Load(gGameSettings.WAD + ':\' + ResName);
     result := g_Map_Load(NewWAD+':\'+ResName);
+    {$IFNDEF HEADLESS}
+      r_Render_LoadTextures;
+    {$ENDIF}
   end;
   if Result then
     begin
@@ -3194,7 +3299,7 @@ begin
       gGameOn := True;
 
       DisableCheats();
-      ResetTimer();
+      wNeedTimeReset := True;
 
       if gGameSettings.GameMode = GM_CTF then
       begin
@@ -5353,7 +5458,9 @@ begin
   end
   else if cmd = 'screenshot' then
   begin
-    g_TakeScreenShot()
+    {$IFNDEF HEADLESS}
+      g_TakeScreenShot()
+    {$ENDIF}
   end
   else if cmd = 'weapon' then
   begin
@@ -5590,17 +5697,10 @@ begin
         g_Game_Free();
         g_Game_Quit();
       end;
+{$IFNDEF HEADLESS}
     'r_reset':
-      begin
-        gRC_Width := Max(1, gRC_Width);
-        gRC_Height := Max(1, gRC_Height);
-        gBPP := Max(1, gBPP);
-        if sys_SetDisplayMode(gRC_Width, gRC_Height, gBPP, gRC_FullScreen, gRC_Maximized) = True then
-          e_LogWriteln('resolution changed')
-        else
-          e_LogWriteln('resolution not changed');
-        sys_EnableVSync(gVSync);
-      end;
+         r_Render_Apply;
+{$ENDIF}
     'r_maxfps':
       begin
         if Length(p) = 2 then
@@ -5646,35 +5746,28 @@ begin
   end;
 end;
 
+{$IFNDEF HEADLESS}
 procedure g_TakeScreenShot(Filename: string = '');
-  var s: TStream; t: TDateTime; dir, date, name: String;
+  var t: TDateTime; dir, date, name: String;
 begin
-  if e_NoGraphics then Exit;
-  try
-    dir := e_GetWriteableDir(ScreenshotDirs);
+  if e_NoGraphics then
+    Exit;
 
-    if Filename = '' then
-    begin
-      t := Now;
-      DateTimeToString(date, 'yyyy-mm-dd-hh-nn-ss', t);
-      Filename := 'screenshot-' + date;
-    end;
-    
-    name := e_CatPath(dir, Filename + '.png');
-    s := createDiskFile(name);
-    try
-      e_MakeScreenshot(s, gScreenWidth, gScreenHeight);
-      s.Free;
-      g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [name]))
-    except
-      g_Console_Add(Format(_lc[I_CONSOLE_ERROR_WRITE], [name]));
-      s.Free;
-      DeleteFile(name)
-    end
-  except
-    g_Console_Add('oh shit, i can''t create screenshot!')
-  end
+  dir := e_GetWriteableDir(ScreenshotDirs);
+  if Filename = '' then
+  begin
+    t := Now;
+    DateTimeToString(date, 'yyyy-mm-dd-hh-nn-ss', t);
+    Filename := 'screenshot-' + date;
+  end;
+
+  name := e_CatPath(dir, Filename + '.png');
+  if r_Render_WriteScreenShot(name) then
+    g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [name]))
+  else
+    g_Console_Add(Format(_lc[I_CONSOLE_ERROR_WRITE], [name]));
 end;
+{$ENDIF}
 
 procedure g_Game_InGameMenu(Show: Boolean);
 begin
@@ -5849,12 +5942,11 @@ begin
   end;
 end;
 
-procedure g_Game_Message(Msg: string; Time: Word);
-begin
-  MessageLineLength := (gScreenWidth - 204) div e_CharFont_GetMaxWidth(gMenuFont);
-  MessageText := b_Text_Wrap(b_Text_Format(Msg), MessageLineLength);
-  MessageTime := Time;
-end;
+  procedure g_Game_Message (Msg: string; Time: Word);
+  begin
+    MessageText := Msg;
+    MessageTime := Time;
+  end;
 
 procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
 const