<commit_before>unit KM_FormMain;
{$I KaM_Remake.inc}
interface
uses
  Classes, ComCtrls, Controls, Buttons, Dialogs, ExtCtrls, Forms, Graphics, Math, Menus, StdCtrls, SysUtils, StrUtils,
  KM_RenderControl, KM_Settings, KM_Video, KM_CommonTypes,
  KM_GameTypes,
  {$IFDEF FPC} LResources, {$ENDIF}
  {$IFDEF MSWindows} ShellAPI, Windows, Messages; {$ENDIF}
  {$IFDEF Unix} LCLIntf, LCLType; {$ENDIF}


type

  { TFormMain }

  TFormMain = class(TForm)
    chkAIEye: TCheckBox;
    chkLogGameTick: TCheckBox;
    MenuItem1: TMenuItem;
    SaveEditableMission1: TMenuItem;
    N2: TMenuItem;
    OpenDialog1: TOpenDialog;
    StatusBar1: TStatusBar;
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    Exit1: TMenuItem;
    N1: TMenuItem;
    About1: TMenuItem;
    Debug1: TMenuItem;
    Debug_PrintScreen: TMenuItem;
    Export1: TMenuItem;
    Export_GUIRX: TMenuItem;
    Export_TreesRX: TMenuItem;
    Export_HousesRX: TMenuItem;
    Export_UnitsRX: TMenuItem;
    Export_GUIMainRX: TMenuItem;
    Export_Custom: TMenuItem;
    Export_Tileset: TMenuItem;
    Export_Fonts1: TMenuItem;
    chkSuperSpeed: TCheckBox;
    Export_Deliverlists1: TMenuItem;
    Export_Sounds1: TMenuItem;
    Export_HouseAnim1: TMenuItem;
    Export_UnitAnim1: TMenuItem;
    RGPlayer: TRadioGroup;
    Button_Stop: TButton;
    OpenMissionMenu: TMenuItem;
    AnimData1: TMenuItem;
    Other1: TMenuItem;
    Debug_ShowPanel: TMenuItem;
    Export_TreeAnim1: TMenuItem;
    ExportMainMenu: TMenuItem;
    Debug_EnableCheats: TMenuItem;
    ExportUIPages: TMenuItem;
    Resources1: TMenuItem;
    HousesDat1: TMenuItem;
    chkShowOwnership: TCheckBox;
    chkShowNavMesh: TCheckBox;
    chkShowAvoid: TCheckBox;
    chkShowBalance: TCheckBox;
    tbOwnMargin: TTrackBar;
    tbOwnThresh: TTrackBar;
    Label5: TLabel;
    Label6: TLabel;
    chkShowDefences: TCheckBox;
    chkBuildAI: TCheckBox;
    chkCombatAI: TCheckBox;
    ResourceValues1: TMenuItem;
    chkUIControlsBounds: TCheckBox;
    chkUITextBounds: TCheckBox;
    tbAngleX: TTrackBar;
    tbAngleY: TTrackBar;
    Label3: TLabel;
    Label4: TLabel;
    tbBuildingStep: TTrackBar;
    Label1: TLabel;
    tbPassability: TTrackBar;
    Label2: TLabel;
    chkShowRoutes: TCheckBox;
    chkShowWires: TCheckBox;
    tbAngleZ: TTrackBar;
    Label7: TLabel;
    chkSelectionBuffer: TCheckBox;
    chkLogDelivery: TCheckBox;
    chkLogNetConnection: TCheckBox;
    RGLogNetPackets: TRadioGroup;
    chkLogsShowInChat: TCheckBox;
    chkUIControlsID: TCheckBox;
    Debug_ShowLogistics: TMenuItem;
    chkShowTerrainIds: TCheckBox;
    chkShowTerrainKinds: TCheckBox;
    UnitAnim_All: TMenuItem;
    N3: TMenuItem;
    Soldiers: TMenuItem;
    Civilians1: TMenuItem;
    SaveSettings: TMenuItem;
    N4: TMenuItem;
    ReloadSettings: TMenuItem;
    SaveDialog1: TSaveDialog;
    chkLogCommands: TCheckBox;
    ScriptData1: TMenuItem;
    chkBevel: TCheckBox;
    chkTilesGrid: TCheckBox;
    N6: TMenuItem;
    GameStats: TMenuItem;
    ExportGameStats: TMenuItem;
    ValidateGameStats: TMenuItem;
    chkLogRngChecks: TCheckBox;
    chkShowGameTick: TCheckBox;
    chkSkipRender: TCheckBox;
    chkSkipSound: TCheckBox;
    chkUIDs: TCheckBox;
    chkShowSoil: TCheckBox;
    chkShowFlatArea: TCheckBox;
    chkShowEyeRoutes: TCheckBox;
    chkSelectedObjInfo: TCheckBox;
    chkShowFPS: TCheckBox;
    chkHands: TCheckBox;
    btnUpdateUI: TButton;
    {$IFDEF WDC}
    mainGroup: TCategoryPanelGroup;
    cpGameControls: TCategoryPanel;
    cpDebugRender: TCategoryPanel;
    cpAI: TCategoryPanel;
    cpUserInreface: TCategoryPanel;
    cpGraphicTweaks: TCategoryPanel;
    cpLogs: TCategoryPanel;
    cpGameAdv: TCategoryPanel;
    cpPerfLogs: TCategoryPanel;
    chkSnowHouses: TCheckBox;
    chkLoadUnsupSaves: TCheckBox;
    chkJamMeter: TCheckBox;
    chkShowTerrainOverlays: TCheckBox;
    {$ENDIF}
    {$IFDEF FPC}
    mainGroup: TGroupBox;
    GroupBox2: TGroupBox;
    GroupBox3: TGroupBox;
    GroupBox4: TGroupBox;
    GroupBox5: TGroupBox;
    GroupBoxLogs: TGroupBox;
    {$ENDIF}
    N5: TMenuItem;
    LoadSavThenRpl: TMenuItem;
    N7: TMenuItem;
    ReloadLibx: TMenuItem;
    N8: TMenuItem;
    N10: TMenuItem;
    N9: TMenuItem;
    Debug_UnlockCmpMissions: TMenuItem;
    N11: TMenuItem;
    mnExportRngChecks: TMenuItem;

    procedure Export_TreeAnim1Click(Sender: TObject);
    procedure MenuItem1Click(Sender: TObject);
    procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure Debug_ExportMenuClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
    procedure Debug_EnableCheatsClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure AboutClick(Sender: TObject);
    procedure ExitClick(Sender: TObject);
    procedure Debug_PrintScreenClick(Sender: TObject);
    procedure Export_TreesRXClick(Sender: TObject);
    procedure Export_HousesRXClick(Sender: TObject);
    procedure Export_UnitsRXClick(Sender: TObject);
    procedure Export_ScriptDataClick(Sender: TObject);
    procedure Export_GUIClick(Sender: TObject);
    procedure Export_GUIMainRXClick(Sender: TObject);
    procedure Export_CustomClick(Sender: TObject);
    procedure Export_TilesetClick(Sender: TObject);
    procedure Export_Sounds1Click(Sender: TObject);
    procedure Export_HouseAnim1Click(Sender: TObject);
    procedure Export_Fonts1Click(Sender: TObject);
    procedure Export_DeliverLists1Click(Sender: TObject);
    procedure Button_StopClick(Sender: TObject);
    procedure RGPlayerClick(Sender: TObject);
    procedure Open_MissionMenuClick(Sender: TObject);
    procedure chkSuperSpeedClick(Sender: TObject);
    procedure Debug_ShowPanelClick(Sender: TObject);
    procedure FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure Debug_ExportUIPagesClick(Sender: TObject);
    procedure HousesDat1Click(Sender: TObject);
    procedure ExportGameStatsClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure ResourceValues1Click(Sender: TObject);
    procedure RenderAreaMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure RenderAreaMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
    procedure RenderAreaMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure RenderAreaMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure RenderAreaResize(aWidth, aHeight: Integer);
    procedure RenderAreaRender(aSender: TObject);
    procedure Debug_ShowLogisticsClick(Sender: TObject);
    procedure UnitAnim_AllClick(Sender: TObject);
    procedure SoldiersClick(Sender: TObject);
    procedure Civilians1Click(Sender: TObject);
    procedure ReloadSettingsClick(Sender: TObject);
    procedure SaveSettingsClick(Sender: TObject);
    procedure SaveEditableMission1Click(Sender: TObject);
    procedure ValidateGameStatsClick(Sender: TObject);
    procedure Button_UpdateUI_Click(Sender: TObject);
    procedure LoadSavThenRplClick(Sender: TObject);
    procedure ReloadLibxClick(Sender: TObject);
    procedure Debug_UnlockCmpMissionsClick(Sender: TObject);
    procedure mnExportRngChecksClick(Sender: TObject);

    procedure ControlsUpdate(Sender: TObject);
  private
    fShowStartVideo: Boolean;
    fUpdating: Boolean;
    fMissionDefOpenPath: UnicodeString;
    fOnControlsUpdated: TEvent;
    procedure FormKeyDownProc(aKey: Word; aShift: TShiftState);
    procedure FormKeyUpProc(aKey: Word; aShift: TShiftState);
    function ConfirmExport: Boolean;
    function GetMouseWheelStepsCnt(aWheelData: Integer): Integer;
    {$IFDEF MSWindows}
    function GetWindowParams: TKMWindowParamsRecord;
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
    procedure WMExitSizeMove(var Msg: TMessage) ; message WM_EXITSIZEMOVE;
    procedure WMAppCommand(var Msg: TMessage); message WM_APPCOMMAND;
    procedure WMMouseWheel(var Msg: TMessage); message WM_MOUSEWHEEL;
  protected
    procedure WndProc(var Message : TMessage); override;
    {$ENDIF}
  public
    RenderArea: TKMRenderControl;
    SuppressAltForMenu: Boolean; //Suppress Alt key 'activate window menu' function
    procedure ControlsSetVisibile(aShowCtrls, aShowGroupBox: Boolean); overload;
    procedure ControlsSetVisibile(aShowCtrls: Boolean); overload;
    procedure ControlsReset;
    procedure ControlsRefill;
    procedure ToggleFullscreen(aFullscreen, aWindowDefaultParams: Boolean);
    procedure SetSaveEditableMission(aEnabled: Boolean);
    procedure SetExportGameStats(aEnabled: Boolean);
    procedure ShowFolderPermissionError;
    property OnControlsUpdated: TEvent read fOnControlsUpdated write fOnControlsUpdated;
  end;


