sync: restore source from Google Drive
Sync changed files to Google Drive / sync (push) Successful in 1h45m13s
Sync changed files to Google Drive / sync (push) Successful in 1h45m13s
This commit is contained in:
1 parent
e2d8247e91
commit
7063d1b1c9
993 files changed
+949976
No files matched your search
@@ -0,0 +1,162 @@
|
||||
unit KM_GameAppSettings;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Generics.Collections,
|
||||
KM_Settings, KM_SettingsXML, KM_GameSettings, KM_MainSettings, KM_KeysSettings,
|
||||
KM_IoXML;
|
||||
|
||||
|
||||
type
|
||||
// GameApp settings, stored in the XML
|
||||
// Loaded and saved only once
|
||||
TKMGameAppSettings = class(TKMSettingsXML)
|
||||
private
|
||||
function GetGameSettings: TKMGameSettings;
|
||||
function GetKeySettings: TKMKeysSettings;
|
||||
function GetMainSettings: TKMainSettings;
|
||||
|
||||
procedure BindRoot;
|
||||
protected
|
||||
procedure LoadFromFile(const aPath: string); override;
|
||||
procedure SaveToFile(const aPath: UnicodeString); override;
|
||||
function GetDefaultSettingsName: string; override;
|
||||
function GetSettingsName: string; override;
|
||||
public
|
||||
constructor Create(aScreenWidth, aScreenHeight: Integer);
|
||||
destructor Destroy; override;
|
||||
|
||||
property MainSettings: TKMainSettings read GetMainSettings;
|
||||
property GameSettings: TKMGameSettings read GetGameSettings;
|
||||
property KeySettings: TKMKeysSettings read GetKeySettings;
|
||||
|
||||
procedure ReloadFavouriteMaps;
|
||||
procedure SaveFavouriteMaps;
|
||||
|
||||
property Root: TKMXmlNode read fRoot;
|
||||
end;
|
||||
|
||||
var
|
||||
gGameAppSettings: TKMGameAppSettings;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
SysUtils, INIfiles, Math,
|
||||
KM_Defaults;
|
||||
|
||||
|
||||
{ TKMGameAppSettings }
|
||||
constructor TKMGameAppSettings.Create(aScreenWidth, aScreenHeight: Integer);
|
||||
begin
|
||||
gMainSettings := TKMainSettings.Create(aScreenWidth, aScreenHeight);
|
||||
gGameSettings := TKMGameSettings.Create;
|
||||
gKeySettings := TKMKeysSettings.Create;
|
||||
|
||||
inherited Create;
|
||||
end;
|
||||
|
||||
|
||||
destructor TKMGameAppSettings.Destroy;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
FreeAndNil(gKeySettings);
|
||||
FreeAndNil(gGameSettings);
|
||||
FreeAndNil(gMainSettings);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameAppSettings.BindRoot;
|
||||
begin
|
||||
gMainSettings.Root := Root;
|
||||
gGameSettings.Root := Root;
|
||||
gKeySettings.Root := Root;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameAppSettings.LoadFromFile(const aPath: string);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
BindRoot;
|
||||
|
||||
gMainSettings.LoadFromXML;
|
||||
gGameSettings.LoadFromXML;
|
||||
gKeySettings.LoadFromXML;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameAppSettings.ReloadFavouriteMaps;
|
||||
begin
|
||||
inherited LoadFromFile(GetPath);
|
||||
|
||||
BindRoot;
|
||||
|
||||
gGameSettings.LoadFavouriteMapsFromXML;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameAppSettings.SaveFavouriteMaps;
|
||||
var
|
||||
path: string;
|
||||
begin
|
||||
path := GetPath;
|
||||
inherited LoadFromFile(path);
|
||||
|
||||
BindRoot;
|
||||
|
||||
gGameSettings.SaveFavouriteMapsToXML;
|
||||
|
||||
inherited SaveToFile(path);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameAppSettings.SaveToFile(const aPath: UnicodeString);
|
||||
begin
|
||||
if SKIP_SETTINGS_SAVE then Exit;
|
||||
|
||||
BindRoot;
|
||||
|
||||
gMainSettings.SaveToXML;
|
||||
gGameSettings.SaveToXML;
|
||||
gKeySettings.SaveToXML;
|
||||
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
function TKMGameAppSettings.GetDefaultSettingsName: string;
|
||||
begin
|
||||
Result := SETTINGS_FILE;
|
||||
end;
|
||||
|
||||
|
||||
function TKMGameAppSettings.GetGameSettings: TKMGameSettings;
|
||||
begin
|
||||
Result := gGameSettings;
|
||||
end;
|
||||
|
||||
|
||||
function TKMGameAppSettings.GetKeySettings: TKMKeysSettings;
|
||||
begin
|
||||
Result := gKeySettings;
|
||||
end;
|
||||
|
||||
|
||||
function TKMGameAppSettings.GetMainSettings: TKMainSettings;
|
||||
begin
|
||||
Result := gMainSettings;
|
||||
end;
|
||||
|
||||
|
||||
function TKMGameAppSettings.GetSettingsName: string;
|
||||
const
|
||||
GAME_APP_SETTINGS_NAME = 'GameApp settings';
|
||||
begin
|
||||
Result := GAME_APP_SETTINGS_NAME;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
unit KM_GameAppSettingsPart;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
KM_IoXML;
|
||||
|
||||
|
||||
type
|
||||
// Part of GameApp settings, which are stored in the XML
|
||||
TKMGameAppSettingsPart = class abstract
|
||||
private
|
||||
fRoot: TKMXmlNode;
|
||||
public
|
||||
procedure SaveToXML; virtual; abstract;
|
||||
procedure LoadFromXML; virtual; abstract;
|
||||
|
||||
property Root: TKMXmlNode read fRoot write fRoot;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
|
||||
end.
|
||||
|
||||
+826
@@ -0,0 +1,826 @@
|
||||
unit KM_GameSettings;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes,
|
||||
{$IFDEF FPC}Forms,{$ENDIF} //Lazarus do not know UITypes
|
||||
{$IFDEF WDC}UITypes,{$ENDIF} //We use settings in console modules
|
||||
KM_WareDistribution, KM_MapTypes,
|
||||
KM_Defaults, KM_CommonTypes,
|
||||
KM_Lists,
|
||||
KM_CommonClasses,
|
||||
|
||||
KM_IoXML, KM_InterfaceTypes,
|
||||
KM_GameAppSettingsPart;
|
||||
|
||||
|
||||
type
|
||||
TKMSettingsGFX = record
|
||||
private
|
||||
fBrightness: Byte;
|
||||
fInterpolatedAnimations: Boolean;
|
||||
procedure SetBrightness(aValue: Byte);
|
||||
function GetInterpolatedAnimations: Boolean;
|
||||
public
|
||||
AlphaShadows: Boolean;
|
||||
LoadFullFonts: Boolean;
|
||||
InterpolatedRender: Boolean;
|
||||
AllowSnowHouses: Boolean;
|
||||
property InterpolatedAnimations: Boolean read GetInterpolatedAnimations write fInterpolatedAnimations;
|
||||
property Brightness: Byte read fBrightness write SetBrightness;
|
||||
end;
|
||||
|
||||
TKMSettingsSFX = record
|
||||
private
|
||||
fMusicVolume: Single;
|
||||
fSoundFXVolume: Single;
|
||||
procedure SetMusicVolume(aValue: Single);
|
||||
procedure SetSoundFXVolume(aValue: Single);
|
||||
public
|
||||
MusicEnabled: Boolean;
|
||||
ShuffleOn: Boolean;
|
||||
property MusicVolume: Single read fMusicVolume write SetMusicVolume;
|
||||
property SoundFXVolume: Single read fSoundFXVolume write SetSoundFXVolume;
|
||||
end;
|
||||
|
||||
TKMSettingsVideo = record
|
||||
private
|
||||
fVideoVolume: Single;
|
||||
procedure SetVideoVolume(aValue: Single);
|
||||
public
|
||||
Enabled: Boolean;
|
||||
VideoStretch: Boolean;
|
||||
PlayOnStartup: Boolean;
|
||||
property VideoVolume: Single read fVideoVolume write SetVideoVolume;
|
||||
end;
|
||||
|
||||
|
||||
// Gameplay settings, those that affect the game
|
||||
TKMGameSettings = class(TKMGameAppSettingsPart)
|
||||
private
|
||||
//Game
|
||||
fAutosave: Boolean;
|
||||
fAutosaveAtGameEnd: Boolean;
|
||||
fAutosaveFrequency: Integer;
|
||||
fAutosaveCount: Integer;
|
||||
fReplayAutopause: Boolean;
|
||||
fReplayShowBeacons: Boolean; //Replay variable - show beacons during replay
|
||||
fSpecShowBeacons: Boolean; //Spectator variable - show beacons while spectating
|
||||
fShowGameTime: Boolean; //Show game time label (always)
|
||||
fShowGameSpeed: Boolean; //Show game speed label and clock (always)
|
||||
|
||||
fSaveCheckpoints: Boolean; //Save game checkpoint for replay
|
||||
fSaveCheckpointsFreq: Integer;
|
||||
fSaveCheckpointsLimit: Integer;
|
||||
|
||||
fPlayersColorMode: TKMPlayerColorMode;
|
||||
fPlayerColorSelf: Cardinal;
|
||||
fPlayerColorAlly: Cardinal;
|
||||
fPlayerColorEnemy: Cardinal;
|
||||
|
||||
fDefaultZoom: Single;
|
||||
fZoomBehaviour: TKMZoomBehaviour;
|
||||
fScrollSpeed: Single;
|
||||
fLocale: AnsiString;
|
||||
fSpeedPace: Word;
|
||||
fSpeedMedium: Single;
|
||||
fSpeedFast: Single;
|
||||
fSpeedVeryFast: Single;
|
||||
fWareDistribution: TKMWareDistribution;
|
||||
fSaveWareDistribution: Boolean;
|
||||
|
||||
fDayGamesCount: Integer; //Number of games played today (used for saves namings)
|
||||
fLastDayGamePlayed: TDateTime; //Last day game played
|
||||
|
||||
//Campaign
|
||||
fCampaignLastDifficulty: TKMMissionDifficulty;
|
||||
|
||||
//Replay
|
||||
fReplaySavepoint: Boolean;
|
||||
fReplaySavepointFrequency: Integer;
|
||||
|
||||
//MapEd
|
||||
fMapEdHistoryDepth: Integer;
|
||||
fMapEdMaxTerrainHeight: Integer;
|
||||
|
||||
//Multiplayer
|
||||
fMultiplayerName: AnsiString;
|
||||
fLastIP: string;
|
||||
fLastPort: string;
|
||||
fLastRoom: string;
|
||||
fLastPassword: string;
|
||||
fFlashOnMessage: Boolean;
|
||||
|
||||
//Misc
|
||||
fAsyncGameResLoader: Boolean;
|
||||
|
||||
//Menu
|
||||
fMenu_FavouriteMapsStr: UnicodeString;
|
||||
fMenu_MapSPType: Byte;
|
||||
fMenu_ReplaysType: Byte;
|
||||
fMenu_MapEdMapType: Byte;
|
||||
fMenu_MapEdNewMapX: Word;
|
||||
fMenu_MapEdNewMapY: Word;
|
||||
fMenu_MapEdSPMapCRC: Cardinal;
|
||||
fMenu_MapEdMPMapCRC: Cardinal;
|
||||
fMenu_MapEdMPMapName: UnicodeString;
|
||||
fMenu_MapEdDLMapCRC: Cardinal;
|
||||
fMenu_CampaignName: UnicodeString;
|
||||
fMenu_ReplaySPSaveName: UnicodeString;
|
||||
fMenu_ReplayMPSaveName: UnicodeString;
|
||||
fMenu_SPScenarioMapCRC: Cardinal;
|
||||
fMenu_SPMissionMapCRC: Cardinal;
|
||||
fMenu_SPTacticMapCRC: Cardinal;
|
||||
fMenu_SPSpecialMapCRC: Cardinal;
|
||||
fMenu_SPSaveFileName: UnicodeString;
|
||||
fMenu_LobbyMapType: Byte;
|
||||
|
||||
fDebug_SaveRandomChecks: Boolean;
|
||||
fDebug_SaveGameAsText: Boolean;
|
||||
|
||||
fFavouriteMaps: TKMMapsCRCList;
|
||||
|
||||
//Game
|
||||
procedure SetAutosaveFrequency(aValue: Integer);
|
||||
procedure SetAutosaveCount(aValue: Integer);
|
||||
procedure SetLocale(const aLocale: AnsiString);
|
||||
|
||||
|
||||
procedure SetSaveCheckpointsFreq(const aValue: Integer);
|
||||
procedure SetSaveCheckpointsLimit(const aValue: Integer);
|
||||
|
||||
//Replay
|
||||
procedure SetReplaySavepointFrequency(aValue: Integer);
|
||||
|
||||
//MapEd
|
||||
procedure SetMapEdHistoryDepth(const aValue: Integer);
|
||||
procedure SetMapEdMaxTerrainHeight(const aValue: Integer);
|
||||
|
||||
//Menu
|
||||
procedure SetMenuFavouriteMapsStr(const aValue: UnicodeString);
|
||||
|
||||
procedure SetSpeedPace(const aValue: Word);
|
||||
function GetFavouriteMaps: TKMMapsCRCList;
|
||||
function GetAsyncGameResLoader: Boolean;
|
||||
public
|
||||
GFX: TKMSettingsGFX;
|
||||
SFX: TKMSettingsSFX;
|
||||
Video: TKMSettingsVideo;
|
||||
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure LoadFromXML; override;
|
||||
procedure SaveToXML; override;
|
||||
|
||||
procedure LoadFavouriteMapsFromXML;
|
||||
procedure SaveFavouriteMapsToXML;
|
||||
|
||||
// Game
|
||||
property Autosave: Boolean read fAutosave write fAutosave;
|
||||
property AutosaveAtGameEnd: Boolean read fAutosaveAtGameEnd write fAutosaveAtGameEnd;
|
||||
property AutosaveFrequency: Integer read fAutosaveFrequency write SetAutosaveFrequency;
|
||||
property AutosaveCount: Integer read fAutosaveCount write SetAutosaveCount;
|
||||
property SpecShowBeacons: Boolean read fSpecShowBeacons write fSpecShowBeacons;
|
||||
property ShowGameTime: Boolean read fShowGameTime write fShowGameTime;
|
||||
property ShowGameSpeed: Boolean read fShowGameSpeed write fShowGameSpeed;
|
||||
|
||||
property SaveCheckpoints: Boolean read fSaveCheckpoints write fSaveCheckpoints;
|
||||
property SaveCheckpointsFreq: Integer read fSaveCheckpointsFreq write SetSaveCheckpointsFreq;
|
||||
property SaveCheckpointsLimit: Integer read fSaveCheckpointsLimit write SetSaveCheckpointsLimit;
|
||||
|
||||
property PlayersColorMode: TKMPlayerColorMode read fPlayersColorMode write fPlayersColorMode;
|
||||
property PlayerColorSelf: Cardinal read fPlayerColorSelf write fPlayerColorSelf;
|
||||
property PlayerColorAlly: Cardinal read fPlayerColorAlly write fPlayerColorAlly;
|
||||
property PlayerColorEnemy: Cardinal read fPlayerColorEnemy write fPlayerColorEnemy;
|
||||
|
||||
property DefaultZoom: Single read fDefaultZoom;
|
||||
property ZoomBehaviour: TKMZoomBehaviour read fZoomBehaviour write fZoomBehaviour;
|
||||
property ScrollSpeed: Single read fScrollSpeed write fScrollSpeed;
|
||||
property Locale: AnsiString read fLocale write SetLocale;
|
||||
property SpeedPace: Word read fSpeedPace write SetSpeedPace;
|
||||
property SpeedMedium: Single read fSpeedMedium;
|
||||
property SpeedFast: Single read fSpeedFast;
|
||||
property SpeedVeryFast: Single read fSpeedVeryFast;
|
||||
property WareDistribution: TKMWareDistribution read fWareDistribution;
|
||||
property SaveWareDistribution: Boolean read fSaveWareDistribution write fSaveWareDistribution;
|
||||
|
||||
property DayGamesCount: Integer read fDayGamesCount write fDayGamesCount;
|
||||
property LastDayGamePlayed: TDateTime read fLastDayGamePlayed write fLastDayGamePlayed;
|
||||
|
||||
// Campaign
|
||||
property CampaignLastDifficulty: TKMMissionDifficulty read fCampaignLastDifficulty write fCampaignLastDifficulty;
|
||||
|
||||
// Replay
|
||||
property ReplayAutopause: Boolean read fReplayAutopause write fReplayAutopause;
|
||||
property ReplayShowBeacons: Boolean read fReplayShowBeacons write fReplayShowBeacons;
|
||||
property ReplaySavepoint: Boolean read fReplaySavepoint write fReplaySavepoint;
|
||||
property ReplaySavepointFrequency: Integer read fReplaySavepointFrequency write SetReplaySavepointFrequency;
|
||||
|
||||
// MapEd
|
||||
property MapEdHistoryDepth: Integer read fMapEdHistoryDepth write SetMapEdHistoryDepth;
|
||||
property MapEdMaxTerrainHeight: Integer read fMapEdMaxTerrainHeight write SetMapEdMaxTerrainHeight;
|
||||
|
||||
//Multiplayer
|
||||
property MultiplayerName: AnsiString read fMultiplayerName write fMultiplayerName;
|
||||
property FlashOnMessage: Boolean read fFlashOnMessage write fFlashOnMessage;
|
||||
property LastIP: string read fLastIP write fLastIP;
|
||||
property LastPort: string read fLastPort write fLastPort;
|
||||
property LastRoom: string read fLastRoom write fLastRoom;
|
||||
property LastPassword: string read fLastPassword write fLastPassword;
|
||||
|
||||
//Misc
|
||||
property AsyncGameResLoader: Boolean read GetAsyncGameResLoader;
|
||||
|
||||
// Menu
|
||||
property MenuMapSPType: Byte read fMenu_MapSPType write fMenu_MapSPType;
|
||||
property MenuReplaysType: Byte read fMenu_ReplaysType write fMenu_ReplaysType;
|
||||
property MenuMapEdMapType: Byte read fMenu_MapEdMapType write fMenu_MapEdMapType;
|
||||
property MenuMapEdNewMapX: Word read fMenu_MapEdNewMapX write fMenu_MapEdNewMapX;
|
||||
property MenuMapEdNewMapY: Word read fMenu_MapEdNewMapY write fMenu_MapEdNewMapY;
|
||||
property MenuMapEdSPMapCRC: Cardinal read fMenu_MapEdSPMapCRC write fMenu_MapEdSPMapCRC;
|
||||
property MenuMapEdMPMapCRC: Cardinal read fMenu_MapEdMPMapCRC write fMenu_MapEdMPMapCRC;
|
||||
property MenuMapEdMPMapName: UnicodeString read fMenu_MapEdMPMapName write fMenu_MapEdMPMapName;
|
||||
property MenuMapEdDLMapCRC: Cardinal read fMenu_MapEdDLMapCRC write fMenu_MapEdDLMapCRC;
|
||||
property MenuCampaignName: UnicodeString read fMenu_CampaignName write fMenu_CampaignName;
|
||||
property MenuReplaySPSaveName: UnicodeString read fMenu_ReplaySPSaveName write fMenu_ReplaySPSaveName;
|
||||
property MenuReplayMPSaveName: UnicodeString read fMenu_ReplayMPSaveName write fMenu_ReplayMPSaveName;
|
||||
property MenuSPScenarioMapCRC: Cardinal read fMenu_SPScenarioMapCRC write fMenu_SPScenarioMapCRC;
|
||||
property MenuSPMissionMapCRC: Cardinal read fMenu_SPMissionMapCRC write fMenu_SPMissionMapCRC;
|
||||
property MenuSPTacticMapCRC: Cardinal read fMenu_SPTacticMapCRC write fMenu_SPTacticMapCRC;
|
||||
property MenuSPSpecialMapCRC: Cardinal read fMenu_SPSpecialMapCRC write fMenu_SPSpecialMapCRC;
|
||||
property MenuSPSaveFileName: UnicodeString read fMenu_SPSaveFileName write fMenu_SPSaveFileName;
|
||||
property MenuLobbyMapType: Byte read fMenu_LobbyMapType write fMenu_LobbyMapType;
|
||||
|
||||
//Debug
|
||||
property DebugSaveRandomChecks: Boolean read fDebug_SaveRandomChecks write fDebug_SaveRandomChecks;
|
||||
property DebugSaveGameAsText: Boolean read fDebug_SaveGameAsText write fDebug_SaveGameAsText;
|
||||
|
||||
property FavouriteMaps: TKMMapsCRCList read GetFavouriteMaps;
|
||||
end;
|
||||
|
||||
var
|
||||
gGameSettings: TKMGameSettings;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
SysUtils, INIfiles, Math,
|
||||
KM_TerrainTypes;
|
||||
|
||||
|
||||
{ TKMSettingsGFX }
|
||||
function TKMSettingsGFX.GetInterpolatedAnimations: Boolean;
|
||||
begin
|
||||
Result := InterpolatedRender and fInterpolatedAnimations;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMSettingsGFX.SetBrightness(aValue: Byte);
|
||||
begin
|
||||
fBrightness := EnsureRange(aValue, 0, 20);
|
||||
end;
|
||||
|
||||
|
||||
{ TKMSettingsSFX }
|
||||
procedure TKMSettingsSFX.SetSoundFXVolume(aValue: Single);
|
||||
begin
|
||||
fSoundFXVolume := EnsureRange(aValue, 0, 1);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMSettingsSFX.SetMusicVolume(aValue: Single);
|
||||
begin
|
||||
fMusicVolume := EnsureRange(aValue, 0, 1);
|
||||
end;
|
||||
|
||||
|
||||
{ TKMSettingsVideo }
|
||||
procedure TKMSettingsVideo.SetVideoVolume(aValue: Single);
|
||||
begin
|
||||
fVideoVolume := EnsureRange(aValue, 0, 1);
|
||||
end;
|
||||
|
||||
|
||||
{ TKMGameSettings }
|
||||
constructor TKMGameSettings.Create;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
fWareDistribution := TKMWareDistribution.Create;
|
||||
|
||||
fFavouriteMaps := TKMMapsCRCList.Create;
|
||||
fFavouriteMaps.OnMapsUpdate := SetMenuFavouriteMapsStr;
|
||||
end;
|
||||
|
||||
|
||||
destructor TKMGameSettings.Destroy;
|
||||
begin
|
||||
FreeAndNil(fWareDistribution);
|
||||
FreeAndNil(fFavouriteMaps);
|
||||
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
function TKMGameSettings.GetAsyncGameResLoader: Boolean;
|
||||
begin
|
||||
if Self = nil then Exit(False);
|
||||
|
||||
Result := fAsyncGameResLoader;
|
||||
end;
|
||||
|
||||
|
||||
function TKMGameSettings.GetFavouriteMaps: TKMMapsCRCList;
|
||||
begin
|
||||
if Self = nil then Exit(nil);
|
||||
|
||||
Result := fFavouriteMaps;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.LoadFavouriteMapsFromXML;
|
||||
var
|
||||
nGameSettings, nMenu: TKMXmlNode;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
|
||||
nGameSettings := Root.AddOrFindChild('Game');
|
||||
|
||||
nMenu := nGameSettings.AddOrFindChild('Menu');
|
||||
fMenu_FavouriteMapsStr := nMenu.Attributes['FavouriteMaps'].AsString('');
|
||||
fFavouriteMaps.LoadFromString(fMenu_FavouriteMapsStr);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.SaveFavouriteMapsToXML;
|
||||
var
|
||||
nGameSettings, nMenu: TKMXmlNode;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
if BLOCK_FILE_WRITE then Exit;
|
||||
|
||||
nGameSettings := Root.AddOrFindChild('Game');
|
||||
|
||||
nMenu := nGameSettings.AddOrFindChild('Menu');
|
||||
nMenu.Attributes['FavouriteMaps'] := fMenu_FavouriteMapsStr;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.LoadFromXML;
|
||||
var
|
||||
nGameSettings,
|
||||
nGFX,
|
||||
nSFX,
|
||||
nMusic,
|
||||
nVideo,
|
||||
nGameCommon,
|
||||
nGameAutosave,
|
||||
nGameSavePoints,
|
||||
nGamePlayersColor,
|
||||
nUI,
|
||||
nGameSpeed,
|
||||
nGameWareDistribution,
|
||||
nGameMisc,
|
||||
nGameTweaks,
|
||||
nCampaign,
|
||||
nReplay,
|
||||
nMapEd,
|
||||
nMultiplayer,
|
||||
nMenu,
|
||||
nMenuSP,
|
||||
nMenuReplay,
|
||||
nMenuMapEd,
|
||||
nMisc,
|
||||
nDebug: TKMXmlNode;
|
||||
tempCard: Int64;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
inherited;
|
||||
|
||||
nGameSettings := Root.AddOrFindChild('Game');
|
||||
|
||||
// Game GFX
|
||||
nGFX := nGameSettings.AddOrFindChild('GFX');
|
||||
GFX.Brightness := nGFX.Attributes['Brightness'].AsInteger(1);
|
||||
GFX.AlphaShadows := nGFX.Attributes['AlphaShadows'].AsBoolean(True);
|
||||
GFX.LoadFullFonts := nGFX.Attributes['LoadFullFonts'].AsBoolean(False);
|
||||
|
||||
// SFX
|
||||
nSFX := nGameSettings.AddOrFindChild('SFX');
|
||||
SFX.SoundFXVolume := nSFX.Attributes['Volume'].AsFloat(0.5);
|
||||
|
||||
// Music
|
||||
nMusic := nGameSettings.AddOrFindChild('Music');
|
||||
SFX.MusicEnabled := nMusic.Attributes['Enabled'].AsBoolean(True);
|
||||
SFX.MusicVolume := nMusic.Attributes['Volume'].AsFloat(0.5);
|
||||
SFX.ShuffleOn := nMusic.Attributes['Shuffle'].AsBoolean(False);
|
||||
|
||||
// Video
|
||||
nVideo := nGameSettings.AddOrFindChild('Video');
|
||||
Video.Enabled := nVideo.Attributes['Enabled'].AsBoolean(False); //Disabled by default
|
||||
Video.VideoStretch := nVideo.Attributes['Stretch'].AsBoolean(True);
|
||||
Video.PlayOnStartup := nVideo.Attributes['Startup'].AsBoolean(True);
|
||||
Video.VideoVolume := nVideo.Attributes['Volume'].AsFloat(1);
|
||||
|
||||
// GameCommon
|
||||
nGameCommon := nGameSettings.AddOrFindChild('GameCommon');
|
||||
fLocale := AnsiString(nGameCommon.Attributes['Locale'].AsString(UnicodeString(DEFAULT_LOCALE)));
|
||||
// UI
|
||||
nUI := nGameCommon.AddOrFindChild('UI');
|
||||
fDefaultZoom := nUI.Attributes['DefaultZoom'].AsFloat(1);
|
||||
fZoomBehaviour := TKMZoomBehaviour(nUI.Attributes['ZoomBehaviour'].AsInteger(Ord(zbFull))); // Default zoom value is zbFull
|
||||
// Speed
|
||||
nGameSpeed := nGameCommon.AddOrFindChild('Speed');
|
||||
fSpeedMedium := nGameSpeed.Attributes['Medium'].AsFloat(3);
|
||||
fSpeedFast := nGameSpeed.Attributes['Fast'].AsFloat(6);
|
||||
fSpeedVeryFast := nGameSpeed.Attributes['VeryFast'].AsFloat(10);
|
||||
SpeedPace := nGameSpeed.Attributes['Pace'].AsInteger(SPEED_PACE_DEFAULT); // Set SpeedPace via setter
|
||||
fScrollSpeed := nGameSpeed.Attributes['ScrollSpeed'].AsFloat(5);
|
||||
|
||||
// Autosave
|
||||
nGameAutosave := nGameCommon.AddOrFindChild('Autosave');
|
||||
fAutosave := nGameAutosave.Attributes['Enabled'].AsBoolean(True);
|
||||
fAutosaveAtGameEnd := nGameAutosave.Attributes['OnGameEnd'].AsBoolean(False);
|
||||
AutosaveFrequency := nGameAutosave.Attributes['Frequency'].AsInteger(AUTOSAVE_FREQUENCY_DEFAULT); // With setter
|
||||
AutosaveCount := nGameAutosave.Attributes['Count'].AsInteger(AUTOSAVE_COUNT_DEF); // With setter
|
||||
|
||||
// SavePoints
|
||||
nGameSavePoints := nGameCommon.AddOrFindChild('SavePoints');
|
||||
fSaveCheckpoints := nGameSavePoints.Attributes['Enabled'].AsBoolean(True);
|
||||
SaveCheckpointsFreq := nGameSavePoints.Attributes['Frequency'].AsInteger(GAME_SAVE_CHECKPOINT_FREQ_DEF); // With setter
|
||||
SaveCheckpointsLimit := nGameSavePoints.Attributes['Limit'].AsInteger(GAME_SAVE_CHECKPOINT_CNT_LIMIT_DEF); // With setter
|
||||
|
||||
// PlayersColor
|
||||
nGamePlayersColor := nGameCommon.AddOrFindChild('PlayersColor');
|
||||
fPlayersColorMode := TKMPlayerColorMode(nGamePlayersColor.Attributes['Mode'].AsInteger(Byte(pcmDefault))); //Show players colors by default
|
||||
//Load minimap colors as hex strings 6-hex digits width
|
||||
if TryStrToInt64('$' + nGamePlayersColor.Attributes['Self'].AsString(IntToHex(Integer(clPlayerSelf and $FFFFFF), 6)), tempCard) then
|
||||
fPlayerColorSelf := $FF000000 or tempCard
|
||||
else
|
||||
fPlayerColorSelf := clPlayerSelf;
|
||||
|
||||
if TryStrToInt64('$' + nGamePlayersColor.Attributes['Ally'].AsString(IntToHex(Integer(clPlayerAlly and $FFFFFF), 6)), tempCard) then
|
||||
fPlayerColorAlly := $FF000000 or tempCard
|
||||
else
|
||||
fPlayerColorAlly := clPlayerAlly;
|
||||
|
||||
if TryStrToInt64('$' + nGamePlayersColor.Attributes['Enemy'].AsString(IntToHex(Integer(clPlayerEnemy and $FFFFFF), 6)), tempCard) then
|
||||
fPlayerColorEnemy := $FF000000 or tempCard
|
||||
else
|
||||
fPlayerColorEnemy := clPlayerEnemy;
|
||||
|
||||
// Ware distribution
|
||||
nGameWareDistribution := nGameCommon.AddOrFindChild('WareDistribution');
|
||||
fWareDistribution.LoadFromStr(nGameWareDistribution.Attributes['Value'].AsString(''));
|
||||
fSaveWareDistribution := nGameWareDistribution.Attributes['SavedBetweenGames'].AsBoolean(False); // Disabled by default
|
||||
|
||||
// Misc
|
||||
nGameMisc := nGameCommon.AddOrFindChild('Misc');
|
||||
fSpecShowBeacons := nGameMisc.Attributes['SpecShowBeacons'].AsBoolean(False);
|
||||
fShowGameTime := nGameMisc.Attributes['ShowGameTime'].AsBoolean(False);
|
||||
fShowGameSpeed := nGameMisc.Attributes['ShowGameSpeed'].AsBoolean(False);
|
||||
fDayGamesCount := nGameMisc.Attributes['DayGamesCount'].AsInteger(0);
|
||||
if nGameMisc.HasAttribute('LastDayGamePlayed') then
|
||||
fLastDayGamePlayed := nGameMisc.Attributes['LastDayGamePlayed'].AsDateTime
|
||||
else
|
||||
fLastDayGamePlayed := DATE_TIME_ZERO;
|
||||
|
||||
// Tweaks
|
||||
nGameTweaks := nGameCommon.AddOrFindChild('Tweaks');
|
||||
GFX.AllowSnowHouses := nGameTweaks.Attributes['AllowSnowHouses'].AsBoolean(True);
|
||||
GFX.InterpolatedRender := nGameTweaks.Attributes['InterpolatedRender'].AsBoolean(False);
|
||||
GFX.InterpolatedAnimations := nGameTweaks.Attributes['InterpolatedAnimations'].AsBoolean(False);
|
||||
|
||||
// Campaign
|
||||
nCampaign := nGameSettings.AddOrFindChild('Campaign');
|
||||
fCampaignLastDifficulty := TKMMissionDifficulty(nCampaign.Attributes['CampaignLastDifficulty'].AsInteger(Byte(mdNormal))); //Normal as default
|
||||
|
||||
// Replay
|
||||
nReplay := nGameSettings.AddOrFindChild('Replay');
|
||||
fReplayAutopause := nReplay.Attributes['Autopause'].AsBoolean(False);
|
||||
fReplayShowBeacons := nReplay.Attributes['ShowBeacons'].AsBoolean(False);
|
||||
fReplaySavepoint := nReplay.Attributes['Savepoint'].AsBoolean(True);
|
||||
ReplaySavepointFrequency := nReplay.Attributes['SavepointFrequency'].AsInteger(REPLAY_SAVEPOINT_FREQUENCY_DEF); // With setter
|
||||
|
||||
// MapEd
|
||||
nMapEd := nGameSettings.AddOrFindChild('MapEd');
|
||||
MapEdHistoryDepth := nMapEd.Attributes['HistoryDepth'].AsInteger(MAPED_HISTORY_DEPTH_DEF); // With setter
|
||||
MapEdMaxTerrainHeight := nMapEd.Attributes['MaxTerrainHeight'].AsInteger(HEIGHT_MAX); // With setter;
|
||||
|
||||
// Multiplayer
|
||||
nMultiplayer := nGameSettings.AddOrFindChild('Multiplayer');
|
||||
fMultiplayerName := AnsiString(nMultiplayer.Attributes['Name'].AsString('NoName'));
|
||||
fLastIP := nMultiplayer.Attributes['LastIP'].AsString('127.0.0.1');
|
||||
fLastPort := nMultiplayer.Attributes['LastPort'].AsString('56789');
|
||||
fLastRoom := nMultiplayer.Attributes['LastRoom'].AsString('0');
|
||||
fLastPassword := nMultiplayer.Attributes['LastPassword'].AsString('');
|
||||
fFlashOnMessage := nMultiplayer.Attributes['FlashOnMessage'].AsBoolean(True);
|
||||
|
||||
// Misc
|
||||
nMisc := nGameSettings.AddOrFindChild('Misc');
|
||||
fAsyncGameResLoader := nMisc.Attributes['AsyncGameResLoader'].AsBoolean(True);
|
||||
|
||||
// Menu
|
||||
nMenu := nGameSettings.AddOrFindChild('Menu');
|
||||
fMenu_FavouriteMapsStr := nMenu.Attributes['FavouriteMaps'].AsString('');
|
||||
fFavouriteMaps.LoadFromString(fMenu_FavouriteMapsStr);
|
||||
fMenu_LobbyMapType := nMenu.Attributes['LobbyMapType'].AsInteger(0);
|
||||
fMenu_CampaignName := nMenu.Attributes['CampaignName'].AsString('');
|
||||
|
||||
nMenuSP := nMenu.AddOrFindChild('Singleplayer');
|
||||
fMenu_MapSPType := nMenuSP.Attributes['Type'].AsInteger(0);
|
||||
fMenu_SPScenarioMapCRC := StrToInt64(nMenuSP.Attributes['ScenarioMapCRC'].AsString('0'));
|
||||
fMenu_SPMissionMapCRC := StrToInt64(nMenuSP.Attributes['MissionMapCRC'].AsString('0'));
|
||||
fMenu_SPTacticMapCRC := StrToInt64(nMenuSP.Attributes['TacticMapCRC'].AsString('0'));
|
||||
fMenu_SPSpecialMapCRC := StrToInt64(nMenuSP.Attributes['SpecialMapCRC'].AsString('0'));
|
||||
fMenu_SPSaveFileName := nMenuSP.Attributes['SaveFileName'].AsString('');
|
||||
|
||||
nMenuReplay := nMenu.AddOrFindChild('Replay');
|
||||
fMenu_ReplaysType := nMenuReplay.Attributes['Type'].AsInteger(0);
|
||||
fMenu_ReplaySPSaveName := nMenuReplay.Attributes['SPSaveName'].AsString('');
|
||||
fMenu_ReplayMPSaveName := nMenuReplay.Attributes['MPSaveName'].AsString('');
|
||||
|
||||
nMenuMapEd := nMenu.AddOrFindChild('MapEd');
|
||||
fMenu_MapEdMapType := nMenuMapEd.Attributes['MapType'].AsInteger(0);
|
||||
fMenu_MapEdNewMapX := nMenuMapEd.Attributes['NewMapX'].AsInteger(128);
|
||||
fMenu_MapEdNewMapY := nMenuMapEd.Attributes['NewMapY'].AsInteger(128);
|
||||
fMenu_MapEdSPMapCRC := StrToInt64(nMenuMapEd.Attributes['SPMapCRC'].AsString('0'));
|
||||
fMenu_MapEdMPMapCRC := StrToInt64(nMenuMapEd.Attributes['MPMapCRC'].AsString('0'));
|
||||
fMenu_MapEdMPMapName := nMenuMapEd.Attributes['MPMapName'].AsString('');
|
||||
fMenu_MapEdDLMapCRC := StrToInt64(nMenuMapEd.Attributes['DLMapCRC'].AsString('0'));
|
||||
|
||||
// Debug
|
||||
nDebug := nGameSettings.AddOrFindChild('Debug');
|
||||
if not DBG_DISALLOW_TO_SAVE_RANDOM_CHECKS then
|
||||
fDebug_SaveRandomChecks := nDebug.Attributes['SaveRandomChecks'].AsBoolean(True);
|
||||
|
||||
if DBG_SAVE_GAME_AS_TEXT then
|
||||
fDebug_SaveGameAsText := nDebug.Attributes['SaveGameAsText'].AsBoolean(False);
|
||||
|
||||
if INI_HITPOINT_RESTORE then
|
||||
HITPOINT_RESTORE_PACE := nDebug.Attributes['HitPointRestorePace'].AsInteger(DEFAULT_HITPOINT_RESTORE)
|
||||
else
|
||||
HITPOINT_RESTORE_PACE := DEFAULT_HITPOINT_RESTORE;
|
||||
end;
|
||||
|
||||
|
||||
//Don't rewrite the file for each individual change, do it in one batch for simplicity
|
||||
procedure TKMGameSettings.SaveToXML;
|
||||
var
|
||||
nGameSettings,
|
||||
nGFX,
|
||||
nSFX,
|
||||
nMusic,
|
||||
nVideo,
|
||||
nGameCommon,
|
||||
nGameAutosave,
|
||||
nGameSavePoints,
|
||||
nGamePlayersColor,
|
||||
nUI,
|
||||
nGameSpeed,
|
||||
nGameWareDistribution,
|
||||
nGameMisc,
|
||||
nGameTweaks,
|
||||
nCampaign,
|
||||
nReplay,
|
||||
nMapEd,
|
||||
nMultiplayer,
|
||||
nMenu,
|
||||
nMenuSP,
|
||||
nMenuReplay,
|
||||
nMenuMapEd,
|
||||
nMisc,
|
||||
nDebug: TKMXmlNode;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
if BLOCK_FILE_WRITE then Exit;
|
||||
|
||||
inherited;
|
||||
|
||||
nGameSettings := Root.AddOrFindChild('Game');
|
||||
// Clear old data before filling in
|
||||
{$IFDEF WDC}
|
||||
//todo -cPractical: do proper check on FPC
|
||||
nGameSettings.Clear;
|
||||
{$ENDIF}
|
||||
|
||||
// Game GFX
|
||||
nGFX := nGameSettings.AddOrFindChild('GFX');
|
||||
nGFX.Attributes['Brightness'] := GFX.Brightness;
|
||||
nGFX.Attributes['AlphaShadows'] := GFX.AlphaShadows;
|
||||
nGFX.Attributes['LoadFullFonts'] := GFX.LoadFullFonts;
|
||||
|
||||
// SFX
|
||||
nSFX := nGameSettings.AddOrFindChild('SFX');
|
||||
nSFX.Attributes['Volume'] := SFX.SoundFXVolume;
|
||||
|
||||
// Music
|
||||
nMusic := nGameSettings.AddOrFindChild('Music');
|
||||
nMusic.Attributes['Enabled'] := SFX.MusicEnabled; // Reversed value
|
||||
nMusic.Attributes['Volume'] := SFX.MusicVolume;
|
||||
nMusic.Attributes['Shuffle'] := SFX.ShuffleOn;
|
||||
|
||||
// Video
|
||||
nVideo := nGameSettings.AddOrFindChild('Video');
|
||||
nVideo.Attributes['Enabled'] := Video.Enabled;
|
||||
nVideo.Attributes['Stretch'] := Video.VideoStretch;
|
||||
nVideo.Attributes['Startup'] := Video.PlayOnStartup;
|
||||
nVideo.Attributes['Volume'] := Video.VideoVolume;
|
||||
|
||||
// GameCommon
|
||||
nGameCommon := nGameSettings.AddOrFindChild('GameCommon');
|
||||
nGameCommon.Attributes['Locale'] := UnicodeString(fLocale);
|
||||
// UI
|
||||
nUI := nGameCommon.AddOrFindChild('UI');
|
||||
nUI.Attributes['DefaultZoom'] := fDefaultZoom;
|
||||
nUI.Attributes['ZoomBehaviour'] := Integer(fZoomBehaviour);
|
||||
|
||||
// Speed
|
||||
nGameSpeed := nGameCommon.AddOrFindChild('Speed');
|
||||
nGameSpeed.Attributes['Medium'] := fSpeedMedium;
|
||||
nGameSpeed.Attributes['Fast'] := fSpeedFast;
|
||||
nGameSpeed.Attributes['VeryFast'] := fSpeedVeryFast;
|
||||
nGameSpeed.Attributes['Pace'] := fSpeedPace;
|
||||
nGameSpeed.Attributes['ScrollSpeed'] := fScrollSpeed;
|
||||
|
||||
// Autosave
|
||||
nGameAutosave := nGameCommon.AddOrFindChild('Autosave');
|
||||
nGameAutosave.Attributes['Enabled'] := fAutosave;
|
||||
nGameAutosave.Attributes['OnGameEnd'] := fAutosaveAtGameEnd;
|
||||
nGameAutosave.Attributes['Frequency'] := fAutosaveFrequency;
|
||||
nGameAutosave.Attributes['Count'] := fAutosaveCount;
|
||||
|
||||
// SavePoints
|
||||
nGameSavePoints := nGameCommon.AddOrFindChild('SavePoints');
|
||||
nGameSavePoints.Attributes['Enabled'] := fSaveCheckpoints;
|
||||
nGameSavePoints.Attributes['Frequency'] := fSaveCheckpointsFreq;
|
||||
nGameSavePoints.Attributes['Limit'] := fSaveCheckpointsLimit;
|
||||
|
||||
// PlayersColor
|
||||
nGamePlayersColor := nGameCommon.AddOrFindChild('PlayersColor');
|
||||
nGamePlayersColor.Attributes['Mode'] := Byte(fPlayersColorMode);
|
||||
nGamePlayersColor.Attributes['Self'] := IntToHex(fPlayerColorSelf and $FFFFFF, 6);
|
||||
nGamePlayersColor.Attributes['Ally'] := IntToHex(fPlayerColorAlly and $FFFFFF, 6);
|
||||
nGamePlayersColor.Attributes['Enemy'] := IntToHex(fPlayerColorEnemy and $FFFFFF, 6);
|
||||
|
||||
// Ware distribution
|
||||
nGameWareDistribution := nGameCommon.AddOrFindChild('WareDistribution');
|
||||
nGameWareDistribution.Attributes['Value'] := fWareDistribution.PackToStr;
|
||||
nGameWareDistribution.Attributes['SavedBetweenGames'] := fSaveWareDistribution;
|
||||
|
||||
// Misc
|
||||
nGameMisc := nGameCommon.AddOrFindChild('Misc');
|
||||
nGameMisc.Attributes['SpecShowBeacons'] := fSpecShowBeacons;
|
||||
nGameMisc.Attributes['ShowGameTime'] := fShowGameTime;
|
||||
nGameMisc.Attributes['ShowGameSpeed'] := fShowGameSpeed;
|
||||
nGameMisc.Attributes['DayGamesCount'] := fDayGamesCount;
|
||||
nGameMisc.Attributes['LastDayGamePlayed'] := fLastDayGamePlayed;
|
||||
|
||||
// Tweaks
|
||||
nGameTweaks := nGameCommon.AddOrFindChild('Tweaks');
|
||||
nGameTweaks.Attributes['AllowSnowHouses'] := GFX.AllowSnowHouses;
|
||||
nGameTweaks.Attributes['InterpolatedRender'] := GFX.InterpolatedRender;
|
||||
nGameTweaks.Attributes['InterpolatedAnimations'] := GFX.InterpolatedAnimations;
|
||||
|
||||
// Campaign
|
||||
nCampaign := nGameSettings.AddOrFindChild('Campaign');
|
||||
nCampaign.Attributes['CampaignLastDifficulty'] := Byte(fCampaignLastDifficulty);
|
||||
|
||||
// Replay
|
||||
nReplay := nGameSettings.AddOrFindChild('Replay');
|
||||
nReplay.Attributes['Autopause'] := fReplayAutopause;
|
||||
nReplay.Attributes['ShowBeacons'] := fReplayShowBeacons;
|
||||
nReplay.Attributes['Savepoint'] := fReplaySavepoint;
|
||||
nReplay.Attributes['SavepointFrequency'] := fReplaySavepointFrequency;
|
||||
|
||||
// MapEd
|
||||
nMapEd := nGameSettings.AddOrFindChild('MapEd');
|
||||
nMapEd.Attributes['HistoryDepth'] := fMapEdHistoryDepth;
|
||||
nMapEd.Attributes['MaxTerrainHeight'] := fMapEdMaxTerrainHeight;
|
||||
|
||||
// Multiplayer
|
||||
nMultiplayer := nGameSettings.AddOrFindChild('Multiplayer');
|
||||
nMultiplayer.Attributes['Name'] := UnicodeString(fMultiplayerName);
|
||||
nMultiplayer.Attributes['LastIP'] := fLastIP;
|
||||
nMultiplayer.Attributes['LastPort'] := fLastPort;
|
||||
nMultiplayer.Attributes['LastRoom'] := fLastRoom;
|
||||
nMultiplayer.Attributes['LastPassword'] := fLastPassword;
|
||||
nMultiplayer.Attributes['FlashOnMessage'] := fFlashOnMessage;
|
||||
|
||||
// Misc
|
||||
nMisc := nGameSettings.AddOrFindChild('Misc');
|
||||
nMisc.Attributes['AsyncGameResLoader'] := fAsyncGameResLoader;
|
||||
|
||||
// Menu
|
||||
nMenu := nGameSettings.AddOrFindChild('Menu');
|
||||
nMenu.Attributes['FavouriteMaps'] := fMenu_FavouriteMapsStr;
|
||||
nMenu.Attributes['CampaignName'] := fMenu_CampaignName;
|
||||
nMenu.Attributes['LobbyMapType'] := fMenu_LobbyMapType;
|
||||
|
||||
nMenuSP := nMenu.AddOrFindChild('Singleplayer');
|
||||
nMenuSP.Attributes['Type'] := fMenu_MapSPType;
|
||||
nMenuSP.Attributes['ScenarioMapCRC'] := IntToStr(fMenu_SPScenarioMapCRC);
|
||||
nMenuSP.Attributes['MissionMapCRC'] := IntToStr(fMenu_SPMissionMapCRC);
|
||||
nMenuSP.Attributes['TacticMapCRC'] := IntToStr(fMenu_SPTacticMapCRC);
|
||||
nMenuSP.Attributes['SpecialMapCRC'] := IntToStr(fMenu_SPSpecialMapCRC);
|
||||
nMenuSP.Attributes['SaveFileName'] := fMenu_SPSaveFileName;
|
||||
|
||||
nMenuReplay := nMenu.AddOrFindChild('Replay');
|
||||
nMenuReplay.Attributes['Type'] := fMenu_ReplaysType;
|
||||
nMenuReplay.Attributes['SPSaveName'] := fMenu_ReplaySPSaveName;
|
||||
nMenuReplay.Attributes['MPSaveName'] := fMenu_ReplayMPSaveName;
|
||||
|
||||
nMenuMapEd := nMenu.AddOrFindChild('MapEd');
|
||||
nMenuMapEd.Attributes['MapType'] := fMenu_MapEdMapType;
|
||||
nMenuMapEd.Attributes['NewMapX'] := fMenu_MapEdNewMapX;
|
||||
nMenuMapEd.Attributes['NewMapY'] := fMenu_MapEdNewMapY;
|
||||
nMenuMapEd.Attributes['SPMapCRC'] := IntToStr(fMenu_MapEdSPMapCRC);
|
||||
nMenuMapEd.Attributes['MPMapCRC'] := IntToStr(fMenu_MapEdMPMapCRC);
|
||||
nMenuMapEd.Attributes['MPMapName'] := fMenu_MapEdMPMapName;
|
||||
nMenuMapEd.Attributes['DLMapCRC'] := IntToStr(fMenu_MapEdDLMapCRC);
|
||||
|
||||
// Debug
|
||||
nDebug := nGameSettings.AddOrFindChild('Debug');
|
||||
if not DBG_DISALLOW_TO_SAVE_RANDOM_CHECKS then
|
||||
nDebug.Attributes['SaveRandomChecks'] := fDebug_SaveRandomChecks;
|
||||
|
||||
if DBG_SAVE_GAME_AS_TEXT then
|
||||
nDebug.Attributes['SaveGameAsText'] := fDebug_SaveGameAsText;
|
||||
|
||||
if INI_HITPOINT_RESTORE then
|
||||
nDebug.Attributes['HitPointRestorePace'] := HITPOINT_RESTORE_PACE;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.SetLocale(const aLocale: AnsiString);
|
||||
begin
|
||||
//We can get some unsupported LocaleCode, but that is fine, it will have Eng fallback anyway
|
||||
fLocale := aLocale;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.SetMenuFavouriteMapsStr(const aValue: UnicodeString);
|
||||
begin
|
||||
// This proc is used by an event
|
||||
fMenu_FavouriteMapsStr := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.SetAutosaveCount(aValue: Integer);
|
||||
begin
|
||||
fAutosaveCount := EnsureRange(aValue, AUTOSAVE_COUNT_MIN, AUTOSAVE_COUNT_MAX);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.SetAutosaveFrequency(aValue: Integer);
|
||||
begin
|
||||
fAutosaveFrequency := EnsureRange(aValue, AUTOSAVE_FREQUENCY_MIN, AUTOSAVE_FREQUENCY_MAX);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.SetSpeedPace(const aValue: Word);
|
||||
begin
|
||||
// Allow to set speed pace only while in debug mode.
|
||||
// Its possible to alter game speed now, so there is no need actual need for speed pace change
|
||||
// And actual game speed is recorded in the game / replay, but speed pace is not.
|
||||
// So its better to show on what actual speed some speedrunner made his crazy run
|
||||
{$IFDEF DEBUG}
|
||||
fSpeedPace := aValue;
|
||||
{$ELSE}
|
||||
fSpeedPace := SPEED_PACE_DEFAULT;
|
||||
{$ENDIF}
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.SetSaveCheckpointsFreq(const aValue: Integer);
|
||||
begin
|
||||
fSaveCheckpointsFreq := EnsureRange(aValue, GAME_SAVE_CHECKPOINT_FREQ_MIN, GAME_SAVE_CHECKPOINT_FREQ_MAX);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.SetSaveCheckpointsLimit(const aValue: Integer);
|
||||
begin
|
||||
fSaveCheckpointsLimit := EnsureRange(aValue, GAME_SAVE_CHECKPOINT_CNT_LIMIT_MIN, GAME_SAVE_CHECKPOINT_CNT_LIMIT_MAX);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.SetReplaySavepointFrequency(aValue: Integer);
|
||||
begin
|
||||
fReplaySavepointFrequency := EnsureRange(aValue, REPLAY_SAVEPOINT_FREQUENCY_MIN, REPLAY_SAVEPOINT_FREQUENCY_MAX);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.SetMapEdHistoryDepth(const aValue: Integer);
|
||||
begin
|
||||
fMapEdHistoryDepth := EnsureRange(aValue, MAPED_HISTORY_DEPTH_MIN, MAPED_HISTORY_DEPTH_MAX);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMGameSettings.SetMapEdMaxTerrainHeight(const aValue: Integer);
|
||||
begin
|
||||
fMapEdMaxTerrainHeight := EnsureRange(aValue, 0, HEIGHT_MAX);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
unit KM_KeysSettings;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
KM_IoXML,
|
||||
KM_GameAppSettingsPart;
|
||||
|
||||
type
|
||||
// Hotkey settings loader/saver
|
||||
TKMKeysSettings = class(TKMGameAppSettingsPart)
|
||||
private
|
||||
fHotkeysNode: TKMXmlNode;
|
||||
public
|
||||
procedure LoadFromXML; override;
|
||||
procedure SaveToXML; override;
|
||||
|
||||
procedure UpdateResKeys;
|
||||
end;
|
||||
|
||||
var
|
||||
gKeySettings: TKMKeysSettings;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
KM_ResTexts,
|
||||
KM_ResKeys,
|
||||
KM_ResKeyFuncs,
|
||||
KM_ResTypes;
|
||||
|
||||
|
||||
{ TKMKeysSettings }
|
||||
procedure TKMKeysSettings.UpdateResKeys;
|
||||
var
|
||||
KF: TKMKeyFunction;
|
||||
nKey: TKMXmlNode;
|
||||
keyFuncName: string;
|
||||
begin
|
||||
if gResKeys = nil then Exit;
|
||||
|
||||
for KF := KEY_FUNC_LOW to High(TKMKeyFunction) do
|
||||
begin
|
||||
keyFuncName := GetKeyFunctionStr(KF);
|
||||
if fHotkeysNode.HasChild(keyFuncName) then
|
||||
begin
|
||||
nKey := fHotkeysNode.AddOrFindChild(keyFuncName);
|
||||
if nKey.HasAttribute('Key') then
|
||||
gResKeys[KF] := nKey.Attributes['Key'].AsInteger;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMKeysSettings.LoadFromXML;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
inherited;
|
||||
|
||||
fHotkeysNode := Root.AddOrFindChild('Hotkeys');
|
||||
|
||||
UpdateResKeys;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMKeysSettings.SaveToXML;
|
||||
var
|
||||
KF: TKMKeyFunction;
|
||||
nHotkeys, nKey: TKMXmlNode;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
inherited;
|
||||
|
||||
nHotkeys := Root.AddOrFindChild('Hotkeys');
|
||||
|
||||
// Those are already nil on game exit (gGameApp is destroyed already)
|
||||
if (gResKeys = nil)
|
||||
or (gResKeyFuncs = nil)
|
||||
or (gResTexts = nil) then
|
||||
Exit;
|
||||
|
||||
// Clear old data before filling in
|
||||
{$IFDEF WDC}
|
||||
//todo -cPractical: do proper check on FPC
|
||||
nHotkeys.Clear;
|
||||
{$ENDIF}
|
||||
|
||||
for KF := KEY_FUNC_LOW to High(TKMKeyFunction) do
|
||||
begin
|
||||
nKey := nHotkeys.AddOrFindChild(GetKeyFunctionStr(KF));
|
||||
nKey.Attributes['Key'] := gResKeys[KF];
|
||||
|
||||
// These are just comments
|
||||
nKey.Attributes['KeyDesc'] := gResKeys.GetKeyName(gResKeys[KF]);
|
||||
nKey.Attributes['FuncDesc'] := gResTexts[gResKeyFuncs[KF].TextId];
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
unit KM_MainSettings;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes,
|
||||
{$IFDEF WDC}UITypes,{$ENDIF} //We use settings in console modules
|
||||
KM_Resolutions,
|
||||
KM_Defaults,
|
||||
KM_WindowParams,
|
||||
KM_IoXML,
|
||||
KM_GameAppSettingsPart;
|
||||
|
||||
type
|
||||
// Settings that are irrelevant to the game (game does not care about them)
|
||||
TKMainSettings = class(TKMGameAppSettingsPart)
|
||||
private
|
||||
// Not a setting
|
||||
// This is current screen resolution used used to set default Resolution and Form size
|
||||
fScreenWidth: Integer;
|
||||
fScreenHeight: Integer;
|
||||
|
||||
fFPSCap: Integer;
|
||||
fWindowParams: TKMWindowParams;
|
||||
fNoRenderMaxTime: Integer; // Longest period of time, when there was no Render (usually on hiiiigh game speed like x300)
|
||||
function GetWindowParams: TKMWindowParams;
|
||||
public
|
||||
FullScreen: Boolean;
|
||||
Resolution: TKMScreenRes;
|
||||
VSync: Boolean;
|
||||
|
||||
constructor Create(aScreenWidth, aScreenHeight: Integer);
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure LoadFromXML; override;
|
||||
procedure SaveToXML; override;
|
||||
|
||||
property FPSCap: Integer read fFPSCap;
|
||||
property WindowParams: TKMWindowParams read GetWindowParams;
|
||||
property NoRenderMaxTime: Integer read fNoRenderMaxTime;
|
||||
|
||||
function IsNoRenderMaxTimeSet: Boolean;
|
||||
end;
|
||||
|
||||
|
||||
var
|
||||
gMainSettings: TKMainSettings;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
SysUtils, INIfiles, Math, Forms;
|
||||
|
||||
const
|
||||
NO_RENDER_MAX_TIME_MIN = 10; //in ms
|
||||
NO_RENDER_MAX_TIME_DEFAULT = 1000; //in ms
|
||||
NO_RENDER_MAX_TIME_UNDEF = -1; //undefined
|
||||
|
||||
|
||||
{ TKMainSettings }
|
||||
constructor TKMainSettings.Create(aScreenWidth, aScreenHeight: Integer);
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
fWindowParams := TKMWindowParams.Create;
|
||||
fScreenWidth := aScreenWidth;
|
||||
fScreenHeight := aScreenHeight;
|
||||
end;
|
||||
|
||||
|
||||
destructor TKMainSettings.Destroy;
|
||||
begin
|
||||
FreeAndNil(fWindowParams);
|
||||
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
function TKMainSettings.GetWindowParams: TKMWindowParams;
|
||||
begin
|
||||
if Self = nil then Exit(nil);
|
||||
|
||||
Result := fWindowParams;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMainSettings.LoadFromXML;
|
||||
var
|
||||
nMainSettings, nGFX, nWindow, nMisc: TKMXmlNode;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
|
||||
inherited;
|
||||
|
||||
nMainSettings := Root.AddOrFindChild('Main');
|
||||
|
||||
// GFX
|
||||
nGFX := nMainSettings.AddOrFindChild('GFX');
|
||||
FullScreen := nGFX.Attributes['FullScreen'].AsBoolean(False);
|
||||
VSync := nGFX.Attributes['VSync'].AsBoolean(True);
|
||||
Resolution.Width := nGFX.Attributes['ResolutionWidth'].AsInteger(Max(MENU_DESIGN_X, fScreenWidth));
|
||||
Resolution.Height := nGFX.Attributes['ResolutionHeight'].AsInteger(Max(MENU_DESIGN_Y, fScreenHeight));
|
||||
Resolution.RefRate := nGFX.Attributes['RefreshRate'].AsInteger(60);
|
||||
fFPSCap := EnsureRange(nGFX.Attributes['FPSCap'].AsInteger(DEF_FPS_CAP), MIN_FPS_CAP, MAX_FPS_CAP);
|
||||
|
||||
// Window
|
||||
// For proper window positioning we need Left and Top records
|
||||
// Otherwise reset all window params to defaults
|
||||
nWindow := nMainSettings.AddOrFindChild('Window');
|
||||
if nWindow.HasAttribute('Left') and nWindow.HasAttribute('Top') then
|
||||
begin
|
||||
fWindowParams.Left := nWindow.Attributes['Left'].AsInteger(-1);
|
||||
fWindowParams.Top := nWindow.Attributes['Top'].AsInteger(-1);
|
||||
fWindowParams.Width := nWindow.Attributes['Width'].AsInteger(Max(MENU_DESIGN_X, fScreenWidth));
|
||||
fWindowParams.Height := nWindow.Attributes['Height'].AsInteger(Max(MENU_DESIGN_Y, fScreenHeight));
|
||||
fWindowParams.State := TWindowState(EnsureRange(nWindow.Attributes['State'].AsInteger(Integer(wsNormal)), 0, 2));
|
||||
fWindowParams.Position := TPosition(EnsureRange(nWindow.Attributes['Position'].AsInteger(Integer(poScreenCenter)), 0, 7));
|
||||
fWindowParams.FixedPosition := nWindow.Attributes['FixedPosition'].AsBoolean(False);
|
||||
end else
|
||||
fWindowParams.NeedResetToDefaults := True;
|
||||
|
||||
// Misc
|
||||
nMisc := nMainSettings.AddOrFindChild('Misc');
|
||||
fNoRenderMaxTime := nMisc.Attributes['NoRenderMaxTime'].AsInteger(NO_RENDER_MAX_TIME_DEFAULT);
|
||||
if fNoRenderMaxTime < NO_RENDER_MAX_TIME_MIN then
|
||||
fNoRenderMaxTime := NO_RENDER_MAX_TIME_UNDEF;
|
||||
|
||||
// Reset wsMinimized state to wsNormal
|
||||
if (fWindowParams.State = TWindowState.wsMinimized) then
|
||||
fWindowParams.State := TWindowState.wsNormal;
|
||||
end;
|
||||
|
||||
|
||||
//Don't rewrite the file for each individual change, do it in one batch for simplicity
|
||||
procedure TKMainSettings.SaveToXML;
|
||||
var
|
||||
nMainSettings, nGFX, nWindow, nMisc: TKMXmlNode;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
if BLOCK_FILE_WRITE or SKIP_SETTINGS_SAVE then Exit;
|
||||
|
||||
inherited;
|
||||
|
||||
nMainSettings := Root.AddOrFindChild('Main');
|
||||
// Clear old data before filling in
|
||||
nMainSettings.Clear;
|
||||
|
||||
// GFX
|
||||
nGFX := nMainSettings.AddOrFindChild('GFX');
|
||||
nGFX.Attributes['FullScreen'] := FullScreen;
|
||||
nGFX.Attributes['VSync'] := VSync;
|
||||
nGFX.Attributes['ResolutionWidth'] := Resolution.Width;
|
||||
nGFX.Attributes['ResolutionHeight'] := Resolution.Height;
|
||||
nGFX.Attributes['RefreshRate'] := Resolution.RefRate;
|
||||
nGFX.Attributes['FPSCap'] := fFPSCap;
|
||||
|
||||
// Window
|
||||
nWindow := nMainSettings.AddOrFindChild('Window');
|
||||
nWindow.Attributes['Left'] := fWindowParams.Left;
|
||||
nWindow.Attributes['Top'] := fWindowParams.Top;
|
||||
nWindow.Attributes['Width'] := fWindowParams.Width;
|
||||
nWindow.Attributes['Height'] := fWindowParams.Height;
|
||||
nWindow.Attributes['State'] := Ord(fWindowParams.State);
|
||||
nWindow.Attributes['Position'] := Ord(fWindowParams.Position);
|
||||
nWindow.Attributes['FixedPosition'] := fWindowParams.FixedPosition;
|
||||
|
||||
// Misc
|
||||
nMisc := nMainSettings.AddOrFindChild('Misc');
|
||||
nMisc.Attributes['NoRenderMaxTime'] := fNoRenderMaxTime;
|
||||
end;
|
||||
|
||||
|
||||
function TKMainSettings.IsNoRenderMaxTimeSet: Boolean;
|
||||
begin
|
||||
Result := fNoRenderMaxTime <> NO_RENDER_MAX_TIME_UNDEF;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
+291
@@ -0,0 +1,291 @@
|
||||
unit KM_Resolutions;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
|
||||
|
||||
type
|
||||
TKMScreenRes = record
|
||||
Width, Height, RefRate: SmallInt;
|
||||
end;
|
||||
|
||||
TKMScreenResIndex = record
|
||||
ResId, RefId: Integer; // Allow for -1, when index not found
|
||||
end;
|
||||
|
||||
// Store resolution and list of its allowed refresh rates
|
||||
TKMScreenResData = record
|
||||
Width, Height: Word;
|
||||
RefRateCount: Integer;
|
||||
RefRate: array of Word;
|
||||
end;
|
||||
|
||||
TKMResolutions = class
|
||||
private
|
||||
fCount: Integer;
|
||||
fItems: array of TKMScreenResData;
|
||||
fNeedsRestoring: Boolean;
|
||||
|
||||
function GetItem(aIndex: Integer): TKMScreenResData;
|
||||
procedure ReadAvailable;
|
||||
procedure Sort;
|
||||
function SupportedRes(aWidth, aHeight, aRate, aBPP: Word): Boolean;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure Restore; //restores resolution used before program was started
|
||||
|
||||
property Count: Integer read fCount; //Used by UI
|
||||
property Items[aIndex: Integer]: TKMScreenResData read GetItem; //Used by UI
|
||||
|
||||
function IsValid(const aResolution: TKMScreenRes): Boolean; //Check, if resolution is correct
|
||||
function FindCorrect(const aResolution: TKMScreenRes): TKMScreenRes; //Try to find correct resolution
|
||||
function GetResolutionIDs(const aResolution: TKMScreenRes): TKMScreenResIndex; //prepares IDs for TMainSettings
|
||||
procedure SetResolution(const aResolution: TKMScreenRes); //Apply the resolution
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
Math,
|
||||
{$IFDEF MSWindows} Windows, {$ENDIF}
|
||||
KM_Defaults;
|
||||
|
||||
|
||||
{ TKMResolutions }
|
||||
constructor TKMResolutions.Create;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
ReadAvailable;
|
||||
Sort;
|
||||
end;
|
||||
|
||||
|
||||
destructor TKMResolutions.Destroy;
|
||||
begin
|
||||
Restore;
|
||||
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
function TKMResolutions.SupportedRes(aWidth, aHeight, aRate, aBPP: Word): Boolean;
|
||||
begin
|
||||
Result := (aBPP = 32) and (aWidth > aHeight)
|
||||
and (aWidth >= RESOLUTION_WIDTH_MIN)
|
||||
and (aHeight >= RESOLUTION_HEIGHT_MIN)
|
||||
and (aRate > 0);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMResolutions.ReadAvailable;
|
||||
{$IFDEF MSWindows}
|
||||
var
|
||||
I, M, N: Integer;
|
||||
devMode: TDevMode;
|
||||
{$ENDIF}
|
||||
begin
|
||||
{$IFDEF MSWindows}
|
||||
I := 0;
|
||||
fCount := 0;
|
||||
while EnumDisplaySettings(nil, I, devMode) do
|
||||
with devMode do //todo -cPractical: Would be good to get rid of this "with"
|
||||
begin
|
||||
Inc(I);
|
||||
// Take only 32bpp modes
|
||||
// Exclude rotated modes, as Win reports them too
|
||||
if SupportedRes(dmPelsWidth, dmPelsHeight, dmDisplayFrequency, dmBitsPerPel) then
|
||||
begin
|
||||
// Find next empty place and avoid duplicating
|
||||
N := 0;
|
||||
while (N < fCount) and (fItems[N].Width <> 0)
|
||||
and ((fItems[N].Width <> dmPelsWidth) or (fItems[N].Height <> dmPelsHeight)) do
|
||||
Inc(N);
|
||||
|
||||
if N+1 > fCount then
|
||||
begin
|
||||
SetLength(fItems, N+1);
|
||||
FillChar(fItems[N], SizeOf(TKMScreenResData), #0);
|
||||
Inc(fCount);
|
||||
end;
|
||||
|
||||
if (N < fCount) and (fItems[N].Width = 0) then
|
||||
begin
|
||||
fItems[N].Width := dmPelsWidth;
|
||||
fItems[N].Height := dmPelsHeight;
|
||||
end;
|
||||
|
||||
//Find next empty place and avoid duplicating
|
||||
M := 0;
|
||||
while (N < fCount) and (M < fItems[N].RefRateCount)
|
||||
and (fItems[N].RefRate[M] <> 0)
|
||||
and (fItems[N].RefRate[M] <> dmDisplayFrequency) do
|
||||
Inc(M);
|
||||
|
||||
if M+1 > fItems[N].RefRateCount then
|
||||
begin
|
||||
SetLength(fItems[N].RefRate, M+1);
|
||||
FillChar(fItems[N].RefRate[M], SizeOf(Word), #0);
|
||||
Inc(fItems[N].RefRateCount);
|
||||
end;
|
||||
|
||||
if (M < fItems[N].RefRateCount) and (N < fCount) and (fItems[N].RefRate[M] = 0) then
|
||||
fItems[N].RefRate[M] := dmDisplayFrequency;
|
||||
end;
|
||||
end;
|
||||
{$ENDIF}
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMResolutions.Sort;
|
||||
var
|
||||
I, J, K: Integer;
|
||||
tempScreenResData: TKMScreenResData;
|
||||
tempRefRate: Word;
|
||||
begin
|
||||
for I := 0 to fCount - 1 do
|
||||
begin
|
||||
for J := 0 to fItems[I].RefRateCount - 1 do
|
||||
begin
|
||||
//firstly, refresh rates for each resolution are being sorted
|
||||
K:=J; //iterator will be modified, but we don't want to lose it
|
||||
while ((K>0) and (fItems[I].RefRate[K] < fItems[I].RefRate[K-1]) and
|
||||
//excluding zero values from sorting, so they are kept at the end of array
|
||||
(fItems[I].RefRate[K] > 0)) do
|
||||
begin
|
||||
//Exchange places
|
||||
tempRefRate := fItems[I].RefRate[K];
|
||||
fItems[I].RefRate[K] := fItems[I].RefRate[K-1];
|
||||
fItems[I].RefRate[K-1] := tempRefRate;
|
||||
dec(K);
|
||||
end;
|
||||
end;
|
||||
|
||||
if I = 0 then Continue;
|
||||
J := I; //iterator will be modified, but we don't want to lose it
|
||||
//moving resolution to its final position
|
||||
while ((J>0) and (((fItems[J].Width < fItems[J-1].Width) and
|
||||
//excluding zero values from sorting, so they are kept at the end of array
|
||||
(fItems[J].Width > 0) and (fItems[J].Height > 0)) or
|
||||
((fItems[J].Width = fItems[J-1].Width) and
|
||||
(fItems[J].Height < fItems[J-1].Height)))) do
|
||||
begin
|
||||
//Exchange places
|
||||
tempScreenResData := fItems[J];
|
||||
fItems[J] := fItems[J-1];
|
||||
fItems[J-1] := tempScreenResData;
|
||||
dec(J);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TKMResolutions.GetItem(aIndex: Integer): TKMScreenResData;
|
||||
begin
|
||||
Assert(InRange(aIndex, 0, fCount - 1));
|
||||
Result := fItems[aIndex];
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMResolutions.Restore;
|
||||
begin
|
||||
if not fNeedsRestoring then Exit;
|
||||
{$IFDEF MSWindows}
|
||||
ChangeDisplaySettings(DEVMODE(nil^), 0);
|
||||
{$ENDIF}
|
||||
fNeedsRestoring := False;
|
||||
end;
|
||||
|
||||
|
||||
function TKMResolutions.IsValid(const aResolution: TKMScreenRes): Boolean;
|
||||
begin
|
||||
Result := GetResolutionIDs(aResolution).RefID <> -1;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMResolutions.SetResolution(const aResolution: TKMScreenRes);
|
||||
{$IFDEF MSWindows}
|
||||
var
|
||||
deviceMode: TDeviceMode;
|
||||
{$ENDIF}
|
||||
begin
|
||||
//Double-check anything we get from outside
|
||||
Assert(IsValid(aResolution));
|
||||
|
||||
{$IFDEF MSWindows}
|
||||
ZeroMemory(@deviceMode, SizeOf(deviceMode));
|
||||
with deviceMode do
|
||||
begin
|
||||
dmSize := SizeOf(TDeviceMode);
|
||||
dmPelsWidth := aResolution.Width;
|
||||
dmPelsHeight := aResolution.Height;
|
||||
dmBitsPerPel := 32;
|
||||
dmDisplayFrequency := aResolution.RefRate;
|
||||
dmFields := DM_DISPLAYFREQUENCY or DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
|
||||
end;
|
||||
|
||||
ChangeDisplaySettings(deviceMode, CDS_FULLSCREEN);
|
||||
{$ENDIF}
|
||||
fNeedsRestoring := True; //Resolution was changed so we must restore it when we exit
|
||||
end;
|
||||
|
||||
|
||||
function TKMResolutions.FindCorrect(const aResolution: TKMScreenRes): TKMScreenRes;
|
||||
{$IFDEF MSWindows}
|
||||
var
|
||||
devMode: TDevMode;
|
||||
{$ENDIF}
|
||||
begin
|
||||
//1. Try to reuse current resolution
|
||||
{$IFDEF MSWindows}
|
||||
EnumDisplaySettings(nil, Cardinal(-1){ENUM_CURRENT_SETTINGS}, devMode);
|
||||
with devMode do
|
||||
if SupportedRes(dmPelsWidth, dmPelsHeight, dmDisplayFrequency, dmBitsPerPel) then
|
||||
begin
|
||||
Result.Width := dmPelsWidth;
|
||||
Result.Height := dmPelsHeight;
|
||||
Result.RefRate := dmDisplayFrequency;
|
||||
Exit;
|
||||
end;
|
||||
{$ENDIF}
|
||||
|
||||
//2. Try to use first available resolution
|
||||
if fCount > 0 then
|
||||
begin
|
||||
Result.Width := fItems[0].Width;
|
||||
Result.Height := fItems[0].Height;
|
||||
Result.RefRate := fItems[0].RefRate[0];
|
||||
end
|
||||
else
|
||||
//3. Fallback to windowed mode
|
||||
begin
|
||||
Result.Width := -1;
|
||||
Result.Height := -1;
|
||||
Result.RefRate := -1;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
//we need to set this IDs in settings, so we don't work on "physical" values
|
||||
//and everything is kept inside this class, not in TMainSettings
|
||||
function TKMResolutions.GetResolutionIDs(const aResolution: TKMScreenRes): TKMScreenResIndex;
|
||||
var
|
||||
I, J: Integer;
|
||||
begin
|
||||
Result.ResID := -1;
|
||||
Result.RefID := -1;
|
||||
|
||||
for I := 0 to fCount - 1 do
|
||||
if (fItems[I].Width = aResolution.Width)
|
||||
and (fItems[I].Height = aResolution.Height) then
|
||||
for J := 0 to fItems[I].RefRateCount - 1 do
|
||||
if fItems[I].RefRate[J] = aResolution.RefRate then
|
||||
begin
|
||||
Result.ResID := I;
|
||||
Result.RefID := J;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
@@ -0,0 +1,320 @@
|
||||
unit KM_ServerSettings;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes,
|
||||
{$IFDEF FPC}Forms,{$ENDIF} // Lazarus does not know about UITypes
|
||||
{$IFDEF WDC}UITypes,{$ENDIF} // We use settings in console modules
|
||||
KM_Defaults,
|
||||
KM_Lists,
|
||||
KM_Settings;
|
||||
|
||||
type
|
||||
// Server settings
|
||||
// Saved in the ini file, since we dont want depend dedicated servers with xml units yet (f.e. they use Generics)
|
||||
TKMServerSettings = class(TKMSettings)
|
||||
private
|
||||
//Server
|
||||
fServerPort: string;
|
||||
fServerUDPScanPort: Word;
|
||||
fServerUDPAnnounce: Boolean;
|
||||
fMasterServerAddress: string;
|
||||
fServerName: AnsiString;
|
||||
fMasterAnnounceInterval: Integer;
|
||||
fMaxRooms: Integer;
|
||||
fServerPacketsAccumulatingDelay: Integer;
|
||||
fAutoKickTimeout: Integer;
|
||||
fPingInterval: Integer;
|
||||
fAnnounceServer: Boolean;
|
||||
fHTMLStatusFile: UnicodeString;
|
||||
fServerWelcomeMessage: UnicodeString;
|
||||
|
||||
fServerDynamicFOW: Boolean;
|
||||
fServerMapsRosterEnabled: Boolean;
|
||||
fServerMapsRosterStr: UnicodeString;
|
||||
fServerLimitPTFrom, fServerLimitPTTo: Integer;
|
||||
fServerLimitSpeedFrom, fServerLimitSpeedTo: Single;
|
||||
fServerLimitSpeedAfterPTFrom, fServerLimitSpeedAfterPTTo: Single;
|
||||
|
||||
fServerMapsRoster: TKMMapsCRCList;
|
||||
|
||||
// Server
|
||||
procedure SetMasterServerAddress(const aValue: string);
|
||||
procedure SetServerName(const aValue: AnsiString);
|
||||
procedure SetServerPort(const aValue: string);
|
||||
procedure SetServerUDPAnnounce(aValue: Boolean);
|
||||
procedure SetServerUDPScanPort(const aValue: Word);
|
||||
procedure SetServerWelcomeMessage(const aValue: UnicodeString);
|
||||
procedure SetAnnounceServer(aValue: Boolean);
|
||||
procedure SetAutoKickTimeout(aValue: Integer);
|
||||
procedure SetPingInterval(aValue: Integer);
|
||||
procedure SetMasterAnnounceInterval(eValue: Integer);
|
||||
procedure SetHTMLStatusFile(const eValue: UnicodeString);
|
||||
procedure SetMaxRooms(eValue: Integer);
|
||||
procedure SetServerPacketsAccumulatingDelay(aValue: Integer);
|
||||
procedure SetServerMapsRosterStr(const aValue: UnicodeString);
|
||||
protected
|
||||
procedure LoadFromFile(const aPath: string); override;
|
||||
procedure SaveToFile(const aFilename: UnicodeString); override;
|
||||
|
||||
function GetDefaultSettingsName: string; override;
|
||||
function GetSettingsName: string; override;
|
||||
public
|
||||
constructor Create(aSettingsLoc: TKMSettingsLocation);
|
||||
destructor Destroy; override;
|
||||
|
||||
// Server
|
||||
property ServerPort: string read fServerPort write SetServerPort;
|
||||
property ServerUDPAnnounce: Boolean read fServerUDPAnnounce write SetServerUDPAnnounce;
|
||||
property ServerUDPScanPort: Word read fServerUDPScanPort write SetServerUDPScanPort;
|
||||
property MasterServerAddress: string read fMasterServerAddress write SetMasterServerAddress;
|
||||
property ServerName: AnsiString read fServerName write SetServerName;
|
||||
property MasterAnnounceInterval: Integer read fMasterAnnounceInterval write SetMasterAnnounceInterval;
|
||||
property AnnounceServer: Boolean read fAnnounceServer write SetAnnounceServer;
|
||||
property MaxRooms: Integer read fMaxRooms write SetMaxRooms;
|
||||
property ServerPacketsAccumulatingDelay: Integer read fServerPacketsAccumulatingDelay write SetServerPacketsAccumulatingDelay;
|
||||
|
||||
property AutoKickTimeout: Integer read fAutoKickTimeout write SetAutoKickTimeout;
|
||||
property PingInterval: Integer read fPingInterval write SetPingInterval;
|
||||
property HTMLStatusFile: UnicodeString read fHTMLStatusFile write SetHTMLStatusFile;
|
||||
property ServerWelcomeMessage: UnicodeString read fServerWelcomeMessage write SetServerWelcomeMessage;
|
||||
property ServerDynamicFOW: Boolean read fServerDynamicFOW;
|
||||
property ServerMapsRosterEnabled: Boolean read fServerMapsRosterEnabled;
|
||||
property ServerMapsRosterStr: UnicodeString read fServerMapsRosterStr;
|
||||
property ServerLimitPTFrom: Integer read fServerLimitPTFrom;
|
||||
property ServerLimitPTTo: Integer read fServerLimitPTTo;
|
||||
property ServerLimitSpeedFrom: Single read fServerLimitSpeedFrom;
|
||||
property ServerLimitSpeedTo: Single read fServerLimitSpeedTo;
|
||||
property ServerLimitSpeedAfterPTFrom: Single read fServerLimitSpeedAfterPTFrom;
|
||||
property ServerLimitSpeedAfterPTTo: Single read fServerLimitSpeedAfterPTTo;
|
||||
|
||||
property ServerMapsRoster: TKMMapsCRCList read fServerMapsRoster;
|
||||
end;
|
||||
|
||||
var
|
||||
gServerSettings: TKMServerSettings;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
SysUtils, INIfiles, Math,
|
||||
KM_CommonUtils;
|
||||
|
||||
|
||||
{ TKMServerSettings }
|
||||
constructor TKMServerSettings.Create(aSettingsLoc: TKMSettingsLocation);
|
||||
begin
|
||||
fServerMapsRoster := TKMMapsCRCList.Create;
|
||||
fServerMapsRoster.OnMapsUpdate := SetServerMapsRosterStr;
|
||||
|
||||
inherited Create(aSettingsLoc);
|
||||
|
||||
gServerSettings := Self;
|
||||
end;
|
||||
|
||||
|
||||
destructor TKMServerSettings.Destroy;
|
||||
begin
|
||||
inherited; // Save settings first
|
||||
|
||||
// Cleanup everything afterwards
|
||||
FreeAndNil(fServerMapsRoster);
|
||||
|
||||
gServerSettings := nil;
|
||||
end;
|
||||
|
||||
|
||||
function TKMServerSettings.GetDefaultSettingsName: string;
|
||||
begin
|
||||
Result := SERVER_SETTINGS_FILE;
|
||||
end;
|
||||
|
||||
|
||||
function TKMServerSettings.GetSettingsName: string;
|
||||
const
|
||||
SERVER_SETTINGS_NAME = 'Server settings';
|
||||
begin
|
||||
Result := SERVER_SETTINGS_NAME;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.LoadFromFile(const aPath: string);
|
||||
var
|
||||
ini: TMemIniFile;
|
||||
serverName: UnicodeString;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
ini := TMemIniFile.Create(aPath {$IFDEF WDC}, TEncoding.UTF8 {$ENDIF} );
|
||||
try
|
||||
fServerPort := ini.ReadString ('Server','ServerPort','56789');
|
||||
fServerUDPScanPort := ini.ReadInteger('Server','UDPScanPort',SERVER_DEFAULT_UDP_SCAN_PORT);
|
||||
fServerUDPAnnounce := ini.ReadBool ('Server','UDPAnnounce',True);
|
||||
|
||||
//We call it MasterServerAddressNew to force it to update in everyone's .ini file when we changed address.
|
||||
//If the key stayed the same then everyone would still be using the old value from their settings.
|
||||
fMasterServerAddress := ini.ReadString ('Server','MasterServerAddressNew','http://master.kamremake.com/');
|
||||
fMasterAnnounceInterval := ini.ReadInteger('Server','MasterServerAnnounceInterval',180);
|
||||
fAnnounceServer := ini.ReadBool ('Server','AnnounceDedicatedServer',True);
|
||||
|
||||
serverName := ini.ReadString ('Server','ServerName','KaM Remake Server');
|
||||
fServerName := AnsiString(StrTrimChar(serverName, #39)); //Trim single quotes from the start and from the end of servername
|
||||
|
||||
fMaxRooms := ini.ReadInteger('Server','MaxRooms',16);
|
||||
ServerPacketsAccumulatingDelay := ini.ReadInteger('Server','PacketsAccumulatingDelay',20);
|
||||
fAutoKickTimeout := ini.ReadInteger('Server','AutoKickTimeout',20);
|
||||
fPingInterval := ini.ReadInteger('Server','PingMeasurementInterval',1000);
|
||||
fHTMLStatusFile := ini.ReadString ('Server','HTMLStatusFile','KaM_Remake_Server_Status.html');
|
||||
fServerWelcomeMessage := {$IFDEF FPC} UTF8Decode {$ENDIF} (ini.ReadString ('Server','WelcomeMessage',''));
|
||||
|
||||
fServerDynamicFOW := ini.ReadBool ('Server', 'DynamicFOW', False);
|
||||
fServerMapsRosterEnabled:= ini.ReadBool ('Server', 'MapsRosterEnabled', False);
|
||||
fServerMapsRoster.Enabled := fServerMapsRosterEnabled; //Set enabled before fServerMapsRoster load
|
||||
|
||||
if fServerMapsRosterEnabled then
|
||||
fServerMapsRosterStr := ini.ReadString('Server', 'MapsRoster', '')
|
||||
else
|
||||
fServerMapsRosterStr := '';
|
||||
|
||||
fServerMapsRoster.LoadFromString(fServerMapsRosterStr);
|
||||
|
||||
fServerLimitPTFrom := ini.ReadInteger('Server', 'LimitPTFrom', 0);
|
||||
fServerLimitPTTo := ini.ReadInteger('Server', 'LimitPTTo', 300);
|
||||
fServerLimitSpeedFrom := ini.ReadFloat ('Server', 'LimitSpeedFrom', 0);
|
||||
fServerLimitSpeedTo := ini.ReadFloat ('Server', 'LimitSpeedTo', 10);
|
||||
fServerLimitSpeedAfterPTFrom := ini.ReadFloat('Server', 'LimitSpeedAfterPTFrom', 0);
|
||||
fServerLimitSpeedAfterPTTo := ini.ReadFloat('Server', 'LimitSpeedAfterPTTo', 10);
|
||||
finally
|
||||
ini.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
// Don't rewrite the file for each individual change, do it in one batch for simplicity
|
||||
procedure TKMServerSettings.SaveToFile(const aFilename: UnicodeString);
|
||||
var
|
||||
ini: TMemIniFile;
|
||||
begin
|
||||
if BLOCK_FILE_WRITE then
|
||||
Exit;
|
||||
|
||||
ForceDirectories(ExtractFilePath(ExpandFileName(aFilename))); // Create folder, if it does not exist
|
||||
|
||||
ini := TMemIniFile.Create(aFilename {$IFDEF WDC}, TEncoding.UTF8 {$ENDIF} );
|
||||
try
|
||||
ini.WriteString ('Server','ServerName', '''' + UnicodeString(fServerName) + ''''); //Add single quotes for server name
|
||||
ini.WriteString ('Server','WelcomeMessage', {$IFDEF FPC} UTF8Encode {$ENDIF}(fServerWelcomeMessage));
|
||||
ini.WriteString ('Server','ServerPort', fServerPort);
|
||||
ini.WriteInteger('Server','UDPScanPort', fServerUDPScanPort);
|
||||
ini.WriteBool ('Server','UDPAnnounce', fServerUDPAnnounce);
|
||||
ini.WriteBool ('Server','AnnounceDedicatedServer', fAnnounceServer);
|
||||
ini.WriteInteger('Server','MaxRooms', fMaxRooms);
|
||||
ini.WriteInteger('Server','PacketsAccumulatingDelay', fServerPacketsAccumulatingDelay);
|
||||
ini.WriteString ('Server','HTMLStatusFile', fHTMLStatusFile);
|
||||
ini.WriteInteger('Server','MasterServerAnnounceInterval', fMasterAnnounceInterval);
|
||||
ini.WriteString ('Server','MasterServerAddressNew', fMasterServerAddress);
|
||||
ini.WriteInteger('Server','AutoKickTimeout', fAutoKickTimeout);
|
||||
ini.WriteInteger('Server','PingMeasurementInterval', fPingInterval);
|
||||
|
||||
ini.WriteBool ('Server','DynamicFOW', fServerDynamicFOW);
|
||||
ini.WriteBool ('Server','MapsRosterEnabled', fServerMapsRosterEnabled);
|
||||
ini.WriteString ('Server','MapsRoster', fServerMapsRosterStr);
|
||||
ini.WriteInteger('Server','LimitPTFrom', fServerLimitPTFrom);
|
||||
ini.WriteInteger('Server','LimitPTTo', fServerLimitPTTo);
|
||||
ini.WriteFloat ('Server','LimitSpeedFrom', fServerLimitSpeedFrom);
|
||||
ini.WriteFloat ('Server','LimitSpeedTo', fServerLimitSpeedTo);
|
||||
ini.WriteFloat ('Server','LimitSpeedAfterPTFrom', fServerLimitSpeedAfterPTFrom);
|
||||
ini.WriteFloat ('Server','LimitSpeedAfterPTTo', fServerLimitSpeedAfterPTTo);
|
||||
|
||||
ini.UpdateFile; //Write changes to file
|
||||
finally
|
||||
ini.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetServerPacketsAccumulatingDelay(aValue: Integer);
|
||||
begin
|
||||
fServerPacketsAccumulatingDelay := EnsureRange(aValue, 0, 1000); //This is rough restrictions. Real one are in TKMNetServer
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetServerPort(const aValue: string);
|
||||
begin
|
||||
fServerPort := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetServerUDPAnnounce(aValue: Boolean);
|
||||
begin
|
||||
fServerUDPAnnounce := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetServerUDPScanPort(const aValue: Word);
|
||||
begin
|
||||
fServerUDPScanPort := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetServerMapsRosterStr(const aValue: UnicodeString);
|
||||
begin
|
||||
fServerMapsRosterStr := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetMasterServerAddress(const aValue: string);
|
||||
begin
|
||||
fMasterServerAddress := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetServerName(const aValue: AnsiString);
|
||||
begin
|
||||
fServerName := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetMaxRooms(eValue: Integer);
|
||||
begin
|
||||
fMaxRooms := eValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetHTMLStatusFile(const eValue: UnicodeString);
|
||||
begin
|
||||
fHTMLStatusFile := eValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetMasterAnnounceInterval(eValue: Integer);
|
||||
begin
|
||||
fMasterAnnounceInterval := eValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetPingInterval(aValue: Integer);
|
||||
begin
|
||||
fPingInterval := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetAutoKickTimeout(aValue: Integer);
|
||||
begin
|
||||
fAutoKickTimeout := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetAnnounceServer(aValue: Boolean);
|
||||
begin
|
||||
fAnnounceServer := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMServerSettings.SetServerWelcomeMessage(const aValue: UnicodeString);
|
||||
begin
|
||||
fServerWelcomeMessage := aValue;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
unit KM_Settings;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
|
||||
type
|
||||
TKMSettingsLocation = (
|
||||
slExeDir, // In the games folder (handy for debug)
|
||||
slShared // In the MyDocuments when possible (good for players to retain the settings between game builds)
|
||||
);
|
||||
|
||||
// Abstract settings entity
|
||||
TKMSettings = class abstract
|
||||
private
|
||||
fSettingsLoc: TKMSettingsLocation;
|
||||
procedure LoadFromDefaultFile;
|
||||
procedure SaveToDefaultFile;
|
||||
|
||||
function GetDirectory: string;
|
||||
protected
|
||||
function GetDefaultSettingsName: string; virtual; abstract;
|
||||
|
||||
procedure LoadFromFile(const aPath: string); virtual; abstract;
|
||||
procedure SaveToFile(const aPath: UnicodeString); virtual; abstract;
|
||||
|
||||
function GetSettingsName: string; virtual; abstract;
|
||||
|
||||
function GetPath: string;
|
||||
public
|
||||
constructor Create(aSettingsLoc: TKMSettingsLocation);
|
||||
destructor Destroy; override;
|
||||
|
||||
property Path: string read GetPath;
|
||||
|
||||
procedure ReloadSettings;
|
||||
procedure SaveSettings;
|
||||
|
||||
class function GetDir(aSettingsLoc: TKMSettingsLocation = slShared): string;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
SysUtils,
|
||||
KM_Defaults,
|
||||
KM_FileIO,
|
||||
KM_CommonUtils,
|
||||
KM_Log;
|
||||
|
||||
|
||||
{ TKMSettings }
|
||||
constructor TKMSettings.Create(aSettingsLoc: TKMSettingsLocation);
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
fSettingsLoc := aSettingsLoc;
|
||||
|
||||
LoadFromDefaultFile;
|
||||
// Save settings to default directory immediately
|
||||
// If there were any problems with settings then we want to be able to customise them
|
||||
SaveToDefaultFile;
|
||||
end;
|
||||
|
||||
|
||||
destructor TKMSettings.Destroy;
|
||||
begin
|
||||
SaveToDefaultFile;
|
||||
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
function TKMSettings.GetPath: string;
|
||||
begin
|
||||
Result := GetDirectory + GetDefaultSettingsName;
|
||||
end;
|
||||
|
||||
|
||||
function TKMSettings.GetDirectory: string;
|
||||
begin
|
||||
Result := GetDir(fSettingsLoc);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMSettings.LoadFromDefaultFile;
|
||||
var
|
||||
path: string;
|
||||
begin
|
||||
path := GetPath;
|
||||
gLog.AddTime('Start loading "%s" from "%s"', [GetSettingsName, path]);
|
||||
LoadFromFile(path);
|
||||
gLog.AddTime('"%s" was successfully loaded from "%s"', [GetSettingsName, path]);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMSettings.SaveToDefaultFile;
|
||||
var
|
||||
saveFolder, path, errorStr: UnicodeString;
|
||||
begin
|
||||
saveFolder := GetDirectory;
|
||||
ForceDirectories(saveFolder);
|
||||
path := saveFolder + GetDefaultSettingsName;
|
||||
gLog.AddTime('Start saving "%s" to "%s"', [GetSettingsName, path]);
|
||||
|
||||
// Debug output of the current stacktrace
|
||||
// We want to catch odd bug, when 'Start saving server settings' is called twice one after another
|
||||
// (without '%s was successfully saved string in the log)
|
||||
if DBG_LOG_SETTINGS_SAVE_CALLSTACK then
|
||||
gLog.AddNoTime(GetStackTrace(20), False);
|
||||
|
||||
// Try to save several times, in case file is blocked (by antivirus f.e.)
|
||||
if not TryExecuteMethod(SaveToFile, path, 'SaveToFile', errorStr) then
|
||||
raise Exception.Create('Can''t save settings to file "' + path + '": ' + errorStr);
|
||||
|
||||
gLog.AddTime('"%s" was successfully saved to "%s"', [GetSettingsName, path]);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMSettings.ReloadSettings;
|
||||
begin
|
||||
LoadFromDefaultFile;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMSettings.SaveSettings;
|
||||
begin
|
||||
if SKIP_SETTINGS_SAVE then Exit;
|
||||
|
||||
SaveToDefaultFile;
|
||||
end;
|
||||
|
||||
|
||||
class function TKMSettings.GetDir(aSettingsLoc: TKMSettingsLocation = slShared): string;
|
||||
begin
|
||||
if USE_KMR_DIR_FOR_SETTINGS or (aSettingsLoc = slExeDir) then
|
||||
Result := ExtractFilePath(ParamStr(0))
|
||||
else
|
||||
Result := CreateAndGetDocumentsSavePath; // Use %My documents%/My Games/
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
+299
@@ -0,0 +1,299 @@
|
||||
unit KM_SettingsDev;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
SysUtils, StrUtils, Classes, Math,
|
||||
ComCtrls, Controls, ExtCtrls, StdCtrls,
|
||||
Forms, Spin
|
||||
{$IFDEF Unix} , LCLIntf, LCLType {$ENDIF}
|
||||
;
|
||||
|
||||
|
||||
type
|
||||
TKMDebugFormState = ( fsNone, // No debug panel or menu are open
|
||||
fsDebugMenu, // Only debug menu is visible
|
||||
fsDebugFull); // Debug panel and menu are visible
|
||||
|
||||
// Manager of F11 controls settings save/load
|
||||
TKMDevSettings = class
|
||||
private
|
||||
fSettingsPath: UnicodeString;
|
||||
|
||||
fSkipSave: Boolean;
|
||||
|
||||
fDebugFormState: TKMDebugFormState; // State of the Debug form
|
||||
|
||||
fMainGroup: TCategoryPanelGroup;
|
||||
fDontCollapse: TCategoryPanel;
|
||||
|
||||
procedure DoLoad;
|
||||
procedure DoSave;
|
||||
function GetXmlSectionName(aPanel: TCategoryPanel): string;
|
||||
public
|
||||
constructor Create(const aExeDir: string; aMainGroup: TCategoryPanelGroup; aDontCollapse: TCategoryPanel);
|
||||
|
||||
property DebugFormState: TKMDebugFormState read fDebugFormState write fDebugFormState;
|
||||
property SkipSave: Boolean read fSkipSave write fSkipSave;
|
||||
|
||||
procedure Load;
|
||||
procedure Save;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
KM_Defaults, KM_Log, KM_IoXML;
|
||||
|
||||
|
||||
|
||||
{ TKMDevSettings }
|
||||
constructor TKMDevSettings.Create(const aExeDir: string; aMainGroup: TCategoryPanelGroup; aDontCollapse: TCategoryPanel);
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
fSettingsPath := aExeDir + DEV_SETTINGS_XML_FILENAME;
|
||||
|
||||
fMainGroup := aMainGroup;
|
||||
fDontCollapse := aDontCollapse;
|
||||
end;
|
||||
|
||||
|
||||
function TKMDevSettings.GetXmlSectionName(aPanel: TCategoryPanel): string;
|
||||
begin
|
||||
Result := StringReplace(aPanel.Caption, ' ', '_', [rfReplaceAll]);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDevSettings.DoLoad;
|
||||
|
||||
procedure LoadSubPanel(aPanel: TWinControl; aNode: TKMXmlNode);
|
||||
var
|
||||
I: Integer;
|
||||
ctrl: TControl;
|
||||
nSection: TKMXmlNode;
|
||||
begin
|
||||
for I := 0 to aPanel.ControlCount - 1 do
|
||||
begin
|
||||
ctrl := aPanel.Controls[I];
|
||||
|
||||
if ctrl is TGroupBox then
|
||||
LoadSubPanel(TGroupBox(ctrl), aNode)
|
||||
else
|
||||
if ctrl is TForm then
|
||||
LoadSubPanel(TForm(ctrl), aNode)
|
||||
else
|
||||
if (ctrl is TCheckBox)
|
||||
or (ctrl is TTrackBar)
|
||||
or (ctrl is TRadioGroup)
|
||||
or (ctrl is TSpinEdit)
|
||||
or (ctrl is TEdit) then
|
||||
if aNode.HasChild(ctrl.Name) then
|
||||
begin
|
||||
nSection := aNode.FindNode(ctrl.Name); // Add section only if its needed
|
||||
|
||||
if (ctrl is TCheckBox) and nSection.HasAttribute('Checked') then
|
||||
TCheckBox(ctrl).Checked := nSection.Attributes['Checked'].AsBoolean
|
||||
else
|
||||
if (ctrl is TTrackBar) and nSection.HasAttribute('Position') then
|
||||
TTrackBar(ctrl).Position := nSection.Attributes['Position'].AsInteger
|
||||
else
|
||||
if (ctrl is TRadioGroup) and nSection.HasAttribute('ItemIndex') then
|
||||
TRadioGroup(ctrl).ItemIndex := nSection.Attributes['ItemIndex'].AsInteger
|
||||
else
|
||||
if (ctrl is TSpinEdit) and nSection.HasAttribute('Value') then
|
||||
TSpinEdit(ctrl).Value := nSection.Attributes['Value'].AsInteger
|
||||
else
|
||||
if (ctrl is TEdit) and nSection.HasAttribute('Text') then
|
||||
TEdit(ctrl).Text := nSection.Attributes['Text'].AsString;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
var
|
||||
I: Integer;
|
||||
newXML: TKMXMLDocument;
|
||||
cp: TCategoryPanel;
|
||||
cpSurface: TCategoryPanelSurface;
|
||||
cpName: string;
|
||||
nRoot, nDebugForm, nSection: TKMXmlNode;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
|
||||
gLog.AddTime('Loading dev settings from file ''' + fSettingsPath + '''');
|
||||
|
||||
fSkipSave := True;
|
||||
|
||||
try
|
||||
// Apply default settings
|
||||
if not FileExists(fSettingsPath) then
|
||||
begin
|
||||
for I := 0 to fMainGroup.Panels.Count - 1 do
|
||||
TCategoryPanel(fMainGroup.Panels[I]).Collapsed := True;
|
||||
|
||||
fDontCollapse.Collapsed := False; //The only not collapsed section
|
||||
Exit;
|
||||
end;
|
||||
|
||||
//Load dev data from XML
|
||||
newXML := TKMXMLDocument.Create;
|
||||
newXML.LoadFromFile(fSettingsPath);
|
||||
nRoot := newXML.Root;
|
||||
|
||||
nDebugForm := nRoot.AddOrFindChild('DebugForm');
|
||||
fDebugFormState := TKMDebugFormState(nDebugForm.Attributes['State'].AsInteger(0));
|
||||
|
||||
for I := 0 to fMainGroup.Panels.Count - 1 do
|
||||
begin
|
||||
cp := TCategoryPanel(fMainGroup.Panels[I]);
|
||||
cpName := GetXmlSectionName(cp);
|
||||
|
||||
if nDebugForm.HasChild(cpName) then
|
||||
begin
|
||||
nSection := nDebugForm.FindNode(cpName);
|
||||
cp.Collapsed := nSection.Attributes['Collapsed'].AsBoolean(True);
|
||||
|
||||
if (cp.ControlCount > 0) and (cp.Controls[0] is TCategoryPanelSurface) then
|
||||
begin
|
||||
cpSurface := TCategoryPanelSurface(cp.Controls[0]);
|
||||
LoadSubPanel(cpSurface, nSection);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
newXML.Free;
|
||||
finally
|
||||
fSkipSave := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
// Save dev settings to kmr_dev.xml
|
||||
procedure TKMDevSettings.DoSave;
|
||||
|
||||
procedure SaveSubPanel(aPanel: TWinControl; aNode: TKMXmlNode);
|
||||
var
|
||||
I: Integer;
|
||||
ctrl: TControl;
|
||||
nSection: TKMXmlNode;
|
||||
begin
|
||||
for I := 0 to aPanel.ControlCount - 1 do
|
||||
begin
|
||||
ctrl := aPanel.Controls[I];
|
||||
|
||||
if ctrl is TGroupBox then
|
||||
SaveSubPanel(TGroupBox(ctrl), aNode)
|
||||
else
|
||||
if ctrl is TForm then
|
||||
SaveSubPanel(TForm(ctrl), aNode)
|
||||
else
|
||||
if (ctrl is TCheckBox)
|
||||
or (ctrl is TTrackBar)
|
||||
or (ctrl is TRadioGroup)
|
||||
or (ctrl is TSpinEdit)
|
||||
or (ctrl is TEdit) then
|
||||
begin
|
||||
nSection := aNode.AddOrFindChild(ctrl.Name); // Add section only if its needed
|
||||
if ctrl is TCheckBox then
|
||||
nSection.Attributes['Checked'] := TCheckBox(ctrl).Checked
|
||||
else
|
||||
if ctrl is TTrackBar then
|
||||
nSection.Attributes['Position'] := TTrackBar(ctrl).Position
|
||||
else
|
||||
if ctrl is TRadioGroup then
|
||||
nSection.Attributes['ItemIndex'] := TRadioGroup(ctrl).ItemIndex
|
||||
else
|
||||
if ctrl is TSpinEdit then
|
||||
nSection.Attributes['Value'] := TSpinEdit(ctrl).Value
|
||||
else
|
||||
if ctrl is TEdit then
|
||||
nSection.Attributes['Text'] := TEdit(ctrl).Text;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
var
|
||||
I: Integer;
|
||||
newXML: TKMXMLDocument;
|
||||
cp: TCategoryPanel;
|
||||
cpSurface: TCategoryPanelSurface;
|
||||
nRoot, nDebugForm, nSection: TKMXmlNode;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
|
||||
if fSkipSave then Exit; // We can invoke Save on Load, if f.e. changed Collapsed state of the subpanel
|
||||
|
||||
gLog.AddTime('Saving dev settings to file ''' + fSettingsPath + '''');
|
||||
|
||||
//Save dev data to XML
|
||||
newXML := TKMXMLDocument.Create;
|
||||
newXML.LoadFromFile(fSettingsPath);
|
||||
nRoot := newXML.Root;
|
||||
|
||||
nDebugForm := nRoot.AddOrFindChild('DebugForm');
|
||||
nDebugForm.Attributes['State'] := Ord(fDebugFormState);
|
||||
|
||||
for I := 0 to fMainGroup.Panels.Count - 1 do
|
||||
begin
|
||||
cp := TCategoryPanel(fMainGroup.Panels[I]);
|
||||
|
||||
nSection := nDebugForm.AddOrFindChild(GetXmlSectionName(cp));
|
||||
|
||||
nSection.Attributes['Collapsed'] := cp.Collapsed;
|
||||
|
||||
if (cp.ControlCount > 0) and (cp.Controls[0] is TCategoryPanelSurface) then
|
||||
begin
|
||||
cpSurface := TCategoryPanelSurface(cp.Controls[0]);
|
||||
SaveSubPanel(cpSurface, nSection);
|
||||
end;
|
||||
end;
|
||||
|
||||
newXML.SaveToFile(fSettingsPath);
|
||||
newXML.Free;
|
||||
end;
|
||||
|
||||
|
||||
// Load dev settings from kmr_dev.xml
|
||||
procedure TKMDevSettings.Load;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
|
||||
{$IFDEF DEBUG}
|
||||
// allow crash while debugging
|
||||
DoLoad;
|
||||
{$ELSE}
|
||||
try
|
||||
// Skip crash on released version, only log the error
|
||||
DoLoad;
|
||||
except
|
||||
on E: Exception do
|
||||
gLog.AddTime('Error while loading dev settings from ''' + fSettingsPath + ''':' + sLineBreak + E.Message
|
||||
{$IFDEF WDC}+ sLineBreak + E.StackTrace{$ENDIF}
|
||||
);
|
||||
end;
|
||||
{$ENDIF}
|
||||
end;
|
||||
|
||||
|
||||
// Save dev settings to kmr_dev.xml
|
||||
procedure TKMDevSettings.Save;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
|
||||
{$IFDEF DEBUG}
|
||||
// allow crash while debugging
|
||||
DoSave;
|
||||
{$ELSE}
|
||||
try
|
||||
// Skip crash on released version, only log the error
|
||||
DoSave;
|
||||
except
|
||||
on E: Exception do
|
||||
gLog.AddTime('Error while saving dev settings to ''' + fSettingsPath + ''':' + sLineBreak + E.Message
|
||||
{$IFDEF WDC}+ sLineBreak + E.StackTrace{$ENDIF}
|
||||
);
|
||||
end;
|
||||
{$ENDIF}
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
@@ -0,0 +1,62 @@
|
||||
unit KM_SettingsXML;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
KM_Settings,
|
||||
KM_IoXML;
|
||||
|
||||
type
|
||||
// Common implementation for settings stored and loaded from XML
|
||||
TKMSettingsXML = class(TKMSettings)
|
||||
private
|
||||
fXML: TKMXmlDocument;
|
||||
protected
|
||||
fRoot: TKMXmlNode;
|
||||
procedure LoadFromFile(const aPath: string); override;
|
||||
procedure SaveToFile(const aPath: UnicodeString); override;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
|
||||
{ TKMSettingsXML }
|
||||
constructor TKMSettingsXML.Create;
|
||||
begin
|
||||
fXML := TKMXmlDocument.Create;
|
||||
|
||||
// Prefer to store XML settings in the shared folder
|
||||
inherited Create(slShared);
|
||||
end;
|
||||
|
||||
|
||||
destructor TKMSettingsXML.Destroy;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
fXML.Free;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMSettingsXML.LoadFromFile(const aPath: string);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
fXML.LoadFromFile(aPath);
|
||||
fRoot := fXML.Root;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMSettingsXML.SaveToFile(const aPath: UnicodeString);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
fXML.SaveToFile(aPath);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
unit KM_WindowParams;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
{$IFDEF WDC}UITypes,{$ENDIF} //We use settings in console modules
|
||||
Forms,
|
||||
KM_Points;
|
||||
|
||||
type
|
||||
TKMWindowParamsRecord = record
|
||||
Width, Height, Left, Top: SmallInt;
|
||||
State: TWindowState;
|
||||
Position: TPosition;
|
||||
end;
|
||||
|
||||
TKMWindowParams = class
|
||||
private
|
||||
fWidth, fHeight, fLeft, fTop: SmallInt; // Window size/position on the screen
|
||||
fState: TWindowState; // Window state (wsNormal/wsMaximized)
|
||||
fPosition: TPosition; // Window position (poDesigned/poCenterScreen)
|
||||
fFixedPosition: Boolean; // Set if user can fix position style in case other styles do not work properly
|
||||
// There was bugreport when game window appeared only with poCenterScreen position on Linux
|
||||
fLockParams: Boolean; // Lock updating window params, used when Fullscreen turned On
|
||||
fIsChanged: Boolean;
|
||||
fNeedResetToDefaults: Boolean;
|
||||
procedure SetHeight(const aValue: SmallInt);
|
||||
procedure SetWidth(const aValue: SmallInt); // Flag, when set params should be updated with defaults
|
||||
public
|
||||
constructor Create;
|
||||
property Width: SmallInt read fWidth write SetWidth;
|
||||
property Height: SmallInt read fHeight write SetHeight;
|
||||
property Left: SmallInt read fLeft write fLeft;
|
||||
property Top: SmallInt read fTop write fTop;
|
||||
property State: TWindowState read fState write fState;
|
||||
property Position: TPosition read fPosition write fPosition;
|
||||
property FixedPosition: Boolean read fFixedPosition write fFixedPosition;
|
||||
property IsChanged: Boolean read fIsChanged;
|
||||
property NeedResetToDefaults: Boolean read fNeedResetToDefaults write fNeedResetToDefaults;
|
||||
|
||||
procedure ApplyWindowParams(const aParams: TKMWindowParamsRecord; aDefaults: Boolean = False);
|
||||
procedure LockParams;
|
||||
procedure UnlockParams;
|
||||
function IsValid(aMonitorsInfo: TKMPointArray): Boolean;
|
||||
|
||||
function ObjToString: String;
|
||||
end;
|
||||
|
||||
implementation
|
||||
uses
|
||||
Math, SysUtils, TypInfo,
|
||||
KM_Defaults;
|
||||
|
||||
|
||||
{ TKMWindowParams }
|
||||
constructor TKMWindowParams.Create;
|
||||
begin
|
||||
inherited;
|
||||
fIsChanged := False;
|
||||
fLockParams := False;
|
||||
fNeedResetToDefaults := False;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWindowParams.ApplyWindowParams(const aParams: TKMWindowParamsRecord; aDefaults: Boolean = False);
|
||||
begin
|
||||
if not fLockParams then
|
||||
begin
|
||||
Width := aParams.Width; // Use property to use setter for value validation
|
||||
Height := aParams.Height; // Use property to use setter for value validation
|
||||
fLeft := aParams.Left;
|
||||
fTop := aParams.Top;
|
||||
fState := aParams.State;
|
||||
// Do not apply Position property if FixedPosition is set
|
||||
if not fFixedPosition then
|
||||
fPosition := aParams.Position;
|
||||
fIsChanged := True;
|
||||
fNeedResetToDefaults := aDefaults;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWindowParams.LockParams;
|
||||
begin
|
||||
fLockParams := True;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWindowParams.UnlockParams;
|
||||
begin
|
||||
fLockParams := False;
|
||||
end;
|
||||
|
||||
|
||||
// Check window param, with current Screen object
|
||||
function TKMWindowParams.IsValid(aMonitorsInfo: TKMPointArray): Boolean;
|
||||
const
|
||||
WINDOW_AT_EDGE_GAP = 100; // in px, how close could be window near the screen edge (left or top)
|
||||
var
|
||||
I, ScreenMaxWidth, ScreenMaxHeight: Integer;
|
||||
begin
|
||||
ScreenMaxWidth := 0;
|
||||
ScreenMaxHeight := 0;
|
||||
// Calc Max width/height for multi screen systems
|
||||
// Assume appending monitor screens left to right, so summarise width, get max of height
|
||||
for I := Low(aMonitorsInfo) to High(aMonitorsInfo) do
|
||||
begin
|
||||
ScreenMaxWidth := ScreenMaxWidth + aMonitorsInfo[I].X;
|
||||
ScreenMaxHeight := Max(ScreenMaxHeight, aMonitorsInfo[I].Y);
|
||||
end;
|
||||
|
||||
// Do not let put window too much left or right. 100px is enough to get it back in that case
|
||||
Result := (fWidth >= RESOLUTION_WIDTH_MIN)
|
||||
and (fWidth <= ScreenMaxWidth)
|
||||
and (fLeft <= ScreenMaxWidth - WINDOW_AT_EDGE_GAP)
|
||||
and (fHeight >= RESOLUTION_HEIGHT_MIN)
|
||||
and (fHeight <= ScreenMaxHeight)
|
||||
and (fTop <= ScreenMaxHeight - WINDOW_AT_EDGE_GAP)
|
||||
and (fState in [TWindowState.wsNormal, TWindowState.wsMaximized]);
|
||||
// it seems there is no need to test Position property
|
||||
end;
|
||||
|
||||
|
||||
function TKMWindowParams.ObjToString: String;
|
||||
begin
|
||||
Result := Format('Left = %d Top = %d Width = %d Height = %d State = %s Position = %s FixedPosition = %s',
|
||||
[fLeft, fTop, fWidth, fHeight,
|
||||
GetEnumName(TypeInfo(TWindowState), Integer(fState)),
|
||||
GetEnumName(TypeInfo(TPosition), Integer(fPosition)),
|
||||
BoolToStr(fFixedPosition, True)]);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWindowParams.SetHeight(const aValue: SmallInt);
|
||||
begin
|
||||
fHeight := Max(aValue, RESOLUTION_HEIGHT_MIN);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWindowParams.SetWidth(const aValue: SmallInt);
|
||||
begin
|
||||
fWidth := Max(aValue, RESOLUTION_WIDTH_MIN);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
Reference in new issue
Block a user