DEADSOFTWARE

cleanup: remove g_main.pas
authorDeaDDooMER <deaddoomer@deadsoftware.ru>
Tue, 15 Jun 2021 15:44:26 +0000 (18:44 +0300)
committerDeaDDooMER <deaddoomer@deadsoftware.ru>
Tue, 29 Jun 2021 09:51:12 +0000 (12:51 +0300)
25 files changed:
src/game/Doom2DF.lpr
src/game/g_console.pas
src/game/g_game.pas
src/game/g_holmes.pas
src/game/g_items.pas
src/game/g_main.pas [deleted file]
src/game/g_map.pas
src/game/g_menu.pas
src/game/g_monsters.pas
src/game/g_net.pas
src/game/g_netmsg.pas
src/game/g_options.pas
src/game/g_player.pas
src/game/g_playermodel.pas
src/game/g_res_downloader.pas
src/game/g_saveload.pas
src/game/g_triggers.pas
src/game/g_weapons.pas
src/game/g_window.pas
src/game/opengl/r_console.pas
src/game/opengl/r_playermodel.pas
src/game/sdl/g_system.pas
src/game/sdl2/g_system.pas
src/game/sdl2/g_touch.pas
src/game/stub/g_system.pas

index f42c4b57cc24d2def0530e4701e0e1834e33fb20..bbbcdd3089eeacf26c5e688d19b377a3e1b76990 100644 (file)
@@ -27,7 +27,10 @@ uses
   ctypes,
 {$ENDIF}
 {$IFDEF UNIX}
   ctypes,
 {$ENDIF}
 {$IFDEF UNIX}
-  cthreads,
+  cthreads, BaseUnix,
+{$ENDIF}
+{$IFDEF DARWIN}
+  MacOSAll, CocoaAll,
 {$ENDIF}
   mempool in '../shared/mempool.pas',
   conbuf in '../shared/conbuf.pas',
 {$ENDIF}
   mempool in '../shared/mempool.pas',
   conbuf in '../shared/conbuf.pas',
@@ -123,7 +126,6 @@ uses
   g_gfx in 'g_gfx.pas',
   g_gui in 'g_gui.pas',
   g_items in 'g_items.pas',
   g_gfx in 'g_gfx.pas',
   g_gui in 'g_gui.pas',
   g_items in 'g_items.pas',
-  g_main in 'g_main.pas',
   g_map in 'g_map.pas',
   g_menu in 'g_menu.pas',
   g_monsters in 'g_monsters.pas',
   g_map in 'g_map.pas',
   g_menu in 'g_menu.pas',
   g_monsters in 'g_monsters.pas',
@@ -204,20 +206,527 @@ uses
   {$R *.res}
 {$ENDIF}
 
   {$R *.res}
 {$ENDIF}
 
-{$IFDEF ANDROID}
-function SDL_main(argc: CInt; argv: PPChar): CInt; cdecl;
-{$ENDIF ANDROID}
+  var
+    noct: Boolean = False;
+    binPath: AnsiString = '';
+    forceBinDir: Boolean = False;
 
 
-var
-  f: Integer;
-  noct: Boolean = false;
-{$IFDEF ANDROID}
-  storage: String;
+function GetBinaryPath (): AnsiString;
+  {$IFDEF LINUX}
+    var sl: AnsiString;
+  {$ENDIF}
+begin
+  result := ExtractFilePath(ParamStr(0));
+  {$IFDEF LINUX}
+  // it may be a symlink; do some guesswork here
+  sl := fpReadLink(ExtractFileName(ParamStr(0)));
+  if (sl = ParamStr(0)) then
+  begin
+    // use current directory, as we don't have anything better
+    //result := '.';
+    GetDir(0, result);
+  end;
+  {$ENDIF}
+  result := fixSlashes(result);
+  if (length(result) > 0) and (result[length(result)] <> '/') then
+    result := result + '/';
+end;
+
+procedure PrintDirs (msg: AnsiString; dirs: SSArray);
+  var dir: AnsiString;
+begin
+  e_LogWriteln(msg + ':');
+  for dir in dirs do
+    e_LogWriteln('  ' + dir);
+end;
+
+{$IFDEF DARWIN}
+  function NSStringToAnsiString (s: NSString): AnsiString;
+    var i: Integer;
+  begin
+    result := '';
+    for i := 0 to s.length - 1 do
+      result := result + AnsiChar(s.characterAtIndex(i));
+  end;
+
+  function GetBundlePath (): AnsiString;
+    var pathRef: CFURLRef; pathCFStr: CFStringRef; pathStr: ShortString;
+  begin
+    pathRef := CFBundleCopyBundleURL(CFBundleGetMainBundle());
+    pathCFStr := CFURLCopyFileSystemPath(pathRef, kCFURLPOSIXPathStyle);
+    CFStringGetPascalString(pathCFStr, @pathStr, 255, CFStringGetSystemEncoding());
+    CFRelease(pathRef);
+    CFRelease(pathCFStr);
+    Result := pathStr;
+  end;
 {$ENDIF}
 {$ENDIF}