implementation
//{$IFDEF WDC}
  {$R *.dfm}
//{$ENDIF}

uses
  {$IFDEF WDC} UITypes, {$ENDIF}
  KromUtils,
  KromShellUtils,
  KM_Defaults,
  KM_Main,
  //Use these units directly to avoid pass-through methods in fMain
  KM_Resource,
  KM_ResSprites,
  KM_ResTexts,
  KM_GameApp,
  KM_HandsCollection,
  KM_ResSound,
  KM_Pics,
  KM_RenderPool,
  KM_Hand,
  KM_ResKeys, KM_FormLogistics, KM_Game,
  KM_RandomChecks,
  KM_Log, KM_CommonClasses, KM_DevPerfLog;


//Remove VCL panel and use flicker-free TMyPanel instead
procedure TFormMain.FormCreate(Sender: TObject);
begin
  fShowStartVideo := True;
  RenderArea := TKMRenderControl.Create(Self);
  RenderArea.Parent := Self;
  RenderArea.Align := alClient;
  RenderArea.Color := clMaroon;
  RenderArea.OnMouseDown := RenderAreaMouseDown;
  RenderArea.OnMouseMove := RenderAreaMouseMove;
  RenderArea.OnMouseUp := RenderAreaMouseUp;
  RenderArea.OnResize := RenderAreaResize;
  RenderArea.OnRender := RenderAreaRender;
  SuppressAltForMenu := False;

  chkSuperSpeed.Caption := 'Speed x' + IntToStr(DEBUG_SPEEDUP_SPEED);

  //Lazarus needs OnMouseWheel event to be for the panel, not the entire form
  {$IFDEF FPC} RenderArea.OnMouseWheel := RenderAreaMouseWheel; {$ENDIF}

  {$IFDEF MSWindows}
    //Means it will receive WM_SIZE WM_PAINT always in pair (if False - WM_PAINT is not called if size becames smaller)
    RenderArea.FullRepaint := True;
    RenderArea.BevelOuter := bvNone;
  {$ENDIF}

  //Put debug panel on top
  {$IFDEF WDC}
  RenderArea.BringToFront;
  mainGroup.SendToBack;
  StatusBar1.SendToBack;
  {$ENDIF}
  {$IFDEF FPC}
  RenderArea.SendToBack;
  mainGroup.BringToFront;
  {$ENDIF}
end;

procedure TFormMain.FormShow(Sender: TObject);
var BordersWidth, BordersHeight: Integer;
begin
  //We do this in OnShow rather than OnCreate as the window borders aren't
  //counted properly in OnCreate
  BordersWidth := Width - ClientWidth;
  BordersHeight := Height - ClientHeight;
  //Constraints includes window borders, so we add them on as Margin
  Constraints.MinWidth := MIN_RESOLUTION_WIDTH + BordersWidth;
  Constraints.MinHeight := MIN_RESOLUTION_HEIGHT + BordersHeight;

  // We have to put it here, to proper window positioning for multimonitor systems
  if not gMain.Settings.FullScreen then
  begin
    Left := gMain.Settings.WindowParams.Left;
    Top := gMain.Settings.WindowParams.Top;
  end;

  fMissionDefOpenPath := ExeDir;

  Application.ProcessMessages;

  if fShowStartVideo and (gGameApp.GameSettings <> nil) and gGameApp.GameSettings.VideoStartup then
  begin
    gVideoPlayer.AddVideo('Campaigns\The Shattered Kingdom\Logo');
    gVideoPlayer.AddVideo('KaM');
    gVideoPlayer.Play;
  end;
  fShowStartVideo := False;
