sync: restore source from Google Drive
Sync changed files to Google Drive / sync (push) Successful in 1h45m13s

This commit is contained in:
admin_d committed 2026-09-12 16:50:30 +05:00
1 parent e2d8247e91
commit 7063d1b1c9
993 files changed
+949976

No files matched your search

+162
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+320
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+62
View File
@@ -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
View File
@@ -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.