-  //tfo: Text;
+
+procedure InitPath;
+  var i: Integer; rwdir, rodir: AnsiString; rwdirs, rodirs: SSArray;
+
+  procedure AddDir (var dirs: SSArray; append: AnsiString);
+  begin
+    SetLength(dirs, Length(dirs) + 1);
+    dirs[High(dirs)] := ExpandFileName(append)
+  end;
+
+  function IsSep (ch: Char): Boolean;
+  begin
+    {$IFDEF WINDOWS}
+    result := (ch = '/') or (ch = '\');
+    {$ELSE}
+    result := (ch = '/');
+    {$ENDIF}
+  end;
+
+  function OptimizePath (dir: AnsiString): AnsiString;
+    var i, len: Integer; s: AnsiString;
+  begin
+    i := 1; len := Length(dir); s := '';
+    while i <= len do
+    begin
+      if IsSep(dir[i]) then
+      begin
+        s := s + DirectorySeparator;
+        Inc(i);
+        while (i <= len) and IsSep(dir[i]) do Inc(i);
+        if (i <= len) and (dir[i] = '.') then
+        begin
+          if (i = len) or IsSep(dir[i + 1]) then
+          begin
+            Inc(i)
+          end
+          else if (i + 1 <= len) and (dir[i + 1] = '.') then
+          begin
+            if (i + 1 = len) or IsSep(dir[i + 2]) then
+            begin
+              s := e_UpperDir(s);
+              Inc(i, 2)
+            end
+          end
+        end
+      end
+      else
+      begin
+        s := s + dir[i];
+        Inc(i)
+      end
+    end;
+    result := s
+  end;
+
+  procedure OptimizeDirs (var dirs: SSArray);
+    var i, j, k: Integer;
+  begin
+    for i := 0 to High(dirs) do
+      dirs[i] := OptimizePath(dirs[i]);
+    // deduplicate
+    i := High(dirs);
+    while i >= 0 do
+    begin
+      j := 0;
+      while j < i do
+      begin
+        if dirs[j] = dirs[i] then
+        begin
+          for k := j + 1 to High(dirs) do
+            dirs[k - 1] := dirs[k];
+          Dec(i);
+          SetLength(dirs, High(dirs))
+        end
+        else
+        begin
+          Inc(j)
+        end
+      end;
+      Dec(i)
+    end
+  end;
+
+  procedure AddDef (var dirs: SSArray; base: SSArray; append: AnsiString);
+    var s: AnsiString;
+  begin
+    if Length(dirs) = 0 then
+      for s in base do
+        AddDir(dirs, e_CatPath(s, append));
+    OptimizeDirs(dirs)
+  end;
+
+  function GetDefaultRODirs (): SSArray;
+    {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
+      var home: AnsiString;
+    {$ENDIF}
+    {$IFDEF WINDOWS}
+      var appdata: AnsiString;
+    {$ENDIF}
+    {$IFDEF DARWIN}
+      var bundle, s: AnsiString; dirArr: NSArray; i: Integer;
+    {$ENDIF}
+  begin
+    result := nil;
+    {$IFDEF DARWIN}
+      bundle := GetBundlePath();
+      if ExtractFileExt(bundle) <> '.app' then
+        AddDir(result, binpath);
+    {$ELSE}
+      AddDir(result, binPath);
+    {$ENDIF}
+    if forceBinDir = false then
+    begin
+      {$IFDEF USE_SDL2}
+        AddDir(result, SDL_GetBasePath());
+        AddDir(result, SDL_GetPrefPath('', 'doom2df'));
+      {$ENDIF}
+      {$IFDEF WINDOWS}
+        appdata := GetEnvironmentVariable('APPDATA') + '\doom2df';
+        if appdata <> '' then
+          AddDir(result, appdata);
+      {$ENDIF}
+      {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
+        AddDir(result, '/usr/share/doom2df');
+        AddDir(result, '/usr/local/share/doom2df');
+        home := GetEnvironmentVariable('HOME');
+        if home <> '' then
+          AddDir(result, e_CatPath(home, '.doom2df'));
+      {$ENDIF}
+      {$IFDEF DARWIN}
+        bundle := GetBundlePath();
+        if bundle <> '' then
+          AddDir(result, e_CatPath(bundle, 'Contents/Resources'));
+        dirArr := NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true);
+        for i := 0 to dirArr.count - 1 do
+        begin
+          s := NSStringToAnsiString(dirArr.objectAtIndex(i));
+          AddDir(result, e_CatPath(s, 'Doom 2D Forever'))
+        end;
+      {$ENDIF}
+      {$IF DEFINED(ANDROID) AND DEFINED(USE_SDL2)}
+        AddDir(result, SDL_AndroidGetInternalStoragePath());
+        if SDL_AndroidGetExternalStorageState() <> 0 then
+          AddDir(result, SDL_AndroidGetExternalStoragePath());
+      {$ENDIF}
+    end
+  end;
+
+  function GetDefaultRWDirs (): SSArray;
+    {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
+      var home: AnsiString;
+    {$ENDIF}
+    {$IFDEF WINDOWS}
+      var appdata: AnsiString;
+    {$ENDIF}
+    {$IFDEF DARWIN}
+      var bundle, s: AnsiString; dirArr: NSArray; i: Integer;
+    {$ENDIF}
+  begin
+    result := nil;
+    {$IFDEF DARWIN}
+      bundle := GetBundlePath();
+      if ExtractFileExt(bundle) <> '.app' then
+        AddDir(result, binPath);
+    {$ELSE}
+      AddDir(result, binPath);
+    {$ENDIF}
+    if forceBinDir = false then
+    begin
+      {$IFDEF USE_SDL2}
+        AddDir(result, SDL_GetPrefPath('', 'doom2df'));
+      {$ENDIF}
+      {$IFDEF WINDOWS}
+        appdata := GetEnvironmentVariable('APPDATA') + '\doom2df';
+        if appdata <> '' then
+          AddDir(result, appdata);
+      {$ENDIF}
+      {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
+        home := GetEnvironmentVariable('HOME');
+        if home <> '' then
+          AddDir(result, e_CatPath(home, '.doom2df'));
+      {$ENDIF}
+      {$IFDEF DARWIN}
+        dirArr := NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true);
+        for i := 0 to dirArr.count - 1 do
+        begin
+          s := NSStringToAnsiString(dirArr.objectAtIndex(i));
+          AddDir(result, e_CatPath(s, 'Doom 2D Forever'))
+        end;
+      {$ENDIF}
+      {$IF DEFINED(ANDROID) AND DEFINED(USE_SDL2)}
+        if SDL_AndroidGetExternalStorageState() <> 0 then
+          AddDir(result, SDL_AndroidGetExternalStoragePath());
+      {$ENDIF}
+    end
+  end;
+
 begin
 begin
-  SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]); //k8: fuck off, that's why
+  forceBinDir := false;
+  binPath := GetBinaryPath();
+
+  i := 1;
+  while i < ParamCount do
+  begin
+    case ParamStr(i) of
+    '--like-windoze': forceBinDir := true;
+    '--rw-dir':
+      begin
+        Inc(i);
+        rwdir := ParamStr(i);
+        (* RW *)
+        AddDir(LogDirs, e_CatPath(rwdir, ''));
+        AddDir(SaveDirs, e_CatPath(rwdir, 'data'));
+        AddDir(CacheDirs, e_CatPath(rwdir, 'data/cache'));
+        AddDir(ConfigDirs, e_CatPath(rwdir, ''));
+        AddDir(MapDownloadDirs, e_CatPath(rwdir, 'maps/downloads'));
+        AddDir(WadDownloadDirs, e_CatPath(rwdir, 'wads/downloads'));
+        AddDir(ScreenshotDirs, e_CatPath(rwdir, 'screenshots'));
+        AddDir(StatsDirs, e_CatPath(rwdir, 'stats'));
+        (* RO *)
+        AddDir(DataDirs, e_CatPath(rwdir, 'data'));
+        AddDir(ModelDirs, e_CatPath(rwdir, 'data/models'));
+        AddDir(MegawadDirs, e_CatPath(rwdir, 'maps/megawads'));
+        AddDir(MapDirs, e_CatPath(rwdir, 'maps'));
+        AddDir(WadDirs, e_CatPath(rwdir, 'wads'));
+      end;
+    '--ro-dir':
+      begin
+        Inc(i);
+        rodir := ParamStr(i);
+        (* RO *)
+        AddDir(DataDirs, e_CatPath(rodir, 'data'));
+        AddDir(ModelDirs, e_CatPath(rodir, 'data/models'));
+        AddDir(MegawadDirs, e_CatPath(rodir, 'maps/megawads'));
+        AddDir(MapDirs, e_CatPath(rodir, 'maps'));
+        AddDir(WadDirs, e_CatPath(rodir, 'wads'));
+      end;
+    '--game-wad':
+      begin
+        Inc(i);
+        GameWADName := ParamStr(i);
+      end;
+    '--config':
+      begin
+        Inc(i);
+        gConfigScript := ParamStr(i);
+      end;
+    end;
+    Inc(i)
+  end;
+
+  // prefer bin dir if it writable and contains game.wad
+  if forceBinDir = false then
+  begin
+    if findDiskWad(binPath + 'data' + '/' + GameWADName) <> '' then
+      if e_CanCreateFilesAt(binPath) then
+        forceBinDir := true
+  end;
+
+  (* RO *)
+  rodirs := GetDefaultRODirs();
+  AddDef(DataDirs, rodirs, 'data');
+  AddDef(ModelDirs, rodirs, 'data/models');
+  AddDef(MegawadDirs, rodirs, 'maps/megawads');
+  AddDef(MapDirs, rodirs, 'maps');
+  AddDef(WadDirs, rodirs, 'wads');
+
+  (* RW *)
+  rwdirs := GetDefaultRWDirs();
+  AddDef(LogDirs, rwdirs, '');
+  AddDef(SaveDirs, rwdirs, 'data');
+  AddDef(CacheDirs, rwdirs, 'data/cache');
+  AddDef(ConfigDirs, rwdirs, '');
+  AddDef(MapDownloadDirs, rwdirs, 'maps/downloads');
+  AddDef(WadDownloadDirs, rwdirs, 'wads/downloads');
+  AddDef(ScreenshotDirs, rwdirs, 'screenshots');
+  AddDef(StatsDirs, rwdirs, 'stats');
+
+  for i := 0 to High(MapDirs) do
+    AddDir(AllMapDirs, MapDirs[i]);
+  for i := 0 to High(MegawadDirs) do
+    AddDir(AllMapDirs, MegawadDirs[i]);
+  OptimizeDirs(AllMapDirs);
+
+  if LogFileName = '' then
+  begin
+    rwdir := e_GetWriteableDir(LogDirs, false);
+    if rwdir <> '' then
+    begin
+      {$IFDEF HEADLESS}
+        LogFileName := e_CatPath(rwdir, 'Doom2DF_H.log');
+      {$ELSE}
+        LogFileName := e_CatPath(rwdir, 'Doom2DF.log');
+      {$ENDIF}
+    end
+  end;
+
+  // HACK: ensure the screenshots folder also has a stats subfolder in it
+  rwdir := e_GetWriteableDir(ScreenshotDirs, false);
+  if rwdir <> '' then CreateDir(rwdir + '/stats');
+end;
+
+procedure InitPrep;
+  {$IF DEFINED(ANDROID) AND DEFINED(USE_SDLMIXER)}
+    var timiditycfg: AnsiString;
+  {$ENDIF}
+  var i: Integer;
+begin
+  {$IFDEF HEADLESS}
+    conbufDumpToStdOut := true;
+  {$ENDIF}
+  for i := 1 to ParamCount do
+  begin
+    case ParamStr(i) of
+      '--con-stdout': conbufDumpToStdOut := true;
+      '--no-fbo': glRenderToFBO := false;
+    end
+  end;
+
+  if LogFileName <> '' then
+    e_InitLog(LogFileName, TWriteMode.WM_NEWFILE);
+  e_InitWritelnDriver();
+  e_WriteLog('Doom 2D: Forever version ' + GAME_VERSION + ' proto ' + IntToStr(NET_PROTOCOL_VER), TMsgType.Notify);
+  e_WriteLog('Build date: ' + GAME_BUILDDATE + ' ' + GAME_BUILDTIME, TMsgType.Notify);
+  e_WriteLog('Build hash: ' + g_GetBuildHash(), TMsgType.Notify);
+  e_WriteLog('Build by: ' + g_GetBuilderName(), TMsgType.Notify);
+
+  e_LogWritefln('Force bin dir: %s', [forceBinDir], TMsgType.Notify);
+  e_LogWritefln('BINARY PATH: [%s]', [binPath], TMsgType.Notify);
+
+  PrintDirs('DataDirs', DataDirs);
+  PrintDirs('ModelDirs', ModelDirs);
+  PrintDirs('MegawadDirs', MegawadDirs);
+  PrintDirs('MapDirs', MapDirs);
+  PrintDirs('WadDirs', WadDirs);
+
+  PrintDirs('LogDirs', LogDirs);
+  PrintDirs('SaveDirs', SaveDirs);
+  PrintDirs('CacheDirs', CacheDirs);
+  PrintDirs('ConfigDirs', ConfigDirs);
+  PrintDirs('ScreenshotDirs', ScreenshotDirs);
+  PrintDirs('StatsDirs', StatsDirs);
+  PrintDirs('MapDownloadDirs', MapDownloadDirs);
+  PrintDirs('WadDownloadDirs', WadDownloadDirs);
+
+  GameWAD := e_FindWad(DataDirs, GameWADName);
+  if GameWad = '' then
+  begin
+    e_WriteLog('WAD ' + GameWADName + ' not found in data directories.', TMsgType.Fatal);
+    {$IF DEFINED(USE_SDL2) AND NOT DEFINED(HEADLESS)}
+      if forceBinDir = false then
+        SDL_ShowSimpleMessageBox(
+          SDL_MESSAGEBOX_ERROR,
+          'Doom 2D Forever',
+          PChar('WAD ' + GameWADName + ' not found in data directories.'),
+          nil
+        );
+    {$ENDIF}
+    e_DeinitLog;
+    Halt(1);
+  end;
+
+  {$IF DEFINED(ANDROID) AND DEFINED(USE_SDLMIXER)}
+    timiditycfg := 'timidity.cfg';
+    if e_FindResource(ConfigDirs, timiditycfg) = true then
+    begin
+      timiditycfg := ExpandFileName(timiditycfg);
+      SetEnvVar('TIMIDITY_CFG', timiditycfg);
+      e_LogWritefln('Set TIMIDITY_CFG = "%s"', [timiditycfg]);
+    end;
+  {$ENDIF}
+end;
+
+procedure Main;
+{$IFDEF ENABLE_HOLMES}
+  var flexloaded: Boolean;
+{$ENDIF}
+begin
+  InitPath;
+  InitPrep;
+  e_InitInput;
+  sys_Init;
+
+  sys_CharPress := @CharPress;
+
+  g_Options_SetDefault;
+  g_Options_SetDefaultVideo;
+  g_Console_SysInit;
+  if sys_SetDisplayMode(gRC_Width, gRC_Height, gBPP, gRC_FullScreen, gRC_Maximized) = False then
+    raise Exception.Create('Failed to set videomode on startup.');
+
+  e_WriteLog(gLanguage, TMsgType.Notify);
+  g_Language_Set(gLanguage);
+
+{$IF not DEFINED(HEADLESS) and DEFINED(ENABLE_HOLMES)}
+  flexloaded := true;
+  if not fuiAddWad('flexui.wad') then
+  begin
+    if not fuiAddWad('./data/flexui.wad') then fuiAddWad('./flexui.wad');
+  end;
+  try
+    fuiGfxLoadFont('win8', 'flexui/fonts/win8.fuifont');
+    fuiGfxLoadFont('win14', 'flexui/fonts/win14.fuifont');
+    fuiGfxLoadFont('win16', 'flexui/fonts/win16.fuifont');
+    fuiGfxLoadFont('dos8', 'flexui/fonts/dos8.fuifont');
+    fuiGfxLoadFont('msx6', 'flexui/fonts/msx6.fuifont');
+  except on e: Exception do
+    begin
+      writeln('ERROR loading FlexUI fonts');
+      flexloaded := false;
+      //raise;
+    end;
+  else
+    begin
+      flexloaded := false;
+      //raise;
+    end;
+  end;
+  if (flexloaded) then
+  begin
+    try
+      e_LogWriteln('FlexUI: loading stylesheet...');
+      uiLoadStyles('flexui/widgets.wgs');
+    except on e: TParserException do
+      begin
+        writeln('ERROR at (', e.tokLine, ',', e.tokCol, '): ', e.message);
+        //raise;
+        flexloaded := false;
+      end;
+    else
+      begin
+        //raise;
+        flexloaded := false;
+      end;
+    end;
+  end;
+  g_holmes_imfunctional := not flexloaded;
+
+  if (not g_holmes_imfunctional) then
+  begin
+    uiInitialize();
+    uiContext.font := 'win14';
+  end;
+
+  if assigned(oglInitCB) then oglInitCB;
+{$ENDIF}
+
+  //g_Res_CreateDatabases(true); // it will be done before connecting to the server for the first time
+
+  e_WriteLog('Entering SDLMain', TMsgType.Notify);
+
+  {$WARNINGS OFF}
+    SDLMain();
+  {$WARNINGS ON}
 
 
+  {$IFDEF ENABLE_HOLMES}
+    if assigned(oglDeinitCB) then oglDeinitCB;
+  {$ENDIF}
+
+  g_Console_WriteGameConfig;
+  sys_Final;
+end;
+
+
+procedure EntryParams;
+  var f: Integer;
+begin
   f := 1;
   while f <= ParamCount do
   begin
   f := 1;
   while f <= ParamCount do
   begin
@@ -230,45 +739,43 @@ begin
       begin
         Inc(f);
         LogFileName := ParamStr(f)
       begin
         Inc(f);
         LogFileName := ParamStr(f)
-      end;
+      end
     end;
     Inc(f)
     end;
     Inc(f)
-  end;
+  end
+end;
 
 
+procedure EntryPoint;
+begin
+  SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]); //k8: fuck off, that's why
+  EntryParams;
   if noct then
   if noct then