end;


procedure TFormMain.SetSaveEditableMission(aEnabled: Boolean);
begin
  SaveEditableMission1.Enabled := aEnabled;
end;


procedure TFormMain.SetExportGameStats(aEnabled: Boolean);
begin
  ExportGameStats.Enabled := aEnabled;
end;


procedure TFormMain.FormKeyDownProc(aKey: Word; aShift: TShiftState);
begin
  if aKey = gResKeys[SC_DEBUG_WINDOW].Key then
  begin
    SHOW_DEBUG_CONTROLS := not SHOW_DEBUG_CONTROLS;
    ControlsSetVisibile(SHOW_DEBUG_CONTROLS, not (ssCtrl in aShift)); //Hide groupbox when Ctrl is pressed
  end;

  if gGameApp <> nil then gGameApp.KeyDown(aKey, aShift);
end;


procedure TFormMain.FormKeyUpProc(aKey: Word; aShift: TShiftState);
begin
  if gGameApp <> nil then gGameApp.KeyUp(aKey, aShift);
end;


procedure TFormMain.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  Assert(KeyPreview, 'MainForm should recieve all keys to pass them to fGame');
  FormKeyDownProc(Key, Shift);
end;


procedure TFormMain.FormKeyPress(Sender: TObject; var Key: Char);
begin
  Assert(KeyPreview, 'MainForm should recieve all keys to pass them to fGame');
  if gGameApp <> nil then gGameApp.KeyPress(Key);
end;


procedure TFormMain.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  Assert(KeyPreview, 'MainForm should recieve all keys to pass them to fGame');

  FormKeyUpProc(Key, Shift);
end;


procedure TFormMain.ReloadLibxClick(Sender: TObject);
begin
  gRes.LoadLocaleAndFonts(gGameApp.GameSettings.Locale, gGameApp.GameSettings.LoadFullFonts);
end;


procedure TFormMain.ReloadSettingsClick(Sender: TObject);
begin
  gMain.Settings.ReloadSettings;
  gGameApp.GameSettings.ReloadSettings;
end;


procedure TFormMain.RenderAreaMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  // Handle middle mouse button as Key
  if Button = mbMiddle then
    FormKeyDownProc(VK_MBUTTON, Shift)
  else if gGameApp <> nil then
    gGameApp.MouseDown(Button, Shift, X, Y);
end;


procedure TFormMain.RenderAreaMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
begin
  if gGameApp <> nil then gGameApp.MouseMove(Shift, X, Y);
end;


procedure TFormMain.RenderAreaMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if gGameApp <> nil then
  begin
    //Somehow Shift state does not contain mouse buttons ssLeft/ssRight/ssMiddle
    if Button = mbLeft then
      Include(Shift, ssLeft)
    else if Button = mbRight then
      Include(Shift, ssRight)
    else if Button = mbMiddle then
      Include(Shift, ssMiddle);

    // Handle middle mouse button as Key
    if Button = mbMiddle then
      FormKeyUpProc(VK_MBUTTON, Shift)
    else
      gGameApp.MouseUp(Button, Shift, X, Y);
  end;
end;


procedure TFormMain.RenderAreaMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
  if gGameApp <> nil then
    gGameApp.MouseWheel(Shift, WheelDelta, MousePos.X, MousePos.Y);
end;


procedure TFormMain.RenderAreaResize(aWidth, aHeight: Integer);
begin
  gMain.Resize(aWidth, aHeight, GetWindowParams);
end;


procedure TFormMain.RenderAreaRender(aSender: TObject);
begin
  gMain.Render;
end;


//Open
procedure TFormMain.Open_MissionMenuClick(Sender: TObject);
begin
  if RunOpenDialog(OpenDialog1, '', fMissionDefOpenPath, 'Knights & Merchants Mission (*.dat)|*.dat') then
  begin
    gGameApp.NewSingleMap(OpenDialog1.FileName, TruncateExt(ExtractFileName(OpenDialog1.FileName)));
    fMissionDefOpenPath := ExtractFileDir(OpenDialog1.FileName);
  end;
end;


procedure TFormMain.MenuItem1Click(Sender: TObject);
begin
  if RunOpenDialog(OpenDialog1, '', fMissionDefOpenPath, 'Knights & Merchants Mission (*.dat)|*.dat') then
  begin
    gGameApp.NewMapEditor(OpenDialog1.FileName);
    fMissionDefOpenPath := ExtractFileDir(OpenDialog1.FileName);
  end;
end;


procedure TFormMain.mnExportRngChecksClick(Sender: TObject);
var
  rngLogger: TKMRandomCheckLogger;
begin
  if RunOpenDialog(OpenDialog1, '', ExeDir, 'KaM Remake Random checks log (*.rng)|*.rng') then
  begin
    rngLogger := TKMRandomCheckLogger.Create;

    rngLogger.LoadFromPathAndParseToDict(OpenDialog1.FileName);
    rngLogger.SaveAsText(OpenDialog1.FileName + '.log');

    rngLogger.Free;
  end;
end;


procedure TFormMain.SaveEditableMission1Click(Sender: TObject);
begin
  if gGameApp.Game = nil then Exit;

  if not gGameApp.Game.IsMapEditor then Exit;

  if RunSaveDialog(SaveDialog1, gGameApp.Game.MapEditor.MissionDefSavePath, ExtractFileDir(gGameApp.Game.MapEditor.MissionDefSavePath), 'Knights & Merchants Mission (*.dat)|*.dat') then
    gGameApp.SaveMapEditor(SaveDialog1.FileName);
end;


//Exit
procedure TFormMain.ExitClick(Sender: TObject);
begin
  Close;
end;


//About
procedure TFormMain.AboutClick(Sender: TObject);
begin
  gMain.ShowAbout;
end;


//Debug Options
procedure TFormMain.Debug_EnableCheatsClick(Sender: TObject);
begin
  Debug_EnableCheats.Checked := not Debug_EnableCheats.Checked;
  DEBUG_CHEATS := Debug_EnableCheats.Checked;
end;


procedure TFormMain.Debug_PrintScreenClick(Sender: TObject);
begin
  if gGameApp <> nil then
    gGameApp.PrintScreen;
end;


procedure TFormMain.Debug_ShowPanelClick(Sender: TObject);
begin
  mainGroup.Visible := not mainGroup.Visible;
end;


procedure TFormMain.Debug_UnlockCmpMissionsClick(Sender: TObject);
begin
  case MessageDlg(Format(gResTexts[TX_MENU_DEBUG_UNLOCK_CAMPAIGNS_CONFIRM], [ExeDir + 'Saves']), mtWarning, [mbYes, mbNo], 0) of
    mrYes:  begin
              Debug_UnlockCmpMissions.Checked := not Debug_UnlockCmpMissions.Checked;
              UNLOCK_CAMPAIGN_MAPS := Debug_UnlockCmpMissions.Checked;
              if UNLOCK_CAMPAIGN_MAPS then
                gGameApp.UnlockAllCampaigns;
            end;
  end;
end;

//Exports
procedure TFormMain.Export_TreesRXClick(Sender: TObject);
begin
  if ConfirmExport then
    gRes.Sprites.ExportToPNG(rxTrees);
end;

procedure TFormMain.Export_HousesRXClick(Sender: TObject);
begin
  if ConfirmExport then
    gRes.Sprites.ExportToPNG(rxHouses);
end;

procedure TFormMain.Export_UnitsRXClick(Sender: TObject);
begin
  if ConfirmExport then
    gRes.Sprites.ExportToPNG(rxUnits);
end;

procedure TFormMain.Export_ScriptDataClick(Sender: TObject);
begin
  if (gGame <> nil)
    and (gGame.Scripting <> nil) then
    gGame.Scripting.ExportDataToText;
end;

procedure TFormMain.Export_GUIClick(Sender: TObject);
begin
  if ConfirmExport then
    gRes.Sprites.ExportToPNG(rxGUI);
end;

procedure TFormMain.Export_GUIMainRXClick(Sender: TObject);
begin
  if ConfirmExport then
    gRes.Sprites.ExportToPNG(rxGUIMain);
end;

procedure TFormMain.Export_CustomClick(Sender: TObject);
begin
  gRes.Sprites.ExportToPNG(rxCustom);
end;

procedure TFormMain.Export_TilesetClick(Sender: TObject);
begin
  gRes.Sprites.ExportToPNG(rxTiles);
end;

procedure TFormMain.Export_Sounds1Click(Sender: TObject);
begin
  gRes.Sounds.ExportSounds;
end;

procedure TFormMain.Export_TreeAnim1Click(Sender: TObject);
begin
  if ConfirmExport then
    gRes.ExportTreeAnim;
end;

procedure TFormMain.Export_HouseAnim1Click(Sender: TObject);
begin
  if ConfirmExport then
    gRes.ExportHouseAnim;
end;


procedure TFormMain.HousesDat1Click(Sender: TObject);
begin
  gRes.Houses.ExportCSV(ExeDir + 'Export' + PathDelim + 'houses.dat.csv')
end;


procedure TFormMain.LoadSavThenRplClick(Sender: TObject);
var
  SavPath, RplPath: UnicodeString;
begin
  if RunOpenDialog(OpenDialog1, '', fMissionDefOpenPath, 'Knights & Merchants Save (*.sav)|*.sav') then
  begin
    SavPath := OpenDialog1.FileName;
    fMissionDefOpenPath := ExtractFileDir(OpenDialog1.FileName);
    if RunOpenDialog(OpenDialog1, '', fMissionDefOpenPath, 'Knights & Merchants Replay (*.rpl)|*.rpl') then
    begin
      RplPath := OpenDialog1.FileName;

      gGameApp.NewSaveAndReplay(SavPath, RplPath);
    end;
  end;
end;


procedure TFormMain.ExportGameStatsClick(Sender: TObject);
var
  DateS: UnicodeString;
begin
  if (gGame <> nil) and not gGame.IsMapEditor then
  begin
    gResTexts.ForceDefaultLocale := True; //Use only eng for exported csv
    DateS := FormatDateTime('yyyy-mm-dd_hh-nn', Now);
    gHands.ExportGameStatsToCSV(ExeDir + 'Export' + PathDelim + gGame.GameName + '_' + DateS + '.csv',
                            Format('Statistics for game at map ''%s'' on %s', [gGame.GameName, DateS]));
    gResTexts.ForceDefaultLocale := False;
  end;
end;


procedure TFormMain.Export_Fonts1Click(Sender: TObject);
begin
  Assert(gRes <> nil, 'Can''t export Fonts cos they aren''t loaded yet');
  gRes.Fonts.ExportFonts;
end;


procedure TFormMain.Export_DeliverLists1Click(Sender: TObject);
var I: Integer;
begin
  if gHands = nil then Exit;
  //You could possibly cheat in multiplayer by seeing what supplies your enemy has
  if (gGameApp.Game <> nil) and (not gGameApp.Game.IsMultiPlayerOrSpec or MULTIPLAYER_CHEATS) then
  for I := 0 to gHands.Count - 1 do
    gHands[I].Deliveries.Queue.ExportToFile(ExeDir + 'Player_' + IntToStr(I) + '_Deliver_List.txt');
end;


procedure TFormMain.RGPlayerClick(Sender: TObject);
begin
  if (gGameApp.Game = nil)
    or gGameApp.Game.IsMapEditor
    or gGameApp.Game.IsMultiPlayerOrSpec then
    Exit;

  if (gHands <> nil) and (RGPlayer.ItemIndex < gHands.Count) then
    gMySpectator.HandID := RGPlayer.ItemIndex;
end;


procedure TFormMain.SaveSettingsClick(Sender: TObject);
begin
  gMain.Settings.SaveSettings(True);
  gGameApp.GameSettings.SaveSettings(True);
end;


procedure TFormMain.Debug_ShowLogisticsClick(Sender: TObject);
begin
  if not Assigned(FormLogistics) then
    FormLogistics := TFormLogistics.Create(Self);
  FormLogistics.Show;
end;


procedure TFormMain.SoldiersClick(Sender: TObject);
begin
  if ConfirmExport then
    gRes.ExportUnitAnim(WARRIOR_MIN, WARRIOR_MAX);
end;


procedure TFormMain.chkSuperSpeedClick(Sender: TObject);
begin
  if (gGameApp.Game = nil)
    or (gGameApp.Game.IsMultiPlayerOrSpec
      and not gGameApp.Game.IsMPGameSpeedChangeAllowed
      and not MULTIPLAYER_SPEEDUP
      and not gGameApp.Game.IsReplay) then
    Exit;

  gGameApp.Game.SetGameSpeed(IfThen(chkSuperSpeed.Checked, DEBUG_SPEEDUP_SPEED, gGameApp.Game.GetNormalGameSpeed), False);

  ActiveControl := nil; //Do not allow to focus on anything on debug panel
end;


procedure TFormMain.Button_StopClick(Sender: TObject);
begin
  if gGameApp.Game <> nil then
    if gGameApp.Game.IsMapEditor then
      gGameApp.StopGame(grMapEdEnd)
    else
      gGameApp.StopGame(grCancel);

  ActiveControl := nil; //Do not allow to focus on anything on debug panel
end;


procedure TFormMain.Button_UpdateUI_Click(Sender: TObject);
begin
  if gGameApp.Game <> nil then
    gGameApp.Game.ActiveInterface.UpdateState(gGameApp.GlobalTickCount); //UpdateUI, even on game Pause