-  begin
-    Main()
-  end
+    Main
   else
   else
-  begin
-    try
-      Main();
-      e_WriteLog('Shutdown with no errors.', TMsgType.Notify);
-    except
-      on e: Exception do
-        begin
-          e_WriteStackTrace(e.message);
-          //e_WriteLog(Format(_lc[I_SYSTEM_ERROR_MSG], [E.Message]), MSG_FATALERROR);
-          (*
-          AssignFile(tfo, GameDir+'/trace.log');
-          {$I-}
-          Append(tfo);
-          if (IOResult <> 0) then Rewrite(tfo);
-          if (IOResult = 0) then begin writeln(tfo, '====================='); DumpExceptionBackTrace(tfo); CloseFile(tfo); end;
-          *)
-        end
-      else
-        begin
-          //e_WriteLog(Format(_lc[I_SYSTEM_ERROR_UNKNOWN], [NativeUInt(ExceptAddr())]), MSG_FATALERROR);
-          e_WriteStackTrace('FATAL ERROR');
-        end;
-    end;
+  try
+    Main;
+    e_WriteLog('Shutdown with no errors.', TMsgType.Notify)
+  except on e: Exception do
+    e_WriteStackTrace(e.message)
+  else
+    e_WriteStackTrace('FATAL ERROR')
   end;
   end;
-  e_DeinitLog();
+
+  e_DeinitLog;
+end;
 
 {$IFDEF ANDROID}
 
 {$IFDEF ANDROID}
-  result := 0;
-end; // SDL_main
-exports SDL_main;
-{$ENDIF ANDROID}
+  function SDL_main (argc: CInt; argv: PPChar): CInt; cdecl;
+  begin
+    // TODO pass argc+argv to rtl
+    EntryPoint;
+    result := 0
+  end;
+
+  exports SDL_main;
+{$ELSE}
+begin
+  EntryPoint
+{$ENDIF}
+
 end.
 end.
index 9db9e79273ebc64c28d7c789b1ac37a88fa58c55..02beb87ee76fc14509f2d5253f9c666a59d70972 100644 (file)
@@ -99,7 +99,7 @@ var
 implementation
 
 uses
 implementation
 
 uses
-  g_textures, g_main, e_input, g_game, g_gfx, g_player, g_items,
+  g_textures, e_input, g_game, g_gfx, g_player, g_items,
   SysUtils, g_basic, g_options, Math, g_touch, e_res,
   g_menu, g_gui, g_language, g_net, g_netmsg, e_log, conbuf;
 
   SysUtils, g_basic, g_options, Math, g_touch, e_res,
   g_menu, g_gui, g_language, g_net, g_netmsg, e_log, conbuf;
 
index 6fc6035b7944ad839a09033261edef58ba675c59..9095b1d4ab6de440e131a4fbfd1aaf2ffb2c90b5 100644 (file)
@@ -148,6 +148,9 @@ function IsActivePlayer(p: TPlayer): Boolean;
 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
 procedure SortGameStat(var stat: TPlayerStatArray);
 
 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
 { procedure SetWinPause(Enable: Boolean); }
 
 const
@@ -440,10 +443,355 @@ uses
   e_input, e_log, g_console, r_console, g_items, g_map, g_panel,
   g_playermodel, g_gfx, g_options, Math,
   g_triggers, g_monsters, e_sound, CONFIG,
   e_input, e_log, g_console, r_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;
 
   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;
 
 // ////////////////////////////////////////////////////////////////////////// //
 function gPause (): Boolean; inline; begin result := gPauseMain or gPauseHolmes; end;
index b31ead3b2da0b03712af63801a5f2ba2da972dbb..7f74dc18f600bfd8e24343f0a1d98797560c9c61 100644 (file)
@@ -50,7 +50,7 @@ uses
   {$INCLUDE ../nogl/noGLuses.inc}
   {rttiobj,} typinfo, e_res,
   SysUtils, Classes, SDL2,
   {$INCLUDE ../nogl/noGLuses.inc}
   {rttiobj,} typinfo, e_res,
   SysUtils, Classes, SDL2,
-  MAPDEF, g_main, g_options,
+  MAPDEF, g_options,
   utils, hashtable, xparser;
 
 
   utils, hashtable, xparser;
 
 
index 02c8b5fc86dc50e62410e335f7fa0eb61aafea48..6c8041ddd97bfe0ba46c4e2081f5dd1ffd8ab866 100644 (file)
@@ -91,9 +91,9 @@ implementation
 
 uses
   Math,
 
 uses
   Math,