<commit_msg>Fix for x300 checkbox was not able to uncheck when game was played on x300 speed<commit_after>unit KM_FormMain;
{$I KaM_Remake.inc}
interface
uses
  Classes, ComCtrls, Controls, Buttons, Dialogs, ExtCtrls, Forms, Graphics, Math, Menus, StdCtrls, SysUtils, StrUtils,
  KM_RenderControl, KM_Settings, KM_Video, KM_CommonTypes,
  KM_GameTypes,
  {$IFDEF FPC} LResources, {$ENDIF}
  {$IFDEF MSWindows} ShellAPI, Windows, Messages; {$ENDIF}
  {$IFDEF Unix} LCLIntf, LCLType; {$ENDIF}


type

  { TFormMain }

  TFormMain = class(TForm)
    chkAIEye: TCheckBox;
    chkLogGameTick: TCheckBox;
    MenuItem1: TMenuItem;
    SaveEditableMission1: TMenuItem;
    N2: TMenuItem;
    OpenDialog1: TOpenDialog;
    StatusBar1: TStatusBar;
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    Exit1: TMenuItem;
    N1: TMenuItem;
    About1: TMenuItem;
    Debug1: TMenuItem;
    Debug_PrintScreen: TMenuItem;
    Export1: TMenuItem;
    Export_GUIRX: TMenuItem;
    Export_TreesRX: TMenuItem;
    Export_HousesRX: TMenuItem;
    Export_UnitsRX: TMenuItem;
    Export_GUIMainRX: TMenuItem;
    Export_Custom: TMenuItem;
    Export_Tileset: TMenuItem;
    Export_Fonts1: TMenuItem;
    chkSuperSpeed: TCheckBox;
    Export_Deliverlists1: TMenuItem;
    Export_Sounds1: TMenuItem;
    Export_HouseAnim1: TMenuItem;
    Export_UnitAnim1: TMenuItem;
    RGPlayer: TRadioGroup;
    Button_Stop: TButton;
    OpenMissionMenu: TMenuItem;
    AnimData1: TMenuItem;
    Other1: TMenuItem;
    Debug_ShowPanel: TMenuItem;
    Export_TreeAnim1: TMenuItem;
    ExportMainMenu: TMenuItem;
    Debug_EnableCheats: TMenuItem;
    ExportUIPages: TMenuItem;
    Resources1: TMenuItem;
    HousesDat1: TMenuItem;
    chkShowOwnership: TCheckBox;
    chkShowNavMesh: TCheckBox;
    chkShowAvoid: TCheckBox;
    chkShowBalance: TCheckBox;
    tbOwnMargin: TTrackBar;
    tbOwnThresh: TTrackBar;
    Label5: TLabel;
    Label6: TLabel;
    chkShowDefences: TCheckBox;
    chkBuildAI: TCheckBox;
    chkCombatAI: TCheckBox;
    ResourceValues1: TMenuItem;
    chkUIControlsBounds: TCheckBox;
    chkUITextBounds: TCheckBox;
    tbAngleX: TTrackBar;
    tbAngleY: TTrackBar;
    Label3: TLabel;
    Label4: TLabel;
    tbBuildingStep: TTrackBar;
    Label1: TLabel;
    tbPassability: TTrackBar;
    Label2: TLabel;
    chkShowRoutes: TCheckBox;
    chkShowWires: TCheckBox;
    tbAngleZ: TTrackBar;
    Label7: TLabel;
    chkSelectionBuffer: TCheckBox;
    chkLogDelivery: TCheckBox;
    chkLogNetConnection: TCheckBox;
    RGLogNetPackets: TRadioGroup;
    chkLogsShowInChat: TCheckBox;
    chkUIControlsID: TCheckBox;
    Debug_ShowLogistics: TMenuItem;
    chkShowTerrainIds: TCheckBox;
    chkShowTerrainKinds: TCheckBox;
    UnitAnim_All: TMenuItem;
    N3: TMenuItem;
    Soldiers: TMenuItem;
    Civilians1: TMenuItem;
    SaveSettings: TMenuItem;
    N4: TMenuItem;
    ReloadSettings: TMenuItem;
    SaveDialog1: TSaveDialog;
    chkLogCommands: TCheckBox;
    ScriptData1: TMenuItem;
    chkBevel: TCheckBox;
    chkTilesGrid: TCheckBox;
    N6: TMenuItem;
    GameStats: TMenuItem;
    ExportGameStats: TMenuItem;
    ValidateGameStats: TMenuItem;
    chkLogRngChecks: TCheckBox;
    chkShowGameTick: TCheckBox;
    chkSkipRender: TCheckBox;
    chkSkipSound: TCheckBox;
    chkUIDs: TCheckBox;
    chkShowSoil: TCheckBox;
    chkShowFlatArea: TCheckBox;
    chkShowEyeRoutes: TCheckBox;
    chkSelectedObjInfo: TCheckBox;
    chkShowFPS: TCheckBox;
    chkHands: TCheckBox;
    btnUpdateUI: TButton;
    {$IFDEF WDC}
    mainGroup: TCategoryPanelGroup;
    cpGameControls: TCategoryPanel;
    cpDebugRender: TCategoryPanel;
    cpAI: TCategoryPanel;
    cpUserInreface: TCategoryPanel;
    cpGraphicTweaks: TCategoryPanel;
    cpLogs: TCategoryPanel;
    cpGameAdv: TCategoryPanel;
    cpPerfLogs: TCategoryPanel;
    chkSnowHouses: TCheckBox;
    chkLoadUnsupSaves: TCheckBox;
    chkJamMeter: TCheckBox;
    chkShowTerrainOverlays: TCheckBox;
    {$ENDIF}
    {$IFDEF FPC}
    mainGroup: TGroupBox;
    GroupBox2: TGroupBox;
    GroupBox3: TGroupBox;
    GroupBox4: TGroupBox;
    GroupBox5: TGroupBox;
    GroupBoxLogs: TGroupBox;
    {$ENDIF}
    N5: TMenuItem;
    LoadSavThenRpl: TMenuItem;
    N7: TMenuItem;
    ReloadLibx: TMenuItem;
    N8: TMenuItem;
    N10: TMenuItem;
    N9: TMenuItem;
    Debug_UnlockCmpMissions: TMenuItem;
    N11: TMenuItem;
    mnExportRngChecks: TMenuItem;

    procedure Export_TreeAnim1Click(Sender: TObject);
    procedure MenuItem1Click(Sender: TObject);
    procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure Debug_ExportMenuClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
    procedure Debug_EnableCheatsClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure AboutClick(Sender: TObject);
    procedure ExitClick(Sender: TObject);
    procedure Debug_PrintScreenClick(Sender: TObject);
    procedure Export_TreesRXClick(Sender: TObject);
    procedure Export_HousesRXClick(Sender: TObject);
    procedure Export_UnitsRXClick(Sender: TObject);
    procedure Export_ScriptDataClick(Sender: TObject);
    procedure Export_GUIClick(Sender: TObject);
    procedure Export_GUIMainRXClick(Sender: TObject);
    procedure Export_CustomClick(Sender: TObject);
    procedure Export_TilesetClick(Sender: TObject);
    procedure Export_Sounds1Click(Sender: TObject);
    procedure Export_HouseAnim1Click(Sender: TObject);
    procedure Export_Fonts1Click(Sender: TObject);
    procedure Export_DeliverLists1Click(Sender: TObject);
    procedure Button_StopClick(Sender: TObject);
    procedure RGPlayerClick(Sender: TObject);
    procedure Open_MissionMenuClick(Sender: TObject);
    procedure chkSuperSpeedClick(Sender: TObject);
    procedure Debug_ShowPanelClick(Sender: TObject);
    procedure FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure Debug_ExportUIPagesClick(Sender: TObject);
    procedure HousesDat1Click(Sender: TObject);
    procedure ExportGameStatsClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure ResourceValues1Click(Sender: TObject);
    procedure RenderAreaMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure RenderAreaMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
    procedure RenderAreaMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure RenderAreaMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure RenderAreaResize(aWidth, aHeight: Integer);
    procedure RenderAreaRender(aSender: TObject);
    procedure Debug_ShowLogisticsClick(Sender: TObject);
    procedure UnitAnim_AllClick(Sender: TObject);
    procedure SoldiersClick(Sender: TObject);
    procedure Civilians1Click(Sender: TObject);
    procedure ReloadSettingsClick(Sender: TObject);
    procedure SaveSettingsClick(Sender: TObject);
    procedure SaveEditableMission1Click(Sender: TObject);
    procedure ValidateGameStatsClick(Sender: TObject);
    procedure Button_UpdateUI_Click(Sender: TObject);
    procedure LoadSavThenRplClick(Sender: TObject);
    procedure ReloadLibxClick(Sender: TObject);
    procedure Debug_UnlockCmpMissionsClick(Sender: TObject);
    procedure mnExportRngChecksClick(Sender: TObject);

    procedure ControlsUpdate(Sender: TObject);
  private
    fShowStartVideo: Boolean;
    fUpdating: Boolean;
    fMissionDefOpenPath: UnicodeString;
    fOnControlsUpdated: TEvent;
    procedure FormKeyDownProc(aKey: Word; aShift: TShiftState);
    procedure FormKeyUpProc(aKey: Word; aShift: TShiftState);
    function ConfirmExport: Boolean;
    function GetMouseWheelStepsCnt(aWheelData: Integer): Integer;
    {$IFDEF MSWindows}
    function GetWindowParams: TKMWindowParamsRecord;
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
    procedure WMExitSizeMove(var Msg: TMessage) ; message WM_EXITSIZEMOVE;
    procedure WMAppCommand(var Msg: TMessage); message WM_APPCOMMAND;
    procedure WMMouseWheel(var Msg: TMessage); message WM_MOUSEWHEEL;
  protected
    procedure WndProc(var Message : TMessage); override;
    {$ENDIF}
  public
    RenderArea: TKMRenderControl;
    SuppressAltForMenu: Boolean; //Suppress Alt key 'activate window menu' function
    procedure ControlsSetVisibile(aShowCtrls, aShowGroupBox: Boolean); overload;
    procedure ControlsSetVisibile(aShowCtrls: Boolean); overload;
    procedure ControlsReset;
    procedure ControlsRefill;
    procedure ToggleFullscreen(aFullscreen, aWindowDefaultParams: Boolean);
    procedure SetSaveEditableMission(aEnabled: Boolean);
    procedure SetExportGameStats(aEnabled: Boolean);
    procedure ShowFolderPermissionError;
    property OnControlsUpdated: TEvent read fOnControlsUpdated write fOnControlsUpdated;
  end;


implementation
//{$IFDEF WDC}
  {$R *.dfm}
//{$ENDIF}

uses
  {$IFDEF WDC} UITypes, {$ENDIF}
  KromUtils,
  KromShellUtils,
  KM_Defaults,
  KM_Main,
  //Use these units directly to avoid pass-through methods in fMain
  KM_Resource,
  KM_ResSprites,
  KM_ResTexts,
  KM_GameApp,
  KM_HandsCollection,
  KM_ResSound,
  KM_Pics,
  KM_RenderPool,
  KM_Hand,
  KM_ResKeys, KM_FormLogistics, KM_Game,
  KM_RandomChecks,
  KM_Log, KM_CommonClasses, KM_DevPerfLog;


//Remove VCL panel and use flicker-free TMyPanel instead
procedure TFormMain.FormCreate(Sender: TObject);
begin
  fShowStartVideo := True;
  RenderArea := TKMRenderControl.Create(Self);
  RenderArea.Parent := Self;
  RenderArea.Align := alClient;
  RenderArea.Color := clMaroon;
  RenderArea.OnMouseDown := RenderAreaMouseDown;
  RenderArea.OnMouseMove := RenderAreaMouseMove;
  RenderArea.OnMouseUp := RenderAreaMouseUp;
  RenderArea.OnResize := RenderAreaResize;
  RenderArea.OnRender := RenderAreaRender;
  SuppressAltForMenu := False;

  chkSuperSpeed.Caption := 'Speed x' + IntToStr(DEBUG_SPEEDUP_SPEED);

  //Lazarus needs OnMouseWheel event to be for the panel, not the entire form
  {$IFDEF FPC} RenderArea.OnMouseWheel := RenderAreaMouseWheel; {$ENDIF}

  {$IFDEF MSWindows}
    //Means it will receive WM_SIZE WM_PAINT always in pair (if False - WM_PAINT is not called if size becames smaller)
    RenderArea.FullRepaint := True;
    RenderArea.BevelOuter := bvNone;
  {$ENDIF}

  //Put debug panel on top
  {$IFDEF WDC}
  RenderArea.BringToFront;
  mainGroup.SendToBack;
  StatusBar1.SendToBack;
  {$ENDIF}
  {$IFDEF FPC}
  RenderArea.SendToBack;
  mainGroup.BringToFront;
  {$ENDIF}
end;

procedure TFormMain.FormShow(Sender: TObject);
var BordersWidth, BordersHeight: Integer;
begin
  //We do this in OnShow rather than OnCreate as the window borders aren't
  //counted properly in OnCreate
  BordersWidth := Width - ClientWidth;
  BordersHeight := Height - ClientHeight;
  //Constraints includes window borders, so we add them on as Margin
  Constraints.MinWidth := MIN_RESOLUTION_WIDTH + BordersWidth;
  Constraints.MinHeight := MIN_RESOLUTION_HEIGHT + BordersHeight;

  // We have to put it here, to proper window positioning for multimonitor systems
  if not gMain.Settings.FullScreen then
  begin
    Left := gMain.Settings.WindowParams.Left;
    Top := gMain.Settings.WindowParams.Top;
  end;

  fMissionDefOpenPath := ExeDir;

  Application.ProcessMessages;

  if fShowStartVideo and (gGameApp.GameSettings <> nil) and gGameApp.GameSettings.VideoStartup then
  begin
    gVideoPlayer.AddVideo('Campaigns\The Shattered Kingdom\Logo');
    gVideoPlayer.AddVideo('KaM');
    gVideoPlayer.Play;
  end;
  fShowStartVideo := False;