-  g_basic, g_sound, g_main, g_gfx, g_map,
+  g_basic, g_sound, g_gfx, g_map,
   g_game, g_triggers, g_console, g_player, g_net, g_netmsg,
   g_game, g_triggers, g_console, g_player, g_net, g_netmsg,
-  e_log,
+  e_log, g_options,
   g_grid, binheap, idpool, utils, xstreams;
 
 // ////////////////////////////////////////////////////////////////////////// //
   g_grid, binheap, idpool, utils, xstreams;
 
 // ////////////////////////////////////////////////////////////////////////// //
diff --git a/src/game/g_main.pas b/src/game/g_main.pas
deleted file mode 100644 (file)
index 3da02c2..0000000
+++ /dev/null
@@ -1,1010 +0,0 @@
-(* Copyright (C)  Doom 2D: Forever Developers
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3 of the License ONLY.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *)
-{$INCLUDE ../shared/a_modes.inc}
-unit g_main;
-
-interface
-
-  uses Utils;
-
-procedure Main ();
-procedure Init ();
-procedure Release ();
-procedure Update ();
-procedure Draw ();
-procedure KeyPress (K: Word);
-procedure CharPress (C: AnsiChar);
-
-var
-  {--- Read-only dirs ---}
-  GameWAD: string;
-  DataDirs: SSArray;
-  ModelDirs: SSArray;
-  MegawadDirs: SSArray;
-  MapDirs: SSArray;
-  WadDirs: SSArray;
-  AllMapDirs: SSArray; // Maps + Megawads
-
-  {--- Read-Write dirs ---}
-  LogFileName: string;
-  LogDirs: SSArray;
-  SaveDirs: SSArray;
-  CacheDirs: SSArray;
-  ConfigDirs: SSArray;
-  ScreenshotDirs: SSArray;
-  StatsDirs: SSArray;
-  MapDownloadDirs: SSArray;
-  WadDownloadDirs: SSArray;
-
-  GameWADName: string = 'GAME';
-
-implementation
-
-uses
-{$IFDEF ENABLE_HOLMES}
-  g_holmes, sdlcarcass, fui_ctls, fui_wadread, fui_style, fui_gfx_gl,
-{$ENDIF}
-{$IFDEF LINUX}
-  BaseUnix,
-{$ENDIF}
-{$IFDEF DARWIN}
-  MacOSAll, CocoaAll,
-{$ENDIF}
-{$IFDEF USE_SDL2}
-  SDL2,
-{$ENDIF}
-  wadreader, e_log, g_window,
-  r_graphics, e_input, g_game, g_console, g_gui,
-  e_sound, g_options, g_sound, g_player, g_basic,
-  g_weapons, SysUtils, g_triggers, MAPDEF, g_map, e_res,
-  g_menu, g_language, g_net, g_touch, g_system, g_res_downloader,
-  conbuf, envvars, r_game,
-  xparser;
-
-
-var
-  charbuff: packed array [0..15] of AnsiChar;
-  binPath: AnsiString = '';
-  forceBinDir: Boolean;
-
-function GetBinaryPath (): AnsiString;
-{$IFDEF LINUX}
-var
-  //cd: AnsiString;
-  sl: AnsiString;
-{$ENDIF}
-begin
-  result := ExtractFilePath(ParamStr(0));
-  {$IFDEF LINUX}
-  // it may be a symlink; do some guesswork here
-  sl := fpReadLink(ExtractFileName(ParamStr(0)));
-  if (sl = ParamStr(0)) then
-  begin
-    // use current directory, as we don't have anything better
-    //result := '.';
-    GetDir(0, result);
-  end;
-  {$ENDIF}
-  result := fixSlashes(result);
-  if (length(result) > 0) and (result[length(result)] <> '/') then result := result+'/';
-end;
-
-procedure PrintDirs (msg: AnsiString; dirs: SSArray);
-  var dir: AnsiString;
-begin
-  e_LogWriteln(msg + ':');
-  for dir in dirs do
-    e_LogWriteln('  ' + dir);
-end;
-
-{$IFDEF DARWIN}
-  function NSStringToAnsiString (s: NSString): AnsiString;
-    var i: Integer;
-  begin
-    result := '';
-    for i := 0 to s.length - 1 do
-      result := result + AnsiChar(s.characterAtIndex(i));
-  end;
-
-  function GetBundlePath (): AnsiString;
-    var pathRef: CFURLRef; pathCFStr: CFStringRef; pathStr: ShortString;
-  begin
-    pathRef := CFBundleCopyBundleURL(CFBundleGetMainBundle());
-    pathCFStr := CFURLCopyFileSystemPath(pathRef, kCFURLPOSIXPathStyle);
-    CFStringGetPascalString(pathCFStr, @pathStr, 255, CFStringGetSystemEncoding());
-    CFRelease(pathRef);
-    CFRelease(pathCFStr);
-    Result := pathStr;
-  end;
-{$ENDIF}
-
-procedure InitPath;
-  var i: Integer; rwdir, rodir: AnsiString; rwdirs, rodirs: SSArray;
-
-  procedure AddDir (var dirs: SSArray; append: AnsiString);
-  begin
-    SetLength(dirs, Length(dirs) + 1);
-    dirs[High(dirs)] := ExpandFileName(append)
-  end;
-
-  function IsSep (ch: Char): Boolean;
-  begin
-    {$IFDEF WINDOWS}
-    result := (ch = '/') or (ch = '\');
-    {$ELSE}
-    result := (ch = '/');
-    {$ENDIF}
-  end;
-
-  function OptimizePath (dir: AnsiString): AnsiString;
-    var i, len: Integer; s: AnsiString;
-  begin
-    i := 1; len := Length(dir); s := '';
-    while i <= len do
-    begin
-      if IsSep(dir[i]) then
-      begin
-        s := s + DirectorySeparator;
-        Inc(i);
-        while (i <= len) and IsSep(dir[i]) do Inc(i);
-        if (i <= len) and (dir[i] = '.') then
-        begin
-          if (i = len) or IsSep(dir[i + 1]) then
-          begin
-            Inc(i)
-          end
-          else if (i + 1 <= len) and (dir[i + 1] = '.') then
-          begin
-            if (i + 1 = len) or IsSep(dir[i + 2]) then
-            begin
-              s := e_UpperDir(s);
-              Inc(i, 2)
-            end
-          end
-        end
-      end
-      else
-      begin
-        s := s + dir[i];
-        Inc(i)
-      end
-    end;
-    result := s
-  end;
-
-  procedure OptimizeDirs (var dirs: SSArray);
-    var i, j, k: Integer;
-  begin
-    for i := 0 to High(dirs) do
-      dirs[i] := OptimizePath(dirs[i]);
-    // deduplicate
-    i := High(dirs);
-    while i >= 0 do
-    begin
-      j := 0;
-      while j < i do
-      begin
-        if dirs[j] = dirs[i] then
-        begin
-          for k := j + 1 to High(dirs) do
-            dirs[k - 1] := dirs[k];
-          Dec(i);
-          SetLength(dirs, High(dirs))
-        end
-        else
-        begin
-          Inc(j)
-        end
-      end;
-      Dec(i)
-    end
-  end;
-
-  procedure AddDef (var dirs: SSArray; base: SSArray; append: AnsiString);
-    var s: AnsiString;
-  begin
-    if Length(dirs) = 0 then
-      for s in base do
-        AddDir(dirs, e_CatPath(s, append));
-    OptimizeDirs(dirs)
-  end;
-
-  function GetDefaultRODirs (): SSArray;
-    {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
-      var home: AnsiString;
-    {$ENDIF}
-    {$IFDEF WINDOWS}
-      var appdata: AnsiString;
-    {$ENDIF}
-    {$IFDEF DARWIN}
-      var bundle, s: AnsiString; dirArr: NSArray; i: Integer;
-    {$ENDIF}
-  begin
-    result := nil;
-    {$IFDEF DARWIN}
-      bundle := GetBundlePath();
-      if ExtractFileExt(bundle) <> '.app' then
-        AddDir(result, binpath);
-    {$ELSE}
-      AddDir(result, binPath);
-    {$ENDIF}
-    if forceBinDir = false then
-    begin
-      {$IFDEF USE_SDL2}
-        AddDir(result, SDL_GetBasePath());
-        AddDir(result, SDL_GetPrefPath('', 'doom2df'));
-      {$ENDIF}
-      {$IFDEF WINDOWS}
-        appdata := GetEnvironmentVariable('APPDATA') + '\doom2df';
-        if appdata <> '' then
-          AddDir(result, appdata);
-      {$ENDIF}
-      {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
-        AddDir(result, '/usr/share/doom2df');
-        AddDir(result, '/usr/local/share/doom2df');
-        home := GetEnvironmentVariable('HOME');
-        if home <> '' then
-          AddDir(result, e_CatPath(home, '.doom2df'));
-      {$ENDIF}
-      {$IFDEF DARWIN}
-        bundle := GetBundlePath();
-        if bundle <> '' then
-          AddDir(result, e_CatPath(bundle, 'Contents/Resources'));
-        dirArr := NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true);
-        for i := 0 to dirArr.count - 1 do
-        begin
-          s := NSStringToAnsiString(dirArr.objectAtIndex(i));
-          AddDir(result, e_CatPath(s, 'Doom 2D Forever'))
-        end;
-      {$ENDIF}
-      {$IF DEFINED(ANDROID) AND DEFINED(USE_SDL2)}
-        AddDir(result, SDL_AndroidGetInternalStoragePath());
-        if SDL_AndroidGetExternalStorageState() <> 0 then
-          AddDir(result, SDL_AndroidGetExternalStoragePath());
-      {$ENDIF}
-    end
-  end;
-
-  function GetDefaultRWDirs (): SSArray;
-    {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
-      var home: AnsiString;
-    {$ENDIF}
-    {$IFDEF WINDOWS}
-      var appdata: AnsiString;
-    {$ENDIF}
-    {$IFDEF DARWIN}
-      var bundle, s: AnsiString; dirArr: NSArray; i: Integer;
-    {$ENDIF}
-  begin
-    result := nil;
-    {$IFDEF DARWIN}
-      bundle := GetBundlePath();
-      if ExtractFileExt(bundle) <> '.app' then
-        AddDir(result, binPath);
-    {$ELSE}
-      AddDir(result, binPath);
-    {$ENDIF}
-    if forceBinDir = false then
-    begin
-      {$IFDEF USE_SDL2}
-        AddDir(result, SDL_GetPrefPath('', 'doom2df'));
-      {$ENDIF}
-      {$IFDEF WINDOWS}
-        appdata := GetEnvironmentVariable('APPDATA') + '\doom2df';
-        if appdata <> '' then
-          AddDir(result, appdata);
-      {$ENDIF}
-      {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
-        home := GetEnvironmentVariable('HOME');
-        if home <> '' then
-          AddDir(result, e_CatPath(home, '.doom2df'));
-      {$ENDIF}
-      {$IFDEF DARWIN}
-        dirArr := NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true);
-        for i := 0 to dirArr.count - 1 do
-        begin
-          s := NSStringToAnsiString(dirArr.objectAtIndex(i));
-          AddDir(result, e_CatPath(s, 'Doom 2D Forever'))
-        end;
-      {$ENDIF}
-      {$IF DEFINED(ANDROID) AND DEFINED(USE_SDL2)}
-        if SDL_AndroidGetExternalStorageState() <> 0 then
-          AddDir(result, SDL_AndroidGetExternalStoragePath());
-      {$ENDIF}
-    end
-  end;
-
-begin
-  forceBinDir := false;
-  binPath := GetBinaryPath();
-
-  i := 1;
-  while i < ParamCount do
-  begin
-    case ParamStr(i) of
-    '--like-windoze': forceBinDir := true;
-    '--rw-dir':
-      begin
-        Inc(i);
-        rwdir := ParamStr(i);
-        (* RW *)
-        AddDir(LogDirs, e_CatPath(rwdir, ''));
-        AddDir(SaveDirs, e_CatPath(rwdir, 'data'));
-        AddDir(CacheDirs, e_CatPath(rwdir, 'data/cache'));
-        AddDir(ConfigDirs, e_CatPath(rwdir, ''));
-        AddDir(MapDownloadDirs, e_CatPath(rwdir, 'maps/downloads'));
-        AddDir(WadDownloadDirs, e_CatPath(rwdir, 'wads/downloads'));
-        AddDir(ScreenshotDirs, e_CatPath(rwdir, 'screenshots'));
-        AddDir(StatsDirs, e_CatPath(rwdir, 'stats'));
-        (* RO *)
-        AddDir(DataDirs, e_CatPath(rwdir, 'data'));
-        AddDir(ModelDirs, e_CatPath(rwdir, 'data/models'));
-        AddDir(MegawadDirs, e_CatPath(rwdir, 'maps/megawads'));
-        AddDir(MapDirs, e_CatPath(rwdir, 'maps'));
-        AddDir(WadDirs, e_CatPath(rwdir, 'wads'));
-      end;
-    '--ro-dir':
-      begin
-        Inc(i);
-        rodir := ParamStr(i);
-        (* RO *)
-        AddDir(DataDirs, e_CatPath(rodir, 'data'));
-        AddDir(ModelDirs, e_CatPath(rodir, 'data/models'));
-        AddDir(MegawadDirs, e_CatPath(rodir, 'maps/megawads'));
-        AddDir(MapDirs, e_CatPath(rodir, 'maps'));
-        AddDir(WadDirs, e_CatPath(rodir, 'wads'));
-      end;
-    '--game-wad':
-      begin
-        Inc(i);
-        GameWADName := ParamStr(i);
-      end;
-    '--config':
-      begin
-        Inc(i);
-        gConfigScript := ParamStr(i);
-      end;
-    end;
-    Inc(i)
-  end;
-
-  // prefer bin dir if it writable and contains game.wad
-  if forceBinDir = false then
-  begin
-    if findDiskWad(binPath + 'data' + '/' + GameWADName) <> '' then
-      if e_CanCreateFilesAt(binPath) then
-        forceBinDir := true
-  end;
-
-  (* RO *)
-  rodirs := GetDefaultRODirs();
-  AddDef(DataDirs, rodirs, 'data');
-  AddDef(ModelDirs, rodirs, 'data/models');
-  AddDef(MegawadDirs, rodirs, 'maps/megawads');
-  AddDef(MapDirs, rodirs, 'maps');
-  AddDef(WadDirs, rodirs, 'wads');
-
-  (* RW *)
-  rwdirs := GetDefaultRWDirs();
-  AddDef(LogDirs, rwdirs, '');
-  AddDef(SaveDirs, rwdirs, 'data');
-  AddDef(CacheDirs, rwdirs, 'data/cache');
-  AddDef(ConfigDirs, rwdirs, '');
-  AddDef(MapDownloadDirs, rwdirs, 'maps/downloads');
-  AddDef(WadDownloadDirs, rwdirs, 'wads/downloads');
-  AddDef(ScreenshotDirs, rwdirs, 'screenshots');
-  AddDef(StatsDirs, rwdirs, 'stats');
-
-  for i := 0 to High(MapDirs) do
-    AddDir(AllMapDirs, MapDirs[i]);
-  for i := 0 to High(MegawadDirs) do
-    AddDir(AllMapDirs, MegawadDirs[i]);
-  OptimizeDirs(AllMapDirs);
-
-  if LogFileName = '' then
-  begin
-    rwdir := e_GetWriteableDir(LogDirs, false);
-    if rwdir <> '' then
-    begin
-      {$IFDEF HEADLESS}
-        LogFileName := e_CatPath(rwdir, 'Doom2DF_H.log');
-      {$ELSE}
-        LogFileName := e_CatPath(rwdir, 'Doom2DF.log');
-      {$ENDIF}
-    end
-  end;
-  
-  // HACK: ensure the screenshots folder also has a stats subfolder in it
-  rwdir := e_GetWriteableDir(ScreenshotDirs, false);
-  if rwdir <> '' then CreateDir(rwdir + '/stats');
-end;
-
-procedure InitPrep;
-  {$IF DEFINED(ANDROID) AND DEFINED(USE_SDLMIXER)}
-    var timiditycfg: AnsiString;
-  {$ENDIF}
-  var i: Integer;
-begin
-  {$IFDEF HEADLESS}
-    conbufDumpToStdOut := true;
-  {$ENDIF}
-  for i := 1 to ParamCount do
-  begin
-    case ParamStr(i) of
-      '--con-stdout': conbufDumpToStdOut := true;
-      '--no-fbo': glRenderToFBO := false;
-    end
-  end;
-
-  if LogFileName <> '' then
-    e_InitLog(LogFileName, TWriteMode.WM_NEWFILE);
-  e_InitWritelnDriver();
-  e_WriteLog('Doom 2D: Forever version ' + GAME_VERSION + ' proto ' + IntToStr(NET_PROTOCOL_VER), TMsgType.Notify);
-  e_WriteLog('Build date: ' + GAME_BUILDDATE + ' ' + GAME_BUILDTIME, TMsgType.Notify);
-  e_WriteLog('Build hash: ' + g_GetBuildHash(), TMsgType.Notify);
-  e_WriteLog('Build by: ' + g_GetBuilderName(), TMsgType.Notify);
-
-  e_LogWritefln('Force bin dir: %s', [forceBinDir], TMsgType.Notify);
-  e_LogWritefln('BINARY PATH: [%s]', [binPath], TMsgType.Notify);
-
-  PrintDirs('DataDirs', DataDirs);
-  PrintDirs('ModelDirs', ModelDirs);
-  PrintDirs('MegawadDirs', MegawadDirs);
-  PrintDirs('MapDirs', MapDirs);
-  PrintDirs('WadDirs', WadDirs);
-
-  PrintDirs('LogDirs', LogDirs);
-  PrintDirs('SaveDirs', SaveDirs);
-  PrintDirs('CacheDirs', CacheDirs);
-  PrintDirs('ConfigDirs', ConfigDirs);
-  PrintDirs('ScreenshotDirs', ScreenshotDirs);
-  PrintDirs('StatsDirs', StatsDirs);
-  PrintDirs('MapDownloadDirs', MapDownloadDirs);
-  PrintDirs('WadDownloadDirs', WadDownloadDirs);
-
-  GameWAD := e_FindWad(DataDirs, GameWADName);
-  if GameWad = '' then
-  begin
-    e_WriteLog('WAD ' + GameWADName + ' not found in data directories.', TMsgType.Fatal);
-    {$IF DEFINED(USE_SDL2) AND NOT DEFINED(HEADLESS)}
-      if forceBinDir = false then
-        SDL_ShowSimpleMessageBox(
-          SDL_MESSAGEBOX_ERROR, 
-          'Doom 2D Forever',
-          PChar('WAD ' + GameWADName + ' not found in data directories.'),
-          nil
-        );
-    {$ENDIF}
-    e_DeinitLog;
-    Halt(1);
-  end;
-
-  {$IF DEFINED(ANDROID) AND DEFINED(USE_SDLMIXER)}
-    timiditycfg := 'timidity.cfg';
-    if e_FindResource(ConfigDirs, timiditycfg) = true then
-    begin
-      timiditycfg := ExpandFileName(timiditycfg);
-      SetEnvVar('TIMIDITY_CFG', timiditycfg);
-      e_LogWritefln('Set TIMIDITY_CFG = "%s"', [timiditycfg]);
-    end;
-  {$ENDIF}
-end;
-
-procedure Main();
-{$IFDEF ENABLE_HOLMES}
-  var flexloaded: Boolean;
-{$ENDIF}
-begin
-  InitPath;
-  InitPrep;
-  e_InitInput;
-  sys_Init;
-
-  g_Options_SetDefault;
-  g_Options_SetDefaultVideo;
-  g_Console_SysInit;
-  if sys_SetDisplayMode(gRC_Width, gRC_Height, gBPP, gRC_FullScreen, gRC_Maximized) = False then
-    raise Exception.Create('Failed to set videomode on startup.');
-
-  e_WriteLog(gLanguage, TMsgType.Notify);
-  g_Language_Set(gLanguage);
-
-{$IF not DEFINED(HEADLESS) and DEFINED(ENABLE_HOLMES)}
-  flexloaded := true;
-  if not fuiAddWad('flexui.wad') then
-  begin
-    if not fuiAddWad('./data/flexui.wad') then fuiAddWad('./flexui.wad');
-  end;
-  try
-    fuiGfxLoadFont('win8', 'flexui/fonts/win8.fuifont');
-    fuiGfxLoadFont('win14', 'flexui/fonts/win14.fuifont');
-    fuiGfxLoadFont('win16', 'flexui/fonts/win16.fuifont');
-    fuiGfxLoadFont('dos8', 'flexui/fonts/dos8.fuifont');
-    fuiGfxLoadFont('msx6', 'flexui/fonts/msx6.fuifont');
-  except on e: Exception do
-    begin
-      writeln('ERROR loading FlexUI fonts');
-      flexloaded := false;
-      //raise;
-    end;
-  else
-    begin
-      flexloaded := false;
-      //raise;
-    end;
-  end;
-  if (flexloaded) then
-  begin
-    try
-      e_LogWriteln('FlexUI: loading stylesheet...');
-      uiLoadStyles('flexui/widgets.wgs');
-    except on e: TParserException do
-      begin
-        writeln('ERROR at (', e.tokLine, ',', e.tokCol, '): ', e.message);
-        //raise;
-        flexloaded := false;
-      end;
-    else
-      begin
-        //raise;
-        flexloaded := false;
-      end;
-    end;
-  end;
-  g_holmes_imfunctional := not flexloaded;
-
-  if (not g_holmes_imfunctional) then
-  begin
-    uiInitialize();
-    uiContext.font := 'win14';
-  end;
-
-  if assigned(oglInitCB) then oglInitCB;
-{$ENDIF}
-
-  //g_Res_CreateDatabases(true); // it will be done before connecting to the server for the first time
-
-  e_WriteLog('Entering SDLMain', TMsgType.Notify);
-
-  {$WARNINGS OFF}
-    SDLMain();
-  {$WARNINGS ON}
-
-  {$IFDEF ENABLE_HOLMES}
-    if assigned(oglDeinitCB) then oglDeinitCB;
-  {$ENDIF}
-
-  g_Console_WriteGameConfig;
-  sys_Final;
-end;
-
-procedure Init();
-var
-  NoSound: Boolean;
-begin
-  Randomize;
-
-{$IFDEF HEADLESS}
- {$IFDEF USE_SDLMIXER}
-  NoSound := False; // hope env has set SDL_AUDIODRIVER to dummy
- {$ELSE}
-  NoSound := True; // FMOD backend will sort it out
- {$ENDIF}
-{$ELSE}
-  NoSound := False;
-{$ENDIF}
-
-  g_Touch_Init;
-
-(*
-  if (e_JoysticksAvailable > 0) then
-    e_WriteLog('Input: Joysticks available.', TMsgType.Notify)
-  else
-    e_WriteLog('Input: No Joysticks.', TMsgType.Notify);
-*)
-
-  if (not gNoSound) then
-  begin
-    e_WriteLog('Initializing sound system', TMsgType.Notify);
-    e_InitSoundSystem(NoSound);
-  end;
-
-  e_WriteLog('Init game', TMsgType.Notify);
-  g_Game_Init();
-
-  FillChar(charbuff, sizeof(charbuff), ' ');
-end;
-
-
-procedure Release();
-begin
-  e_WriteLog('Releasing engine', TMsgType.Notify);
-  e_ReleaseEngine();
-
-  e_WriteLog('Releasing input', TMsgType.Notify);
-  e_ReleaseInput();
-
-  if not gNoSound then
-  begin
-    e_WriteLog('Releasing sound', TMsgType.Notify);
-    e_ReleaseSoundSystem();
-  end;
-end;
-
-
-procedure Update ();
-begin
-  // remember old mobj positions, prepare for update
-  g_Game_PreUpdate();
-  // server: receive client commands for new frame
-  // client: receive game state changes from server
-       if (NetMode = NET_SERVER) then g_Net_Host_Update()
-  else if (NetMode = NET_CLIENT) then g_Net_Client_Update();
-  // think
-  g_Game_Update();
-  // server: send any accumulated outgoing data to clients
-  if NetMode = NET_SERVER then g_Net_Flush();
-end;
-
-
-procedure Draw ();
-begin
-  r_Game_Draw();
-end;
-
-
-function Translit (const S: AnsiString): AnsiString;
-var
-  i: Integer;
-begin
-  Result := S;
-  for i := 1 to Length(Result) do
-  begin
-    case Result[i] of
-      'É': Result[i] := 'Q';
-      'Ö': Result[i] := 'W';
-      'Ó': Result[i] := 'E';
-      'Ê': Result[i] := 'R';
-      'Å': Result[i] := 'T';
-      'Í': Result[i] := 'Y';
-      'Ã': Result[i] := 'U';
-      'Ø': Result[i] := 'I';
-      'Ù': Result[i] := 'O';
-      'Ç': Result[i] := 'P';
-      'Õ': Result[i] := '['; //Chr(219);
-      'Ú': Result[i] := ']'; //Chr(221);
-      'Ô': Result[i] := 'A';
-      'Û': Result[i] := 'S';
-      'Â': Result[i] := 'D';
-      'À': Result[i] := 'F';
-      'Ï': Result[i] := 'G';
-      'Ð': Result[i] := 'H';
-      'Î': Result[i] := 'J';
-      'Ë': Result[i] := 'K';
-      'Ä': Result[i] := 'L';
-      'Æ': Result[i] := ';'; //Chr(186);
-      'Ý': Result[i] := #39; //Chr(222);
-      'ß': Result[i] := 'Z';
-      '×': Result[i] := 'X';
-      'Ñ': Result[i] := 'C';
-      'Ì': Result[i] := 'V';
-      'È': Result[i] := 'B';
-      'Ò': Result[i] := 'N';
-      'Ü': Result[i] := 'M';
-      'Á': Result[i] := ','; //Chr(188);
-      'Þ': 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;
-
-end.
index 7ec904be0437189e8670f3079ed3a3ff8481c4e5..eb9454263364990821dbb6ab0bdd5b4bc4ded22c 100644 (file)
@@ -246,7 +246,7 @@ var
 implementation
 
 uses
 implementation
 
 uses
-  e_input, g_main, e_log, e_res, g_items, g_gfx, g_console,
+  e_input, e_log, e_res, g_items, g_gfx, g_console,
   g_weapons, g_game, g_sound, e_sound, CONFIG,
   g_options, g_triggers, g_player,
   Math, g_monsters, g_saveload, g_language, g_netmsg,
   g_weapons, g_game, g_sound, e_sound, CONFIG,
   g_options, g_triggers, g_player,
   Math, g_monsters, g_saveload, g_language, g_netmsg,
index 61da4c4b89857cad02e905c8c9b78429db4b4f77..265bade66424b2a0412cac54634581691f123a91 100644 (file)
@@ -43,7 +43,7 @@ var
 implementation
 
 uses
 implementation
 
 uses
-  g_gui, g_textures, r_graphics, g_main, g_window, g_game, g_map,
+  g_gui, g_textures, r_graphics, g_window, g_game, g_map,
   g_base, g_basic, g_console, g_sound, g_gfx, g_player, g_options, g_weapons,
   e_log, SysUtils, CONFIG, g_playermodel, DateUtils,
   MAPDEF, Math, g_saveload,
   g_base, g_basic, g_console, g_sound, g_gfx, g_player, g_options, g_weapons,
   e_log, SysUtils, CONFIG, g_playermodel, DateUtils,
   MAPDEF, Math, g_saveload,
index 434730b492cccf9c686c0d5c546dd5fa57fa40a3..5824ffe082020edf16f9c29e215005f4e38a789f 100644 (file)
@@ -488,7 +488,7 @@ var
 implementation
 
 uses
 implementation
 
 uses
-  e_log, g_main, g_sound, g_gfx, g_player, g_game,
+  e_log, g_sound, g_gfx, g_player, g_game,
   g_weapons, g_triggers, g_items, g_options,
   g_console, g_map, Math, g_menu, wadreader,
   g_language, g_netmsg, idpool, utils, xstreams;
   g_weapons, g_triggers, g_items, g_options,
   g_console, g_map, Math, g_menu, wadreader,
   g_language, g_netmsg, idpool, utils, xstreams;
index 63e4cb91ed172945f96137fe185d91895a5f6020..2316d1c7e5e77f354434d250c393a516c38660e4 100644 (file)
@@ -254,9 +254,9 @@ implementation
 
 uses
   SysUtils,
 
 uses
   SysUtils,
-  e_input, e_res,
+  e_input, e_res, g_options,
   g_nethandler, g_netmsg, g_netmaster, g_player, g_window, g_console,
   g_nethandler, g_netmsg, g_netmaster, g_player, g_window, g_console,
-  g_main, g_game, g_language, g_weapons, ctypes, g_system, g_map;
+  g_game, g_language, g_weapons, ctypes, g_system, g_map;
 
 const
   FILE_CHUNK_SIZE = 8192;
 
 const
   FILE_CHUNK_SIZE = 8192;
index b5edf0afdcfcc5950293be410d82d0f7b35eb202..c49ffaa6aa6ae4a192465faad13ef82a7e3e3973 100644 (file)
@@ -277,7 +277,7 @@ implementation
 
 uses
   Math, ENet, e_input, e_log, g_base, g_basic,
 
 uses
   Math, ENet, e_input, e_log, g_base, g_basic,
-  g_textures, g_gfx, g_sound, g_console, g_options, g_main,
+  g_textures, g_gfx, g_sound, g_console, g_options,
   g_game, g_player, g_map, g_panel, g_items, g_weapons, g_phys, g_gui,
   g_language, g_monsters, g_netmaster, utils, wadreader, MAPDEF;
 
   g_game, g_player, g_map, g_panel, g_items, g_weapons, g_phys, g_gui,
   g_language, g_monsters, g_netmaster, utils, wadreader, MAPDEF;
 
index 487dbd22b9c286bc2ebaea676535b69d6fa2d8d8..f1f0653493f8ba86a2b3c107d89c75871090f0fd 100644 (file)
@@ -86,6 +86,28 @@ var
   g_dbg_scale: Single = 1.0;
   r_pixel_scale: Single = 1.0;
 
   g_dbg_scale: Single = 1.0;
   r_pixel_scale: Single = 1.0;
 
+  {--- Read-only dirs ---}
+  GameWAD: string;
+  DataDirs: SSArray;
+  ModelDirs: SSArray;
+  MegawadDirs: SSArray;
+  MapDirs: SSArray;
+  WadDirs: SSArray;
+  AllMapDirs: SSArray; // Maps + Megawads
+
+  {--- Read-Write dirs ---}
+  LogFileName: string;
+  LogDirs: SSArray;
+  SaveDirs: SSArray;
+  CacheDirs: SSArray;
+  ConfigDirs: SSArray;
+  ScreenshotDirs: SSArray;
+  StatsDirs: SSArray;
+  MapDownloadDirs: SSArray;
+  WadDownloadDirs: SSArray;
+
+  GameWADName: string = 'GAME';
+
 implementation
 
 uses
 implementation
 
 uses
@@ -93,7 +115,7 @@ uses
     SDL2,
   {$ENDIF}
   e_log, e_input, g_console, g_window, g_sound, g_gfx, g_player, Math,
     SDL2,
   {$ENDIF}
   e_log, e_input, g_console, g_window, g_sound, g_gfx, g_player, Math,
-  g_map, g_net, g_netmaster, SysUtils, CONFIG, g_game, g_main,
+  g_map, g_net, g_netmaster, SysUtils, CONFIG, g_game,
   g_items, wadreader, g_touch, envvars, g_system;
 
   var
   g_items, wadreader, g_touch, envvars, g_system;
 
   var
index e2e9e97e645187117633e7b6d47fbeb930d41eeb..dab24aed016a61ee69982a742f4aac794d9b6e17 100644 (file)
@@ -637,7 +637,7 @@ uses
 {$ENDIF}
   e_log, g_map, g_items, g_console, g_gfx, Math,
   g_options, g_triggers, g_menu, g_game, g_grid, e_res,
 {$ENDIF}
   e_log, g_map, g_items, g_console, g_gfx, Math,
   g_options, g_triggers, g_menu, g_game, g_grid, e_res,
-  wadreader, g_main, g_monsters, CONFIG, g_language,
+  wadreader, g_monsters, CONFIG, g_language,
   g_net, g_netmsg, g_window,
   utils, xstreams;
 
   g_net, g_netmsg, g_window,
   utils, xstreams;
 
index 9b018cb216d53d5dc161702bace1ed287250be0f..b2f57e6c95997ff2835acc80f58380b4ec6827ca 100644 (file)
@@ -167,7 +167,7 @@ function  g_PlayerModel_GetGibs(ModelName: String; var Gibs: TGibsArray): Boolea
 implementation
 
 uses
 implementation
 
 uses
-  g_main, g_sound, g_console, SysUtils, g_player, CONFIG,
+  g_sound, g_console, SysUtils, g_player, CONFIG,
   e_sound, g_options, g_map, Math, e_log, wadreader;
 
 type
   e_sound, g_options, g_map, Math, e_log, wadreader;
 
 type
index 7fc295119c4cdada4b8e7b98f8db5fb2026f6f6e..8bd5e04cd70ddd6dcd87e2be585261c3e067c480 100644 (file)
@@ -17,7 +17,7 @@ unit g_res_downloader;
 
 interface
 
 
 interface
 
-uses sysutils, Classes, md5, g_net, g_netmsg, g_console, g_main, e_log;
+uses sysutils, Classes, md5, g_net, g_netmsg, g_console, e_log;
 
 
 // download map wad from server (if necessary)
 
 
 // download map wad from server (if necessary)
@@ -35,7 +35,7 @@ procedure g_Res_CreateDatabases (allowRescan: Boolean=false);
 
 implementation
 
 
 implementation
 
-uses g_language, sfs, utils, wadreader, g_game, hashtable, fhashdb, e_res;
+uses g_language, sfs, utils, wadreader, g_game, hashtable, fhashdb, e_res, g_options;
 
 var
   // cvars
 
 var
   // cvars
index eb1a79a8db17dd725634f3e4f8ae28319a6e2493..32f6821770c20af0affaf213d2b5ee23853e6cc2 100644 (file)
@@ -38,9 +38,9 @@ implementation
 uses
   MAPDEF, utils, xstreams,
   g_game, g_items, g_map, g_monsters, g_triggers,
 uses
   MAPDEF, utils, xstreams,
   g_game, g_items, g_map, g_monsters, g_triggers,
-  g_basic, g_main, Math, wadreader,
+  g_basic, Math, wadreader,
   g_weapons, g_player, g_console,
   g_weapons, g_player, g_console,
-  e_log, e_res, g_language;
+  e_log, e_res, g_language, g_options;
 
 const
   SAVE_SIGNATURE = $56534644; // 'DFSV'
 
 const
   SAVE_SIGNATURE = $56534644; // 'DFSV'
index f89aa9f3de7f89d23c63367c846260100139e9a3..e8891a06467dffaf806ba96cb96dabc9dbe824b1 100644 (file)
@@ -105,7 +105,7 @@ uses
   Math,
   g_player, g_map, g_panel, g_gfx, g_game, g_textures,
   g_console, g_monsters, g_items, g_phys, g_weapons,
   Math,
   g_player, g_map, g_panel, g_gfx, g_game, g_textures,
   g_console, g_monsters, g_items, g_phys, g_weapons,
-  wadreader, g_main, e_log, g_language, e_res,
+  wadreader, e_log, g_language, e_res,
   g_options, g_net, g_netmsg, utils, xparser, xstreams;
 
 const
   g_options, g_net, g_netmsg, utils, xparser, xstreams;
 
 const
index 21c580375a533aa95e6f2dc6b8e9e0791bb52c27..81767e653f03be80e7d7a5b58fb7dd0970db4413 100644 (file)
@@ -113,7 +113,7 @@ var
 implementation
 
 uses
 implementation
 
 uses
-  Math, g_map, g_player, g_gfx, g_sound, g_main, g_panel,
+  Math, g_map, g_player, g_gfx, g_sound, g_panel,
   g_console, g_options, g_game,
   g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
   g_language, g_netmsg, g_grid,
   g_console, g_options, g_game,
   g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
   g_language, g_netmsg, g_grid,
index 4e84b5d53eb9823a4b5234c44a38cbbc99ca9077..bfd4377830c271bc278fe5480357782093cec5f7 100644 (file)
@@ -37,8 +37,8 @@ uses
 {$IFDEF ENABLE_HOLMES}
   g_holmes, sdlcarcass, fui_ctls,
 {$ENDIF}
 {$IFDEF ENABLE_HOLMES}
   g_holmes, sdlcarcass, fui_ctls,
 {$ENDIF}
-  SysUtils, Classes, MAPDEF, Math,
-  r_window, e_log, g_main,
+  SysUtils, Classes, MAPDEF, Math, r_graphics,
+  r_window, e_log, r_game,
   g_console, r_console, e_input, g_options, g_game,
   g_basic, g_textures, e_sound, g_sound, g_menu, ENet, g_net,
   g_map, g_gfx, g_monsters, xprofiler,
   g_console, r_console, e_input, g_options, g_game,
   g_basic, g_textures, e_sound, g_sound, g_menu, ENet, g_net,
   g_map, g_gfx, g_monsters, xprofiler,
@@ -51,6 +51,79 @@ var
   wNeedTimeReset: Boolean = false;
   wLoadingQuit: Boolean = false;
 
   wNeedTimeReset: Boolean = false;
   wLoadingQuit: Boolean = false;
 
+procedure Update ();
+begin
+  // remember old mobj positions, prepare for update
+  g_Game_PreUpdate();
+  // server: receive client commands for new frame
+  // client: receive game state changes from server
+       if (NetMode = NET_SERVER) then g_Net_Host_Update()
+  else if (NetMode = NET_CLIENT) then g_Net_Client_Update();
+  // think
+  g_Game_Update();
+  // server: send any accumulated outgoing data to clients
+  if NetMode = NET_SERVER then g_Net_Flush();
+end;
+
+
+procedure Draw ();
+begin
+  r_Game_Draw();
+end;
+
+
+procedure Init();
+var
+  NoSound: Boolean;
+begin
+  Randomize;
+
+{$IFDEF HEADLESS}
+ {$IFDEF USE_SDLMIXER}
+  NoSound := False; // hope env has set SDL_AUDIODRIVER to dummy
+ {$ELSE}
+  NoSound := True; // FMOD backend will sort it out
+ {$ENDIF}
+{$ELSE}
+  NoSound := False;
+{$ENDIF}
+
+  g_Touch_Init;
+
+(*
+  if (e_JoysticksAvailable > 0) then
+    e_WriteLog('Input: Joysticks available.', TMsgType.Notify)
+  else
+    e_WriteLog('Input: No Joysticks.', TMsgType.Notify);
+*)
+
+  if (not gNoSound) then
+  begin
+    e_WriteLog('Initializing sound system', TMsgType.Notify);
+    e_InitSoundSystem(NoSound);
+  end;
+
+  e_WriteLog('Init game', TMsgType.Notify);
+  g_Game_Init();
+
+//  FillChar(charbuff, sizeof(charbuff), ' ');
+end;
+
+procedure Release();
+begin
+  e_WriteLog('Releasing engine', TMsgType.Notify);
+  e_ReleaseEngine();
+
+  e_WriteLog('Releasing input', TMsgType.Notify);
+  e_ReleaseInput();
+
+  if not gNoSound then
+  begin
+    e_WriteLog('Releasing sound', TMsgType.Notify);
+    e_ReleaseSoundSystem();
+  end;
+end;
+
 procedure ResetTimer ();
 begin
   wNeedTimeReset := true;
 procedure ResetTimer ();
 begin
   wNeedTimeReset := true;
index e7a0b6a81e28522ef7b1c2e9e6c3f64998629065..1d5081efaa2975ed967e95eff00af459d3b87533 100644 (file)
@@ -25,9 +25,9 @@ implementation
 
   uses
     SysUtils, Classes, Math,
 
   uses
     SysUtils, Classes, Math,
-    e_log, r_graphics,
+    e_log, r_graphics, g_options,
     conbuf,
     conbuf,
-    g_base, g_main, g_console, g_game, g_menu, g_textures
+    g_base, g_console, g_game, g_menu, g_textures
   ;
 
 (* ====== Console ====== *)
   ;
 
 (* ====== Console ====== *)
index 9b0f244eed5d4ea446a524f161f5b1386125f1a4..2f40e9fea9f8aa6cf7c8769c46105ee2c8b55cb1 100644 (file)
@@ -28,8 +28,8 @@ implementation
   uses
     SysUtils, Classes, Math,
     MAPDEF,
   uses
     SysUtils, Classes, Math,
     MAPDEF,
-    r_graphics,
-    g_base, g_basic, g_map, g_weapons, g_textures, g_main
+    r_graphics, g_options,
+    g_base, g_basic, g_map, g_weapons, g_textures
   ;
 
   const
   ;
 
   const
index fe7225f6573ebfdaa68a1649600b74e12b600b66..7527073c1ad7e0ad811001cf519c7bcf2476bd46 100644 (file)
@@ -37,13 +37,16 @@ interface
   procedure sys_Init;
   procedure sys_Final;
 
   procedure sys_Init;
   procedure sys_Final;
 
+  var (* hooks *)
+    sys_CharPress: procedure (ch: AnsiChar) = nil;
+
 implementation
 
   uses
     SysUtils, SDL, Math,
     {$INCLUDE ../nogl/noGLuses.inc}
     e_log, r_graphics, e_input, e_sound,
 implementation
 
   uses
     SysUtils, SDL, Math,
     {$INCLUDE ../nogl/noGLuses.inc}
     e_log, r_graphics, e_input, e_sound,
-    g_options, g_window, g_console, g_game, g_menu, g_gui, g_main, g_basic;
+    g_options, g_window, g_console, g_game, g_menu, g_gui, g_basic;
 
   const
     GameTitle = 'Doom 2D: Forever (SDL 1.2, %s)';
 
   const
     GameTitle = 'Doom 2D: Forever (SDL 1.2, %s)';
@@ -453,8 +456,9 @@ implementation
     begin
       g_Console_ProcessBindRepeat(key)
     end;
     begin
       g_Console_ProcessBindRepeat(key)
     end;
-    if down and IsValid1251(ev.keysym.unicode) and IsPrintable1251(ch) then
-      CharPress(ch)
+    if @sys_CharPress <> nil then
+      if down and IsValid1251(ev.keysym.unicode) and IsPrintable1251(ch) then
+        sys_CharPress(ch)
   end;
 
   procedure HandleResize (var ev: TSDL_ResizeEvent);
   end;
 
   procedure HandleResize (var ev: TSDL_ResizeEvent);
index 40e0dff752b0b90b3ac59a9ebf9246cb96314896..fd79aa60e30f651f9ecda934dcc1d90bf1cbc674 100644 (file)
@@ -37,6 +37,9 @@ interface
   procedure sys_Init;
   procedure sys_Final;
 
   procedure sys_Init;
   procedure sys_Final;
 
+  var (* hooks *)
+    sys_CharPress: procedure (ch: AnsiChar) = nil;
+
 implementation
 
   uses
 implementation
 
   uses
@@ -46,7 +49,7 @@ implementation
     {$IFDEF ENABLE_HOLMES}
       g_holmes, sdlcarcass, fui_ctls,
     {$ENDIF}
     {$IFDEF ENABLE_HOLMES}
       g_holmes, sdlcarcass, fui_ctls,
     {$ENDIF}
-    g_touch, g_options, g_window, g_console, g_game, g_menu, g_gui, g_main, g_basic;
+    g_touch, g_options, g_window, g_console, g_game, g_menu, g_gui, g_basic;
 
   const
     GameTitle = 'Doom 2D: Forever (SDL 2, %s)';
 
   const
     GameTitle = 'Doom 2D: Forever (SDL 2, %s)';
@@ -522,8 +525,9 @@ implementation
     sch := AnsiChar(wchar2win(ch));
     if g_dbg_input then
       e_LogWritefln('Input Debug: text, text="%s", ch = %s, sch = %s', [ev.text, Ord(ch), Ord(sch)]);
     sch := AnsiChar(wchar2win(ch));
     if g_dbg_input then
       e_LogWritefln('Input Debug: text, text="%s", ch = %s, sch = %s', [ev.text, Ord(ch), Ord(sch)]);
-    if IsValid1251(Word(ch)) and IsPrintable1251(ch) then
-      CharPress(sch);
+    if @sys_CharPress <> nil then
+      if IsValid1251(Word(ch)) and IsPrintable1251(ch) then
+        sys_CharPress(sch)
   end;
 
   function sys_HandleInput (): Boolean;
   end;
 
   function sys_HandleInput (): Boolean;
index c6a9e232048d3e5b670ad063c7f92a4753e990a9..12a46645989b8d016dd6cc056c3aeab61e4ad935 100644 (file)
@@ -36,7 +36,7 @@ implementation
 
   uses
     SysUtils,
 
   uses
     SysUtils,
-    e_log, r_graphics, e_input, g_options, g_game, g_main, g_gui, g_weapons, g_console, g_window;
+    e_log, r_graphics, e_input, g_options, g_game, g_gui, g_weapons, g_console, g_window;
 
   var
     angleFire: Boolean;
 
   var
     angleFire: Boolean;
index c3b4b23e0ee43fbacdec4756a62f4bdd782c4dcd..4e4f42377e3da2e59abb09b1967ce97add6ad01f 100644 (file)
@@ -37,6 +37,9 @@ interface
   procedure sys_Init;
   procedure sys_Final;
 
   procedure sys_Init;
   procedure sys_Final;
 
+  var (* hooks *)
+    sys_CharPress: procedure (ch: AnsiChar) = nil;
+
 implementation
 
   uses SysUtils;
 implementation
 
   uses SysUtils;