end;


procedure TFormMain.SetSaveEditableMission(aEnabled: Boolean);
begin
  SaveEditableMission1.Enabled := aEnabled;
end;


procedure TFormMain.SetExportGameStats(aEnabled: Boolean);
begin
  ExportGameStats.Enabled := aEnabled;
end;


procedure TFormMain.FormKeyDownProc(aKey: Word; aShift: TShiftState);
begin
  if aKey = gResKeys[SC_DEBUG_WINDOW].Key then
  begin
    SHOW_DEBUG_CONTROLS := not SHOW_DEBUG_CONTROLS;
    ControlsSetVisibile(SHOW_DEBUG_CONTROLS, not (ssCtrl in aShift)); //Hide groupbox when Ctrl is pressed
  end;

  if gGameApp <> nil then gGameApp.KeyDown(aKey, aShift);
end;


procedure TFormMain.FormKeyUpProc(aKey: Word; aShift: TShiftState);
begin
  if gGameApp <> nil then gGameApp.KeyUp(aKey, aShift);
end;


procedure TFormMain.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  Assert(KeyPreview, 'MainForm should recieve all keys to pass them to fGame');
  FormKeyDownProc(Key, Shift);
end;


procedure TFormMain.FormKeyPress(Sender: TObject; var Key: Char);
begin
  Assert(KeyPreview, 'MainForm should recieve all keys to pass them to fGame');
  if gGameApp <> nil then gGameApp.KeyPress(Key);
end;


procedure TFormMain.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  Assert(KeyPreview, 'MainForm should recieve all keys to pass them to fGame');

  FormKeyUpProc(Key, Shift);
end;


procedure TFormMain.ReloadLibxClick(Sender: TObject);
begin
  gRes.LoadLocaleAndFonts(gGameApp.GameSettings.Locale, gGameApp.GameSettings.LoadFullFonts);
end;


procedure TFormMain.ReloadSettingsClick(Sender: TObject);
begin
  gMain.Settings.ReloadSettings;
  gGameApp.GameSettings.ReloadSettings;
end;


procedure TFormMain.RenderAreaMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  // Handle middle mouse button as Key
  if Button = mbMiddle then
    FormKeyDownProc(VK_MBUTTON, Shift)
  else if gGameApp <> nil then
    gGameApp.MouseDown(Button, Shift, X, Y);
end;


procedure TFormMain.RenderAreaMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
begin
  if gGameApp <> nil then gGameApp.MouseMove(Shift, X, Y);
end;


procedure TFormMain.RenderAreaMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if gGameApp <> nil then
  begin
    //Somehow Shift state does not contain mouse buttons ssLeft/ssRight/ssMiddle
    if Button = mbLeft then
      Include(Shift, ssLeft)
    else if Button = mbRight then
      Include(Shift, ssRight)
    else if Button = mbMiddle then
      Include(Shift, ssMiddle);

    // Handle middle mouse button as Key
    if Button = mbMiddle then
      FormKeyUpProc(VK_MBUTTON, Shift)
    else
      gGameApp.MouseUp(Button, Shift, X, Y);
  end;
end;


procedure TFormMain.RenderAreaMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
  if gGameApp <> nil then
    gGameApp.MouseWheel(Shift, WheelDelta, MousePos.X, MousePos.Y);
end;


procedure TFormMain.RenderAreaResize(aWidth, aHeight: Integer);
begin
  gMain.Resize(aWidth, aHeight, GetWindowParams);
end;


procedure TFormMain.RenderAreaRender(aSender: TObject);
begin
  gMain.Render;
end;


//Open
procedure TFormMain.Open_MissionMenuClick(Sender: TObject);
begin
  if RunOpenDialog(OpenDialog1, '', fMissionDefOpenPath, 'Knights & Merchants Mission (*.dat)|*.dat') then
  begin
    gGameApp.NewSingleMap(OpenDialog1.FileName, TruncateExt(ExtractFileName(OpenDialog1.FileName)));
    fMissionDefOpenPath := ExtractFileDir(OpenDialog1.FileName);
  end;
end;


procedure TFormMain.MenuItem1Click(Sender: TObject);
begin
  if RunOpenDialog(OpenDialog1, '', fMissionDefOpenPath, 'Knights & Merchants Mission (*.dat)|*.dat') then
  begin
    gGameApp.NewMapEditor(OpenDialog1.FileName);
    fMissionDefOpenPath := ExtractFileDir(OpenDialog1.FileName);
  end;
end;


procedure TFormMain.mnExportRngChecksClick(Sender: TObject);
var
  rngLogger: TKMRandomCheckLogger;
begin
  if RunOpenDialog(OpenDialog1, '', ExeDir, 'KaM Remake Random checks log (*.rng)|*.rng') then
  begin
    rngLogger := TKMRandomCheckLogger.Create;

    rngLogger.LoadFromPathAndParseToDict(OpenDialog1.FileName);
    rngLogger.SaveAsText(OpenDialog1.FileName + '.log');

    rngLogger.Free;
  end;
end;


procedure TFormMain.SaveEditableMission1Click(Sender: TObject);
begin
  if gGameApp.Game = nil then Exit;

  if not gGameApp.Game.IsMapEditor then Exit;

  if RunSaveDialog(SaveDialog1, gGameApp.Game.MapEditor.MissionDefSavePath, ExtractFileDir(gGameApp.Game.MapEditor.MissionDefSavePath), 'Knights & Merchants Mission (*.dat)|*.dat') then
    gGameApp.SaveMapEditor(SaveDialog1.FileName);
end;


//Exit
procedure TFormMain.ExitClick(Sender: TObject);
begin
  Close;
end;


//About
procedure TFormMain.AboutClick(Sender: TObject);
begin
  gMain.ShowAbout;
end;


//Debug Options
procedure TFormMain.Debug_EnableCheatsClick(Sender: TObject);
begin
  Debug_EnableCheats.Checked := not Debug_EnableCheats.Checked;
  DEBUG_CHEATS := Debug_EnableCheats.Checked;
end;


procedure TFormMain.Debug_PrintScreenClick(Sender: TObject);
begin
  if gGameApp <> nil then
    gGameApp.PrintScreen;
end;


procedure TFormMain.Debug_ShowPanelClick(Sender: TObject);
begin
  mainGroup.Visible := not mainGroup.Visible;
end;


procedure TFormMain.Debug_UnlockCmpMissionsClick(Sender: TObject);
begin
  case MessageDlg(Format(gResTexts[TX_MENU_DEBUG_UNLOCK_CAMPAIGNS_CONFIRM], [ExeDir + 'Saves']), mtWarning, [mbYes, mbNo], 0) of
    mrYes:  begin
              Debug_UnlockCmpMissions.Checked := not Debug_UnlockCmpMissions.Checked;
              UNLOCK_CAMPAIGN_MAPS := Debug_UnlockCmpMissions.Checked;
              if UNLOCK_CAMPAIGN_MAPS then
                gGameApp.UnlockAllCampaigns;
            end;
  end;
end;

//Exports
procedure TFormMain.Export_TreesRXClick(Sender: TObject);
begin
  if ConfirmExport then
    gRes.Sprites.ExportToPNG(rxTrees);
end;

procedure TFormMain.Export_HousesRXClick(Sender: TObject);
begin
  if ConfirmExport then
    gRes.Sprites.ExportToPNG(rxHouses);
end;

procedure TFormMain.Export_UnitsRXClick(Sender: TObject);
begin
  if ConfirmExport then
    gRes.Sprites.ExportToPNG(rxUnits);
end;

procedure TFormMain.Export_ScriptDataClick(Sender: TObject);
begin
  if (gGame <> nil)
    and (gGame.Scripting <> nil) then
    gGame.Scripting.ExportDataToText;
end;

procedure TFormMain.Export_GUIClick(Sender: TObject);
begin
  if ConfirmExport then
    gRes.Sprites.ExportToPNG(rxGUI);
end;

procedure TFormMain.Export_GUIMainRXClick(Sender: TObject);
begin
  if ConfirmExport then
    gRes.Sprites.ExportToPNG(rxGUIMain);
end;

procedure TFormMain.Export_CustomClick(Sender: TObject);
begin
  gRes.Sprites.ExportToPNG(rxCustom);
end;

procedure TFormMain.Export_TilesetClick(Sender: TObject);
begin
  gRes.Sprites.ExportToPNG(rxTiles);
end;

procedure TFormMain.Export_Sounds1Click(Sender: TObject);
begin
  gRes.Sounds.ExportSounds;
end;

procedure TFormMain.Export_TreeAnim1Click(Sender: TObject);
begin
  if ConfirmExport then
    gRes.ExportTreeAnim;
end;

procedure TFormMain.Export_HouseAnim1Click(Sender: TObject);
begin
  if ConfirmExport then
    gRes.ExportHouseAnim;
end;


procedure TFormMain.HousesDat1Click(Sender: TObject);
begin
  gRes.Houses.ExportCSV(ExeDir + 'Export' + PathDelim + 'houses.dat.csv')
end;


procedure TFormMain.LoadSavThenRplClick(Sender: TObject);
var
  SavPath, RplPath: UnicodeString;
begin
  if RunOpenDialog(OpenDialog1, '', fMissionDefOpenPath, 'Knights & Merchants Save (*.sav)|*.sav') then
  begin
    SavPath := OpenDialog1.FileName;
    fMissionDefOpenPath := ExtractFileDir(OpenDialog1.FileName);
    if RunOpenDialog(OpenDialog1, '', fMissionDefOpenPath, 'Knights & Merchants Replay (*.rpl)|*.rpl') then
    begin
      RplPath := OpenDialog1.FileName;

      gGameApp.NewSaveAndReplay(SavPath, RplPath);
    end;
  end;
end;


procedure TFormMain.ExportGameStatsClick(Sender: TObject);
var
  DateS: UnicodeString;
begin
  if (gGame <> nil) and not gGame.IsMapEditor then
  begin
    gResTexts.ForceDefaultLocale := True; //Use only eng for exported csv
    DateS := FormatDateTime('yyyy-mm-dd_hh-nn', Now);
    gHands.ExportGameStatsToCSV(ExeDir + 'Export' + PathDelim + gGame.GameName + '_' + DateS + '.csv',
                            Format('Statistics for game at map ''%s'' on %s', [gGame.GameName, DateS]));
    gResTexts.ForceDefaultLocale := False;
  end;
end;


procedure TFormMain.Export_Fonts1Click(Sender: TObject);
begin
  Assert(gRes <> nil, 'Can''t export Fonts cos they aren''t loaded yet');
  gRes.Fonts.ExportFonts;
end;


procedure TFormMain.Export_DeliverLists1Click(Sender: TObject);
var I: Integer;
begin
  if gHands = nil then Exit;
  //You could possibly cheat in multiplayer by seeing what supplies your enemy has
  if (gGameApp.Game <> nil) and (not gGameApp.Game.IsMultiPlayerOrSpec or MULTIPLAYER_CHEATS) then
  for I := 0 to gHands.Count - 1 do
    gHands[I].Deliveries.Queue.ExportToFile(ExeDir + 'Player_' + IntToStr(I) + '_Deliver_List.txt');
end;


procedure TFormMain.RGPlayerClick(Sender: TObject);
begin
  if (gGameApp.Game = nil)
    or gGameApp.Game.IsMapEditor
    or gGameApp.Game.IsMultiPlayerOrSpec then
    Exit;

  if (gHands <> nil) and (RGPlayer.ItemIndex < gHands.Count) then
    gMySpectator.HandID := RGPlayer.ItemIndex;
end;


procedure TFormMain.SaveSettingsClick(Sender: TObject);
begin
  gMain.Settings.SaveSettings(True);
  gGameApp.GameSettings.SaveSettings(True);
end;


procedure TFormMain.Debug_ShowLogisticsClick(Sender: TObject);
begin
  if not Assigned(FormLogistics) then
    FormLogistics := TFormLogistics.Create(Self);
  FormLogistics.Show;
end;


procedure TFormMain.SoldiersClick(Sender: TObject);
begin
  if ConfirmExport then
    gRes.ExportUnitAnim(WARRIOR_MIN, WARRIOR_MAX);
end;


procedure TFormMain.chkSuperSpeedClick(Sender: TObject);
var
  speed: Single;
begin
  if (gGameApp.Game = nil)
    or (gGameApp.Game.IsMultiPlayerOrSpec
      and not gGameApp.Game.IsMPGameSpeedChangeAllowed
      and not MULTIPLAYER_SPEEDUP
      and not gGameApp.Game.IsReplay) then
    Exit;

  speed := gGameApp.Game.GetNormalGameSpeed;

  if speed = DEBUG_SPEEDUP_SPEED then
    speed := 1;

  gGameApp.Game.SetGameSpeed(IfThen(chkSuperSpeed.Checked, DEBUG_SPEEDUP_SPEED, speed), False);

  ActiveControl := nil; //Do not allow to focus on anything on debug panel
end;


procedure TFormMain.Button_StopClick(Sender: TObject);
begin
  if gGameApp.Game <> nil then
    if gGameApp.Game.IsMapEditor then
      gGameApp.StopGame(grMapEdEnd)
    else
      gGameApp.StopGame(grCancel);

  ActiveControl := nil; //Do not allow to focus on anything on debug panel
end;


procedure TFormMain.Button_UpdateUI_Click(Sender: TObject);
begin
  if gGameApp.Game <> nil then
    gGameApp.Game.ActiveInterface.UpdateState(gGameApp.GlobalTickCount); //UpdateUI, even on game Pause

{'fraction_non_alphanumeric': 0.08807157057654076, 'fraction_numerical': 0.004042412193505633, 'mean_word_length': 4.836900464156781, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5588914', 'n_tokens_mistral': 15491, 'n_tokens_neox': 14894, 'n_words': 3444}