Added: tracking of Modified state
Added: saving / reverting changes Fixed: access violation on preview with an empty map list
This commit is contained in:
parent
47129c0b32
commit
12eba2e832
@ -14,7 +14,8 @@ uses
|
|||||||
Game.List in 'source\model\Game.List.pas',
|
Game.List in 'source\model\Game.List.pas',
|
||||||
Persist.GameList in 'source\persist\Persist.GameList.pas',
|
Persist.GameList in 'source\persist\Persist.GameList.pas',
|
||||||
Forms.Map in 'source\view\Forms.Map.pas' {MapForm},
|
Forms.Map in 'source\view\Forms.Map.pas' {MapForm},
|
||||||
Frame.MapPreview in 'source\view\Frame.MapPreview.pas' {MapPreviewFrame: TFrame};
|
Frame.MapPreview in 'source\view\Frame.MapPreview.pas' {MapPreviewFrame: TFrame},
|
||||||
|
UDKIniFile in 'source\UDKIniFile.pas';
|
||||||
|
|
||||||
{$R *.res}
|
{$R *.res}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
<VerInfo_Keys>CompanyName=X²Software;FileDescription=Chivalry Server Launcher;FileVersion=0.1.0.0;InternalName=;LegalCopyright=Copyright (c) 2014 X²Software;LegalTrademarks=;OriginalFilename=ChivalryServerLauncher.exe;ProductName=Chivalry Server Launcher;ProductVersion=0.1</VerInfo_Keys>
|
<VerInfo_Keys>CompanyName=X²Software;FileDescription=Chivalry Server Launcher;FileVersion=0.1.0.0;InternalName=;LegalCopyright=Copyright (c) 2014 X²Software;LegalTrademarks=;OriginalFilename=ChivalryServerLauncher.exe;ProductName=Chivalry Server Launcher;ProductVersion=0.1</VerInfo_Keys>
|
||||||
<VerInfo_MinorVer>1</VerInfo_MinorVer>
|
<VerInfo_MinorVer>1</VerInfo_MinorVer>
|
||||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
||||||
<VerInfo_Locale>1043</VerInfo_Locale>
|
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||||
<VerInfo_MajorVer>0</VerInfo_MajorVer>
|
<VerInfo_MajorVer>0</VerInfo_MajorVer>
|
||||||
<Icon_MainIcon>resources\icons\MainIcon.ico</Icon_MainIcon>
|
<Icon_MainIcon>resources\icons\MainIcon.ico</Icon_MainIcon>
|
||||||
<Manifest_File>None</Manifest_File>
|
<Manifest_File>None</Manifest_File>
|
||||||
@ -69,6 +69,7 @@
|
|||||||
<DCC_RemoteDebug>true</DCC_RemoteDebug>
|
<DCC_RemoteDebug>true</DCC_RemoteDebug>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Cfg_1_Win64)'!=''">
|
<PropertyGroup Condition="'$(Cfg_1_Win64)'!=''">
|
||||||
|
<DCC_DebugDCUs>true</DCC_DebugDCUs>
|
||||||
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
|
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
|
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
|
||||||
@ -104,6 +105,7 @@
|
|||||||
<FormType>dfm</FormType>
|
<FormType>dfm</FormType>
|
||||||
<DesignClass>TFrame</DesignClass>
|
<DesignClass>TFrame</DesignClass>
|
||||||
</DCCReference>
|
</DCCReference>
|
||||||
|
<DCCReference Include="source\UDKIniFile.pas"/>
|
||||||
<BuildConfiguration Include="Base">
|
<BuildConfiguration Include="Base">
|
||||||
<Key>Base</Key>
|
<Key>Base</Key>
|
||||||
</BuildConfiguration>
|
</BuildConfiguration>
|
||||||
|
Binary file not shown.
444
source/UDKIniFile.pas
Normal file
444
source/UDKIniFile.pas
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
unit UDKIniFile;
|
||||||
|
|
||||||
|
interface
|
||||||
|
uses
|
||||||
|
System.Classes,
|
||||||
|
System.IniFiles,
|
||||||
|
System.SysUtils;
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
{ Basically a TMemIniFile with benefits (TMemIniFile isn't virtual enough
|
||||||
|
to allow simple additions to it). Adds helpers to handle multiple keys
|
||||||
|
with the same name. }
|
||||||
|
TUDKIniFile = class(TCustomIniFile)
|
||||||
|
private
|
||||||
|
FSections: TStringList;
|
||||||
|
FEncoding: TEncoding;
|
||||||
|
|
||||||
|
function GetCaseSensitive: Boolean;
|
||||||
|
procedure SetCaseSensitive(Value: Boolean);
|
||||||
|
protected
|
||||||
|
function AddSection(const ASection: string): TStrings;
|
||||||
|
procedure LoadValues;
|
||||||
|
|
||||||
|
property Sections: TStringList read FSections;
|
||||||
|
public
|
||||||
|
constructor Create(const AFileName: string); overload;
|
||||||
|
constructor Create(const AFileName: string; AEncoding: TEncoding); overload;
|
||||||
|
destructor Destroy; override;
|
||||||
|
|
||||||
|
procedure Clear;
|
||||||
|
|
||||||
|
function ReadString(const ASection, AIdent, ADefault: string): string; override;
|
||||||
|
procedure DeleteKey(const ASection, AIdent: string); override;
|
||||||
|
procedure EraseSection(const ASection: string); override;
|
||||||
|
procedure ReadSection(const ASection: string; AStrings: TStrings); override;
|
||||||
|
procedure ReadSections(AStrings: TStrings); override;
|
||||||
|
procedure ReadSectionValues(const ASection: string; AStrings: TStrings); override;
|
||||||
|
procedure UpdateFile; override;
|
||||||
|
procedure WriteString(const ASection, AIdent, AValue: string); override;
|
||||||
|
|
||||||
|
procedure GetStrings(AList: TStrings);
|
||||||
|
procedure SetStrings(AList: TStrings);
|
||||||
|
|
||||||
|
{ Helpers for duplicate keys }
|
||||||
|
procedure ReadDuplicateStrings(const ASection, AIdent: string; AList: TStrings);
|
||||||
|
procedure WriteDuplicateString(const ASection, AIdent, AValue: string);
|
||||||
|
procedure DeleteDuplicateKeys(const ASection, AIdent: string);
|
||||||
|
|
||||||
|
property CaseSensitive: Boolean read GetCaseSensitive write SetCaseSensitive;
|
||||||
|
property Encoding: TEncoding read FEncoding write FEncoding;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
implementation
|
||||||
|
type
|
||||||
|
{ Added here so we can access the protected members }
|
||||||
|
TUDKHashedStringList = class(THashedStringList);
|
||||||
|
|
||||||
|
|
||||||
|
{ TUDKIniFile }
|
||||||
|
constructor TUDKIniFile.Create(const AFileName: string);
|
||||||
|
begin
|
||||||
|
Create(AFilename, nil);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
constructor TUDKIniFile.Create(const AFileName: string; AEncoding: TEncoding);
|
||||||
|
begin
|
||||||
|
inherited Create(AFileName);
|
||||||
|
|
||||||
|
FEncoding := AEncoding;
|
||||||
|
FSections := TUDKHashedStringList.Create;
|
||||||
|
LoadValues;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
destructor TUDKIniFile.Destroy;
|
||||||
|
begin
|
||||||
|
Clear;
|
||||||
|
FreeAndNil(FSections);
|
||||||
|
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.Clear;
|
||||||
|
var
|
||||||
|
sectionIndex: Integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
for sectionIndex := 0 to Pred(Sections.Count) do
|
||||||
|
Sections.Objects[sectionIndex].Free;
|
||||||
|
|
||||||
|
Sections.Clear;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TUDKIniFile.ReadString(const ASection, AIdent, ADefault: string): string;
|
||||||
|
var
|
||||||
|
sectionIndex: Integer;
|
||||||
|
strings: TStrings;
|
||||||
|
|
||||||
|
begin
|
||||||
|
sectionIndex := Sections.IndexOf(ASection);
|
||||||
|
if sectionIndex > -1 then
|
||||||
|
begin
|
||||||
|
strings := TStrings(Sections.Objects[sectionIndex]);
|
||||||
|
sectionIndex := strings.IndexOfName(AIdent);
|
||||||
|
|
||||||
|
if sectionIndex > -1 then
|
||||||
|
begin
|
||||||
|
Result := Copy(strings[sectionIndex], Length(AIdent) + 2, MaxInt);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := ADefault;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.DeleteKey(const ASection, AIdent: string);
|
||||||
|
var
|
||||||
|
sectionIndex: Integer;
|
||||||
|
keyIndex: Integer;
|
||||||
|
Strings: TStrings;
|
||||||
|
|
||||||
|
begin
|
||||||
|
sectionIndex := FSections.IndexOf(ASection);
|
||||||
|
if sectionIndex > -1 then
|
||||||
|
begin
|
||||||
|
strings := TStrings(FSections.Objects[sectionIndex]);
|
||||||
|
keyIndex := strings.IndexOfName(AIdent);
|
||||||
|
|
||||||
|
if keyIndex > -1 then
|
||||||
|
strings.Delete(keyIndex);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.EraseSection(const ASection: string);
|
||||||
|
var
|
||||||
|
sectionIndex: Integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
sectionIndex := Sections.IndexOf(ASection);
|
||||||
|
if sectionIndex > -1 then
|
||||||
|
begin
|
||||||
|
TStrings(Sections.Objects[sectionIndex]).Free;
|
||||||
|
Sections.Delete(sectionIndex);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.ReadSection(const ASection: string; AStrings: TStrings);
|
||||||
|
var
|
||||||
|
sectionIndex: Integer;
|
||||||
|
keyIndex: Integer;
|
||||||
|
sectionStrings: TStrings;
|
||||||
|
|
||||||
|
begin
|
||||||
|
AStrings.BeginUpdate;
|
||||||
|
try
|
||||||
|
AStrings.Clear;
|
||||||
|
|
||||||
|
sectionIndex := Sections.IndexOf(ASection);
|
||||||
|
if sectionIndex > -1 then
|
||||||
|
begin
|
||||||
|
sectionStrings := TStrings(Sections.Objects[sectionIndex]);
|
||||||
|
for keyIndex := 0 to Pred(sectionStrings.Count) do
|
||||||
|
AStrings.Add(sectionStrings.Names[keyIndex]);
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
AStrings.EndUpdate;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.ReadSections(AStrings: TStrings);
|
||||||
|
begin
|
||||||
|
AStrings.Assign(Sections);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.ReadSectionValues(const ASection: string; AStrings: TStrings);
|
||||||
|
var
|
||||||
|
sectionIndex: Integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
AStrings.BeginUpdate;
|
||||||
|
try
|
||||||
|
AStrings.Clear;
|
||||||
|
|
||||||
|
sectionIndex := Sections.IndexOf(ASection);
|
||||||
|
if sectionIndex > -1 then
|
||||||
|
AStrings.Assign(TStrings(Sections.Objects[sectionIndex]));
|
||||||
|
finally
|
||||||
|
AStrings.EndUpdate;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.UpdateFile;
|
||||||
|
var
|
||||||
|
list: TStringList;
|
||||||
|
|
||||||
|
begin
|
||||||
|
list := TStringList.Create;
|
||||||
|
try
|
||||||
|
GetStrings(list);
|
||||||
|
list.SaveToFile(FileName, FEncoding);
|
||||||
|
finally
|
||||||
|
FreeAndNil(list);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.WriteString(const ASection, AIdent, AValue: string);
|
||||||
|
var
|
||||||
|
sectionIndex: Integer;
|
||||||
|
keyIndex: Integer;
|
||||||
|
value: string;
|
||||||
|
strings: TStrings;
|
||||||
|
|
||||||
|
begin
|
||||||
|
sectionIndex := Sections.IndexOf(ASection);
|
||||||
|
if sectionIndex > -1 then
|
||||||
|
strings := TStrings(Sections.Objects[sectionIndex])
|
||||||
|
else
|
||||||
|
strings := AddSection(ASection);
|
||||||
|
|
||||||
|
value := AIdent + '=' + AValue;
|
||||||
|
keyIndex := strings.IndexOfName(AIdent);
|
||||||
|
|
||||||
|
if keyIndex > -1 then
|
||||||
|
strings[keyIndex] := value
|
||||||
|
else
|
||||||
|
strings.Add(value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.GetStrings(AList: TStrings);
|
||||||
|
var
|
||||||
|
sectionIndex: Integer;
|
||||||
|
strings: TStrings;
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
begin
|
||||||
|
AList.BeginUpdate;
|
||||||
|
try
|
||||||
|
for sectionIndex := 0 to Pred(Sections.Count) do
|
||||||
|
begin
|
||||||
|
AList.Add('[' + Sections[sectionIndex] + ']');
|
||||||
|
|
||||||
|
strings := TStrings(Sections.Objects[sectionIndex]);
|
||||||
|
for value in strings do
|
||||||
|
AList.Add(value);
|
||||||
|
|
||||||
|
AList.Add('');
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
AList.EndUpdate;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.SetStrings(AList: TStrings);
|
||||||
|
var
|
||||||
|
lineIndex: Integer;
|
||||||
|
line: string;
|
||||||
|
separatorPos: Integer;
|
||||||
|
strings: TStrings;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Clear;
|
||||||
|
strings := nil;
|
||||||
|
|
||||||
|
for lineIndex := 0 to Pred(AList.Count) do
|
||||||
|
begin
|
||||||
|
line := Trim(AList[lineIndex]);
|
||||||
|
|
||||||
|
if (Length(line) > 0) and (line[1] <> ';') then
|
||||||
|
begin
|
||||||
|
if (line[1] = '[') and (line[Length(line)] = ']') then
|
||||||
|
begin
|
||||||
|
Delete(line, 1, 1);
|
||||||
|
SetLength(line, Length(line) - 1);
|
||||||
|
|
||||||
|
strings := AddSection(Trim(line));
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if Assigned(strings) then
|
||||||
|
begin
|
||||||
|
separatorPos := Pos('=', line);
|
||||||
|
if separatorPos > 0 then
|
||||||
|
strings.Add(Trim(Copy(line, 1, Pred(separatorPos))) + '=' + Trim(Copy(line, Succ(separatorPos), MaxInt)))
|
||||||
|
else
|
||||||
|
strings.Add(line);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.ReadDuplicateStrings(const ASection, AIdent: string; AList: TStrings);
|
||||||
|
var
|
||||||
|
sectionIndex: Integer;
|
||||||
|
keyIndex: Integer;
|
||||||
|
Strings: TStrings;
|
||||||
|
|
||||||
|
begin
|
||||||
|
sectionIndex := FSections.IndexOf(ASection);
|
||||||
|
if sectionIndex > -1 then
|
||||||
|
begin
|
||||||
|
strings := TStrings(FSections.Objects[sectionIndex]);
|
||||||
|
|
||||||
|
for keyIndex := 0 to Pred(strings.Count) do
|
||||||
|
begin
|
||||||
|
if (CaseSensitive and (strings.Names[keyIndex] = AIdent)) or
|
||||||
|
((not CaseSensitive) and SameText(strings.Names[keyIndex], AIdent)) then
|
||||||
|
begin
|
||||||
|
AList.Add(strings.ValueFromIndex[keyIndex]);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.WriteDuplicateString(const ASection, AIdent, AValue: string);
|
||||||
|
var
|
||||||
|
sectionIndex: Integer;
|
||||||
|
strings: TStrings;
|
||||||
|
|
||||||
|
begin
|
||||||
|
sectionIndex := Sections.IndexOf(ASection);
|
||||||
|
if sectionIndex > -1 then
|
||||||
|
strings := TStrings(Sections.Objects[sectionIndex])
|
||||||
|
else
|
||||||
|
strings := AddSection(ASection);
|
||||||
|
|
||||||
|
strings.Add(AIdent + '=' + AValue);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.DeleteDuplicateKeys(const ASection, AIdent: string);
|
||||||
|
var
|
||||||
|
sectionIndex: Integer;
|
||||||
|
keyIndex: Integer;
|
||||||
|
Strings: TStrings;
|
||||||
|
|
||||||
|
begin
|
||||||
|
sectionIndex := Sections.IndexOf(ASection);
|
||||||
|
if sectionIndex > -1 then
|
||||||
|
begin
|
||||||
|
strings := TStrings(Sections.Objects[sectionIndex]);
|
||||||
|
|
||||||
|
for keyIndex := Pred(strings.Count) downto 0 do
|
||||||
|
begin
|
||||||
|
if (CaseSensitive and (strings.Names[keyIndex] = AIdent)) or
|
||||||
|
((not CaseSensitive) and SameText(strings.Names[keyIndex], AIdent)) then
|
||||||
|
begin
|
||||||
|
strings.Delete(keyIndex)
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TUDKIniFile.AddSection(const ASection: string): TStrings;
|
||||||
|
begin
|
||||||
|
Result := THashedStringList.Create;
|
||||||
|
try
|
||||||
|
THashedStringList(Result).CaseSensitive := CaseSensitive;
|
||||||
|
FSections.AddObject(ASection, Result);
|
||||||
|
except
|
||||||
|
FreeAndNil(Result);
|
||||||
|
raise;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.LoadValues;
|
||||||
|
var
|
||||||
|
size: Integer;
|
||||||
|
buffer: TBytes;
|
||||||
|
list: TStringList;
|
||||||
|
stream: TFileStream;
|
||||||
|
|
||||||
|
begin
|
||||||
|
if (Length(FileName) > 0) and FileExists(FileName) then
|
||||||
|
begin
|
||||||
|
stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
|
||||||
|
try
|
||||||
|
{ Load file into buffer and detect encoding }
|
||||||
|
size := stream.Size - stream.Position;
|
||||||
|
SetLength(buffer, size);
|
||||||
|
stream.Read(buffer[0], size);
|
||||||
|
|
||||||
|
size := TEncoding.GetBufferEncoding(buffer, FEncoding);
|
||||||
|
|
||||||
|
{ Load strings from buffer }
|
||||||
|
list := TStringList.Create;
|
||||||
|
try
|
||||||
|
list.Text := FEncoding.GetString(buffer, size, Length(buffer) - size);
|
||||||
|
SetStrings(list);
|
||||||
|
finally
|
||||||
|
FreeAndNil(list);
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
FreeAndNil(stream);
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
Clear;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TUDKIniFile.GetCaseSensitive: Boolean;
|
||||||
|
begin
|
||||||
|
Result := Sections.CaseSensitive;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TUDKIniFile.SetCaseSensitive(Value: Boolean);
|
||||||
|
var
|
||||||
|
sectionIndex: Integer;
|
||||||
|
sectionList: TUDKHashedStringList;
|
||||||
|
|
||||||
|
begin
|
||||||
|
if Value <> Sections.CaseSensitive then
|
||||||
|
begin
|
||||||
|
Sections.CaseSensitive := Value;
|
||||||
|
|
||||||
|
for sectionIndex := 0 to Pred(Sections.Count) do
|
||||||
|
begin
|
||||||
|
sectionList := TUDKHashedStringList(Sections.Objects[sectionIndex]);
|
||||||
|
sectionList.CaseSensitive := Value;
|
||||||
|
sectionList.Changed;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TUDKHashedStringList(Sections).Changed;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
@ -10,8 +10,9 @@ type
|
|||||||
private
|
private
|
||||||
FLocation: string;
|
FLocation: string;
|
||||||
FLoaded: Boolean;
|
FLoaded: Boolean;
|
||||||
|
FModified: Boolean;
|
||||||
protected
|
protected
|
||||||
procedure Notify(const APropertyName: string); virtual;
|
procedure PropertyChanged(const APropertyName: string); virtual;
|
||||||
|
|
||||||
procedure DoLoad; virtual; abstract;
|
procedure DoLoad; virtual; abstract;
|
||||||
procedure DoSave; virtual; abstract;
|
procedure DoSave; virtual; abstract;
|
||||||
@ -26,6 +27,7 @@ type
|
|||||||
|
|
||||||
property Loaded: Boolean read FLoaded;
|
property Loaded: Boolean read FLoaded;
|
||||||
property Location: string read FLocation;
|
property Location: string read FLocation;
|
||||||
|
property Modified: Boolean read FModified;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TCustomGameClass = class of TCustomGame;
|
TCustomGameClass = class of TCustomGame;
|
||||||
@ -53,20 +55,44 @@ end;
|
|||||||
|
|
||||||
|
|
||||||
procedure TCustomGame.Load;
|
procedure TCustomGame.Load;
|
||||||
|
var
|
||||||
|
wasModified: Boolean;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
wasModified := FModified;
|
||||||
|
|
||||||
DoLoad;
|
DoLoad;
|
||||||
FLoaded := True;
|
FLoaded := True;
|
||||||
|
|
||||||
|
FModified := False;
|
||||||
|
if wasModified then
|
||||||
|
TBindings.Notify(Self, 'Modified');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TCustomGame.Save;
|
procedure TCustomGame.Save;
|
||||||
|
var
|
||||||
|
wasModified: Boolean;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
wasModified := FModified;
|
||||||
|
|
||||||
DoSave;
|
DoSave;
|
||||||
|
|
||||||
|
FModified := False;
|
||||||
|
if wasModified then
|
||||||
|
TBindings.Notify(Self, 'Modified');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TCustomGame.Notify(const APropertyName: string);
|
procedure TCustomGame.PropertyChanged(const APropertyName: string);
|
||||||
begin
|
begin
|
||||||
|
if not FModified then
|
||||||
|
begin
|
||||||
|
FModified := True;
|
||||||
|
TBindings.Notify(Self, 'Modified');
|
||||||
|
end;
|
||||||
|
|
||||||
TBindings.Notify(Self, APropertyName);
|
TBindings.Notify(Self, APropertyName);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ type
|
|||||||
in the base Chivalry class. }
|
in the base Chivalry class. }
|
||||||
TChivalryMedievalWarfareGame = class(TChivalryGame)
|
TChivalryMedievalWarfareGame = class(TChivalryGame)
|
||||||
protected
|
protected
|
||||||
procedure LoadSupportedMapList(AList: TList<TGameMap>); override;
|
procedure LoadPredefinedMapList(AList: TList<TGameMap>); override;
|
||||||
public
|
public
|
||||||
class function GameName: string; override;
|
class function GameName: string; override;
|
||||||
class function AutoDetect: TCustomGame; override;
|
class function AutoDetect: TCustomGame; override;
|
||||||
@ -81,7 +81,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TChivalryMedievalWarfareGame.LoadSupportedMapList(AList: TList<TGameMap>);
|
procedure TChivalryMedievalWarfareGame.LoadPredefinedMapList(AList: TList<TGameMap>);
|
||||||
var
|
var
|
||||||
mapListFileName: string;
|
mapListFileName: string;
|
||||||
mapList: TMemIniFile;
|
mapList: TMemIniFile;
|
||||||
|
@ -18,13 +18,13 @@ type
|
|||||||
FServerName: string;
|
FServerName: string;
|
||||||
FMessageOfTheDay: string;
|
FMessageOfTheDay: string;
|
||||||
|
|
||||||
FSupportedMapList: TObjectList<TGameMap>;
|
FPredefinedMapList: TObjectList<TGameMap>;
|
||||||
FMapList: TObjectList<TGameMap>;
|
FMapList: TObjectList<TGameMap>;
|
||||||
protected
|
protected
|
||||||
procedure DoLoad; override;
|
procedure DoLoad; override;
|
||||||
procedure DoSave; override;
|
procedure DoSave; override;
|
||||||
|
|
||||||
procedure LoadSupportedMapList(AList: TList<TGameMap>); virtual; abstract;
|
procedure LoadPredefinedMapList(AList: TList<TGameMap>); virtual; abstract;
|
||||||
|
|
||||||
function CreateGameMap(const AMapName: string): TGameMap; virtual;
|
function CreateGameMap(const AMapName: string): TGameMap; virtual;
|
||||||
public
|
public
|
||||||
@ -55,8 +55,22 @@ type
|
|||||||
property MessageOfTheDay: string read GetMessageOfTheDay write SetMessageOfTheDay;
|
property MessageOfTheDay: string read GetMessageOfTheDay write SetMessageOfTheDay;
|
||||||
|
|
||||||
{ IGameMapList }
|
{ IGameMapList }
|
||||||
function GetSupportedMapList: TList<TGameMap>;
|
function GetPredefinedMapList: TEnumerable<TGameMap>;
|
||||||
function GetMapList: TList<TGameMap>;
|
function GetMapList: TEnumerable<TGameMap>;
|
||||||
|
|
||||||
|
function GetMapCount: Integer;
|
||||||
|
function GetMap(Index: Integer): TGameMap;
|
||||||
|
|
||||||
|
procedure AddMap(AMap: TGameMap);
|
||||||
|
procedure InsertMap(Index: Integer; AMap: TGameMap);
|
||||||
|
procedure DeleteMap(AIndex: Integer);
|
||||||
|
procedure RemoveMap(AMap: TGameMap);
|
||||||
|
procedure MoveMap(ASourceIndex, ATargetIndex: Integer);
|
||||||
|
|
||||||
|
property PredefinedMapList: TEnumerable<TGameMap> read GetPredefinedMapList;
|
||||||
|
property MapList: TEnumerable<TGameMap> read GetMapList;
|
||||||
|
property MapCount: Integer read GetMapCount;
|
||||||
|
property Map[Index: Integer]: TGameMap read GetMap;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -64,7 +78,9 @@ implementation
|
|||||||
uses
|
uses
|
||||||
System.Classes,
|
System.Classes,
|
||||||
System.IniFiles,
|
System.IniFiles,
|
||||||
System.SysUtils;
|
System.SysUtils,
|
||||||
|
|
||||||
|
UDKIniFile;
|
||||||
|
|
||||||
|
|
||||||
const
|
const
|
||||||
@ -92,17 +108,17 @@ constructor TChivalryGame.Create(const ALocation: string);
|
|||||||
begin
|
begin
|
||||||
inherited Create(ALocation);
|
inherited Create(ALocation);
|
||||||
|
|
||||||
FSupportedMapList := TObjectList<TGameMap>.Create(True);
|
FPredefinedMapList := TObjectList<TGameMap>.Create(True);
|
||||||
FMapList := TObjectList<TGameMap>.Create(True);
|
FMapList := TObjectList<TGameMap>.Create(True);
|
||||||
|
|
||||||
LoadSupportedMapList(FSupportedMapList);
|
LoadPredefinedMapList(FPredefinedMapList);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
destructor TChivalryGame.Destroy;
|
destructor TChivalryGame.Destroy;
|
||||||
begin
|
begin
|
||||||
FreeAndNil(FMapList);
|
FreeAndNil(FMapList);
|
||||||
FreeAndNil(FSupportedMapList);
|
FreeAndNil(FPredefinedMapList);
|
||||||
|
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
@ -110,40 +126,45 @@ end;
|
|||||||
|
|
||||||
procedure TChivalryGame.DoLoad;
|
procedure TChivalryGame.DoLoad;
|
||||||
var
|
var
|
||||||
gameSettings: TMemIniFile;
|
gameSettings: TUDKIniFile;
|
||||||
engineSettings: TMemIniFile;
|
engineSettings: TUDKIniFile;
|
||||||
mapListValues: TStringList;
|
mapListValues: TStringList;
|
||||||
valueIndex: Integer;
|
mapName: string;
|
||||||
|
mapListChanged: Boolean;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
gameSettings := TMemIniFile.Create(Location + GameSettingsFileName);
|
gameSettings := TUDKIniFile.Create(Location + GameSettingsFileName);
|
||||||
try
|
try
|
||||||
FServerPort := gameSettings.ReadInteger(GameURL, GameURLPort, GameURLPortDefault);
|
SetServerPort(gameSettings.ReadInteger(GameURL, GameURLPort, GameURLPortDefault));
|
||||||
FPeerPort := gameSettings.ReadInteger(GameURL, GameURLPeerPort, GameURLPeerPortDefault);
|
SetPeerPort(gameSettings.ReadInteger(GameURL, GameURLPeerPort, GameURLPeerPortDefault));
|
||||||
|
|
||||||
FServerName := gameSettings.ReadString(GameReplicationInfo, GameReplicationInfoServerName, '');
|
SetServerName(gameSettings.ReadString(GameReplicationInfo, GameReplicationInfoServerName, ''));
|
||||||
FMessageOfTheDay := gameSettings.ReadString(GameReplicationInfo, GameReplicationInfoMessageOfTheDay, '');
|
SetMessageOfTheDay(gameSettings.ReadString(GameReplicationInfo, GameReplicationInfoMessageOfTheDay, ''));
|
||||||
|
|
||||||
|
mapListChanged := (FMapList.Count > 0);
|
||||||
|
|
||||||
{ Maplist is special; it occurs multiple times and order matters }
|
{ Maplist is special; it occurs multiple times and order matters }
|
||||||
mapListValues := TStringList.Create;
|
mapListValues := TStringList.Create;
|
||||||
try
|
try
|
||||||
gameSettings.ReadSectionValues(GameAOCGame, mapListValues);
|
gameSettings.ReadDuplicateStrings(GameAOCGame, GameAOCGameMapList, mapListValues);
|
||||||
|
for mapName in mapListValues do
|
||||||
for valueIndex := 0 to Pred(mapListValues.Count) do
|
|
||||||
begin
|
begin
|
||||||
if SameText(mapListValues.Names[valueIndex], GameAOCGameMapList) then
|
FMapList.Add(CreateGameMap(mapName));
|
||||||
FMapList.Add(CreateGameMap(mapListValues.ValueFromIndex[valueIndex]));
|
mapListChanged := True;
|
||||||
end;
|
end;
|
||||||
finally
|
finally
|
||||||
FreeAndNil(mapListValues);
|
FreeAndNil(mapListValues);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
if mapListChanged then
|
||||||
|
PropertyChanged('MapList');
|
||||||
finally
|
finally
|
||||||
FreeAndNil(gameSettings);
|
FreeAndNil(gameSettings);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
engineSettings := TMemIniFile.Create(Location + EngineSettingsFileName);
|
engineSettings := TUDKIniFile.Create(Location + EngineSettingsFileName);
|
||||||
try
|
try
|
||||||
FQueryPort := engineSettings.ReadInteger(EngineSteam, EngineSteamQueryPort, EngineSteamQueryPortDefault);
|
SetQueryPort(engineSettings.ReadInteger(EngineSteam, EngineSteamQueryPort, EngineSteamQueryPortDefault));
|
||||||
finally
|
finally
|
||||||
FreeAndNil(engineSettings);
|
FreeAndNil(engineSettings);
|
||||||
end;
|
end;
|
||||||
@ -151,8 +172,39 @@ end;
|
|||||||
|
|
||||||
|
|
||||||
procedure TChivalryGame.DoSave;
|
procedure TChivalryGame.DoSave;
|
||||||
|
var
|
||||||
|
gameSettings: TUDKIniFile;
|
||||||
|
engineSettings: TUDKIniFile;
|
||||||
|
map: TGameMap;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
// #ToDo1 -oMvR: 30-6-2014: save to INI files
|
gameSettings := TUDKIniFile.Create(Location + GameSettingsFileName);
|
||||||
|
try
|
||||||
|
gameSettings.WriteInteger(GameURL, GameURLPort, ServerPort);
|
||||||
|
gameSettings.WriteInteger(GameURL, GameURLPeerPort, PeerPort);
|
||||||
|
|
||||||
|
gameSettings.WriteString(GameReplicationInfo, GameReplicationInfoServerName, ServerName);
|
||||||
|
gameSettings.WriteString(GameReplicationInfo, GameReplicationInfoMessageOfTheDay, MessageOfTheDay);
|
||||||
|
|
||||||
|
{ Remove all Maplist references before rewriting the list }
|
||||||
|
gameSettings.DeleteDuplicateKeys(GameAOCGame, GameAOCGameMapList);
|
||||||
|
|
||||||
|
for map in MapList do
|
||||||
|
gameSettings.WriteDuplicateString(GameAOCGame, GameAOCGameMapList, map.Name);
|
||||||
|
|
||||||
|
gameSettings.UpdateFile;
|
||||||
|
finally
|
||||||
|
FreeAndNil(gameSettings);
|
||||||
|
end;
|
||||||
|
|
||||||
|
engineSettings := TUDKIniFile.Create(Location + EngineSettingsFileName);
|
||||||
|
try
|
||||||
|
engineSettings.WriteInteger(EngineSteam, EngineSteamQueryPort, QueryPort);
|
||||||
|
|
||||||
|
engineSettings.UpdateFile;
|
||||||
|
finally
|
||||||
|
FreeAndNil(engineSettings);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -163,7 +215,7 @@ var
|
|||||||
begin
|
begin
|
||||||
Result := nil;
|
Result := nil;
|
||||||
|
|
||||||
for map in GetSupportedMapList do
|
for map in PredefinedMapList do
|
||||||
if SameText(map.Name, AMapName) then
|
if SameText(map.Name, AMapName) then
|
||||||
begin
|
begin
|
||||||
Result := TGameMap.Create(map);
|
Result := TGameMap.Create(map);
|
||||||
@ -199,7 +251,7 @@ begin
|
|||||||
if Value <> FServerPort then
|
if Value <> FServerPort then
|
||||||
begin
|
begin
|
||||||
FServerPort := Value;
|
FServerPort := Value;
|
||||||
Notify('ServerPort');
|
PropertyChanged('ServerPort');
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -209,7 +261,7 @@ begin
|
|||||||
if Value <> FPeerPort then
|
if Value <> FPeerPort then
|
||||||
begin
|
begin
|
||||||
FPeerPort := Value;
|
FPeerPort := Value;
|
||||||
Notify('PeerPort');
|
PropertyChanged('PeerPort');
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -219,7 +271,7 @@ begin
|
|||||||
if Value <> FQueryPort then
|
if Value <> FQueryPort then
|
||||||
begin
|
begin
|
||||||
FQueryPort := Value;
|
FQueryPort := Value;
|
||||||
Notify('QueryPort');
|
PropertyChanged('QueryPort');
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -241,7 +293,7 @@ begin
|
|||||||
if Value <> FServerName then
|
if Value <> FServerName then
|
||||||
begin
|
begin
|
||||||
FServerName := Value;
|
FServerName := Value;
|
||||||
Notify('ServerName');
|
PropertyChanged('ServerName');
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -251,20 +303,70 @@ begin
|
|||||||
if Value <> FMessageOfTheDay then
|
if Value <> FMessageOfTheDay then
|
||||||
begin
|
begin
|
||||||
FMessageOfTheDay := Value;
|
FMessageOfTheDay := Value;
|
||||||
Notify('MessageOfTheDay');
|
PropertyChanged('MessageOfTheDay');
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
function TChivalryGame.GetMapList: TList<TGameMap>;
|
function TChivalryGame.GetPredefinedMapList: TEnumerable<TGameMap>;
|
||||||
|
begin
|
||||||
|
Result := FPredefinedMapList;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TChivalryGame.GetMapList: TEnumerable<TGameMap>;
|
||||||
begin
|
begin
|
||||||
Result := FMapList;
|
Result := FMapList;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
function TChivalryGame.GetSupportedMapList: TList<TGameMap>;
|
function TChivalryGame.GetMapCount: Integer;
|
||||||
begin
|
begin
|
||||||
Result := FSupportedMapList;
|
Result := FMapList.Count;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TChivalryGame.GetMap(Index: Integer): TGameMap;
|
||||||
|
begin
|
||||||
|
Result := FMapList[Index];
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TChivalryGame.AddMap(AMap: TGameMap);
|
||||||
|
begin
|
||||||
|
FMapList.Add(AMap);
|
||||||
|
PropertyChanged('MapList');
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TChivalryGame.InsertMap(Index: Integer; AMap: TGameMap);
|
||||||
|
begin
|
||||||
|
FMapList.Insert(Index, AMap);
|
||||||
|
PropertyChanged('MapList');
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TChivalryGame.DeleteMap(AIndex: Integer);
|
||||||
|
begin
|
||||||
|
FMapList.Delete(AIndex);
|
||||||
|
PropertyChanged('MapList');
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TChivalryGame.RemoveMap(AMap: TGameMap);
|
||||||
|
begin
|
||||||
|
if FMapList.Remove(AMap) > -1 then
|
||||||
|
PropertyChanged('MapList');
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TChivalryGame.MoveMap(ASourceIndex, ATargetIndex: Integer);
|
||||||
|
begin
|
||||||
|
if ATargetIndex <> ASourceIndex then
|
||||||
|
begin
|
||||||
|
FMapList.Move(ASourceIndex, ATargetIndex);
|
||||||
|
PropertyChanged('MapList');
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
@ -54,8 +54,23 @@ type
|
|||||||
|
|
||||||
IGameMapList = interface
|
IGameMapList = interface
|
||||||
['{E8552B4C-9447-4FAD-BB20-C5EB3AF07B0E}']
|
['{E8552B4C-9447-4FAD-BB20-C5EB3AF07B0E}']
|
||||||
function GetSupportedMapList: TList<TGameMap>;
|
function GetPredefinedMapList: TEnumerable<TGameMap>;
|
||||||
function GetMapList: TList<TGameMap>;
|
function GetMapList: TEnumerable<TGameMap>;
|
||||||
|
|
||||||
|
function GetMapCount: Integer;
|
||||||
|
function GetMap(Index: Integer): TGameMap;
|
||||||
|
|
||||||
|
procedure AddMap(AMap: TGameMap);
|
||||||
|
procedure InsertMap(Index: Integer; AMap: TGameMap);
|
||||||
|
procedure DeleteMap(AIndex: Integer);
|
||||||
|
procedure RemoveMap(AMap: TGameMap);
|
||||||
|
procedure MoveMap(ASourceIndex, ATargetIndex: Integer);
|
||||||
|
|
||||||
|
property PredefinedMapList: TEnumerable<TGameMap> read GetPredefinedMapList;
|
||||||
|
property MapList: TEnumerable<TGameMap> read GetMapList;
|
||||||
|
|
||||||
|
property MapCount: Integer read GetMapCount;
|
||||||
|
property Map[Index: Integer]: TGameMap read GetMap;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ object MainForm: TMainForm
|
|||||||
OldCreateOrder = False
|
OldCreateOrder = False
|
||||||
Position = poScreenCenter
|
Position = poScreenCenter
|
||||||
ShowHint = True
|
ShowHint = True
|
||||||
|
OnCloseQuery = FormCloseQuery
|
||||||
OnCreate = FormCreate
|
OnCreate = FormCreate
|
||||||
OnDestroy = FormDestroy
|
OnDestroy = FormDestroy
|
||||||
PixelsPerInch = 96
|
PixelsPerInch = 96
|
||||||
@ -65,7 +66,7 @@ object MainForm: TMainForm
|
|||||||
Top = 76
|
Top = 76
|
||||||
Width = 565
|
Width = 565
|
||||||
Height = 428
|
Height = 428
|
||||||
ActivePage = tsMapList
|
ActivePage = tsAbout
|
||||||
Align = alClient
|
Align = alClient
|
||||||
Style = tsButtons
|
Style = tsButtons
|
||||||
TabOrder = 2
|
TabOrder = 2
|
||||||
@ -175,7 +176,6 @@ object MainForm: TMainForm
|
|||||||
Align = alRight
|
Align = alRight
|
||||||
BevelOuter = bvNone
|
BevelOuter = bvNone
|
||||||
TabOrder = 2
|
TabOrder = 2
|
||||||
ExplicitLeft = 415
|
|
||||||
inline frmMapPreview: TMapPreviewFrame
|
inline frmMapPreview: TMapPreviewFrame
|
||||||
Left = 0
|
Left = 0
|
||||||
Top = 0
|
Top = 0
|
||||||
@ -183,7 +183,6 @@ object MainForm: TMainForm
|
|||||||
Height = 130
|
Height = 130
|
||||||
Align = alTop
|
Align = alTop
|
||||||
TabOrder = 0
|
TabOrder = 0
|
||||||
ExplicitTop = 216
|
|
||||||
end
|
end
|
||||||
object btnMapListSave: TButton
|
object btnMapListSave: TButton
|
||||||
AlignWithMargins = True
|
AlignWithMargins = True
|
||||||
@ -198,9 +197,6 @@ object MainForm: TMainForm
|
|||||||
Action = actMapListSave
|
Action = actMapListSave
|
||||||
Align = alTop
|
Align = alTop
|
||||||
TabOrder = 2
|
TabOrder = 2
|
||||||
ExplicitLeft = 28
|
|
||||||
ExplicitTop = 168
|
|
||||||
ExplicitWidth = 75
|
|
||||||
end
|
end
|
||||||
object btnMapListLoad: TButton
|
object btnMapListLoad: TButton
|
||||||
AlignWithMargins = True
|
AlignWithMargins = True
|
||||||
@ -215,9 +211,6 @@ object MainForm: TMainForm
|
|||||||
Action = actMapListLoad
|
Action = actMapListLoad
|
||||||
Align = alTop
|
Align = alTop
|
||||||
TabOrder = 1
|
TabOrder = 1
|
||||||
ExplicitLeft = 28
|
|
||||||
ExplicitTop = 168
|
|
||||||
ExplicitWidth = 75
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -600,135 +593,167 @@ object MainForm: TMainForm
|
|||||||
ImageIndex = 4
|
ImageIndex = 4
|
||||||
object lblJCL: TLabel
|
object lblJCL: TLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 222
|
Top = 262
|
||||||
Width = 319
|
Width = 319
|
||||||
Height = 13
|
Height = 13
|
||||||
Caption = 'Uses JEDI Code Library (JCL) and Visual Component Library (JVCL)'
|
Caption = 'Uses JEDI Code Library (JCL) and Visual Component Library (JVCL)'
|
||||||
end
|
end
|
||||||
object lblVirtualTreeview: TLabel
|
object lblVirtualTreeview: TLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 334
|
Top = 358
|
||||||
Width = 179
|
Width = 179
|
||||||
Height = 13
|
Height = 13
|
||||||
Caption = 'Uses Virtual Treeview by Mike Lischke'
|
Caption = 'Uses Virtual Treeview by Mike Lischke'
|
||||||
end
|
end
|
||||||
object lblChivalry: TLabel
|
object lblChivalry: TLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 16
|
Top = 80
|
||||||
Width = 246
|
Width = 246
|
||||||
Height = 13
|
Height = 13
|
||||||
Caption = 'Chivalry: Medieval Warfare by Torn Banner Studios'
|
Caption = 'Chivalry: Medieval Warfare by Torn Banner Studios'
|
||||||
end
|
end
|
||||||
object lblPixelophilia: TLabel
|
object lblPixelophilia: TLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 72
|
Top = 128
|
||||||
Width = 293
|
Width = 293
|
||||||
Height = 13
|
Height = 13
|
||||||
Caption = 'Various icons are part of the Pixelophilia packs by '#214'mer '#199'etin'
|
Caption = 'Various icons are part of the Pixelophilia packs by '#214'mer '#199'etin'
|
||||||
end
|
end
|
||||||
object lblGentleface: TLabel
|
object lblGentleface: TLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 147
|
Top = 195
|
||||||
Width = 217
|
Width = 217
|
||||||
Height = 13
|
Height = 13
|
||||||
Caption = 'Toolbar icons are part of the Gentleface pack'
|
Caption = 'Toolbar icons are part of the Gentleface pack'
|
||||||
end
|
end
|
||||||
object lblSuperObject: TLabel
|
object lblSuperObject: TLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 278
|
Top = 310
|
||||||
Width = 176
|
Width = 176
|
||||||
Height = 13
|
Height = 13
|
||||||
Caption = 'Uses SuperObject by Henri Gourvest'
|
Caption = 'Uses SuperObject by Henri Gourvest'
|
||||||
end
|
end
|
||||||
|
object lblProductName: TLabel
|
||||||
|
Left = 12
|
||||||
|
Top = 12
|
||||||
|
Width = 148
|
||||||
|
Height = 13
|
||||||
|
Caption = '<runtime: product name>'
|
||||||
|
Font.Charset = DEFAULT_CHARSET
|
||||||
|
Font.Color = clWindowText
|
||||||
|
Font.Height = -11
|
||||||
|
Font.Name = 'Tahoma'
|
||||||
|
Font.Style = [fsBold]
|
||||||
|
ParentFont = False
|
||||||
|
end
|
||||||
|
object lblCopyright: TLabel
|
||||||
|
Left = 12
|
||||||
|
Top = 27
|
||||||
|
Width = 104
|
||||||
|
Height = 13
|
||||||
|
Caption = '<runtime: copyright>'
|
||||||
|
end
|
||||||
object llJCL: TLinkLabel
|
object llJCL: TLinkLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 241
|
Top = 277
|
||||||
Width = 101
|
Width = 101
|
||||||
Height = 17
|
Height = 17
|
||||||
Caption = '<a href="http://www.delphi-jedi.org/">www.delphi-jedi.org</a>'
|
Caption = '<a href="http://www.delphi-jedi.org/">www.delphi-jedi.org</a>'
|
||||||
TabOrder = 5
|
TabOrder = 6
|
||||||
TabStop = True
|
TabStop = True
|
||||||
OnLinkClick = llLinkClick
|
OnLinkClick = llLinkClick
|
||||||
end
|
end
|
||||||
object llVirtualTreeview: TLinkLabel
|
object llVirtualTreeview: TLinkLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 353
|
Top = 373
|
||||||
Width = 274
|
Width = 274
|
||||||
Height = 17
|
Height = 17
|
||||||
Caption =
|
Caption =
|
||||||
'<a href="http://www.soft-gems.net/index.php/controls/virtual-tre' +
|
'<a href="http://www.soft-gems.net/index.php/controls/virtual-tre' +
|
||||||
'eview">www.soft-gems.net/index.php/controls/virtual-treeview</a>'
|
'eview">www.soft-gems.net/index.php/controls/virtual-treeview</a>'
|
||||||
TabOrder = 7
|
TabOrder = 8
|
||||||
TabStop = True
|
TabStop = True
|
||||||
OnLinkClick = llLinkClick
|
OnLinkClick = llLinkClick
|
||||||
end
|
end
|
||||||
object llChivalry: TLinkLabel
|
object llChivalry: TLinkLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 35
|
Top = 95
|
||||||
Width = 109
|
Width = 109
|
||||||
Height = 17
|
Height = 17
|
||||||
Caption = '<a href="http://www.tornbanner.com/">www.tornbanner.com</a>'
|
Caption = '<a href="http://www.tornbanner.com/">www.tornbanner.com</a>'
|
||||||
TabOrder = 0
|
TabOrder = 1
|
||||||
TabStop = True
|
TabStop = True
|
||||||
OnLinkClick = llLinkClick
|
OnLinkClick = llLinkClick
|
||||||
end
|
end
|
||||||
object llPixelophilia: TLinkLabel
|
object llPixelophilia: TLinkLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 91
|
Top = 143
|
||||||
Width = 128
|
Width = 128
|
||||||
Height = 17
|
Height = 17
|
||||||
Caption =
|
Caption =
|
||||||
'<a href="http://omercetin.deviantart.com/">omercetin.deviantart.' +
|
'<a href="http://omercetin.deviantart.com/">omercetin.deviantart.' +
|
||||||
'com</a>'
|
'com</a>'
|
||||||
TabOrder = 1
|
TabOrder = 2
|
||||||
TabStop = True
|
TabStop = True
|
||||||
OnLinkClick = llLinkClick
|
OnLinkClick = llLinkClick
|
||||||
end
|
end
|
||||||
object llGentleface: TLinkLabel
|
object llGentleface: TLinkLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 166
|
Top = 210
|
||||||
Width = 172
|
Width = 172
|
||||||
Height = 17
|
Height = 17
|
||||||
Caption =
|
Caption =
|
||||||
'<a href="http://gentleface.com/free_icon_set.html">gentleface.co' +
|
'<a href="http://gentleface.com/free_icon_set.html">gentleface.co' +
|
||||||
'm/free_icon_set.html</a>'
|
'm/free_icon_set.html</a>'
|
||||||
TabOrder = 3
|
TabOrder = 4
|
||||||
TabStop = True
|
TabStop = True
|
||||||
OnLinkClick = llLinkClick
|
OnLinkClick = llLinkClick
|
||||||
end
|
end
|
||||||
object llPixelophiliaCC: TLinkLabel
|
object llPixelophiliaCC: TLinkLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 110
|
Top = 158
|
||||||
Width = 303
|
Width = 303
|
||||||
Height = 17
|
Height = 17
|
||||||
Caption =
|
Caption =
|
||||||
'<a href="http://creativecommons.org/licenses/by-nc-nd/3.0/">Crea' +
|
'<a href="http://creativecommons.org/licenses/by-nc-nd/3.0/">Crea' +
|
||||||
'tive Commons Attribution-Noncommercial-No Derivate 3.0</a>'
|
'tive Commons Attribution-Noncommercial-No Derivate 3.0</a>'
|
||||||
TabOrder = 2
|
TabOrder = 3
|
||||||
TabStop = True
|
TabStop = True
|
||||||
OnLinkClick = llLinkClick
|
OnLinkClick = llLinkClick
|
||||||
end
|
end
|
||||||
object llGentlefaceCC: TLinkLabel
|
object llGentlefaceCC: TLinkLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 185
|
Top = 225
|
||||||
Width = 242
|
Width = 242
|
||||||
Height = 17
|
Height = 17
|
||||||
Caption =
|
Caption =
|
||||||
'<a href="http://creativecommons.org/licenses/by-nc/3.0/">Creativ' +
|
'<a href="http://creativecommons.org/licenses/by-nc/3.0/">Creativ' +
|
||||||
'e Commons Attribution-Noncommercial 3.0</a>'
|
'e Commons Attribution-Noncommercial 3.0</a>'
|
||||||
TabOrder = 4
|
TabOrder = 5
|
||||||
TabStop = True
|
TabStop = True
|
||||||
OnLinkClick = llLinkClick
|
OnLinkClick = llLinkClick
|
||||||
end
|
end
|
||||||
object llSuperObject: TLinkLabel
|
object llSuperObject: TLinkLabel
|
||||||
Left = 12
|
Left = 12
|
||||||
Top = 297
|
Top = 325
|
||||||
Width = 157
|
Width = 157
|
||||||
Height = 17
|
Height = 17
|
||||||
Caption =
|
Caption =
|
||||||
'<a href="https://code.google.com/p/superobject/">code.google.com' +
|
'<a href="https://code.google.com/p/superobject/">code.google.com' +
|
||||||
'/p/superobject</a>'
|
'/p/superobject</a>'
|
||||||
TabOrder = 6
|
TabOrder = 7
|
||||||
|
TabStop = True
|
||||||
|
OnLinkClick = llLinkClick
|
||||||
|
end
|
||||||
|
object llWebsite: TLinkLabel
|
||||||
|
Left = 12
|
||||||
|
Top = 43
|
||||||
|
Width = 213
|
||||||
|
Height = 17
|
||||||
|
Caption =
|
||||||
|
'<a href="http://wiki.x2software.net/chivalryserverlauncher">wiki' +
|
||||||
|
'.x2software.net/chivalryserverlauncher</a>'
|
||||||
|
TabOrder = 0
|
||||||
TabStop = True
|
TabStop = True
|
||||||
OnLinkClick = llLinkClick
|
OnLinkClick = llLinkClick
|
||||||
end
|
end
|
||||||
@ -1689,7 +1714,7 @@ object MainForm: TMainForm
|
|||||||
end
|
end
|
||||||
object btnSave: TButton
|
object btnSave: TButton
|
||||||
AlignWithMargins = True
|
AlignWithMargins = True
|
||||||
Left = 533
|
Left = 434
|
||||||
Top = 0
|
Top = 0
|
||||||
Width = 91
|
Width = 91
|
||||||
Height = 25
|
Height = 25
|
||||||
@ -1701,6 +1726,20 @@ object MainForm: TMainForm
|
|||||||
Align = alRight
|
Align = alRight
|
||||||
TabOrder = 2
|
TabOrder = 2
|
||||||
end
|
end
|
||||||
|
object btnRevert: TButton
|
||||||
|
AlignWithMargins = True
|
||||||
|
Left = 533
|
||||||
|
Top = 0
|
||||||
|
Width = 91
|
||||||
|
Height = 25
|
||||||
|
Margins.Left = 0
|
||||||
|
Margins.Top = 0
|
||||||
|
Margins.Right = 8
|
||||||
|
Margins.Bottom = 0
|
||||||
|
Action = actRevert
|
||||||
|
Align = alRight
|
||||||
|
TabOrder = 3
|
||||||
|
end
|
||||||
end
|
end
|
||||||
object mbpMenuPainter: TX2MenuBarmusikCubePainter
|
object mbpMenuPainter: TX2MenuBarmusikCubePainter
|
||||||
Left = 44
|
Left = 44
|
||||||
@ -2469,11 +2508,16 @@ object MainForm: TMainForm
|
|||||||
end
|
end
|
||||||
object actSave: TAction
|
object actSave: TAction
|
||||||
Caption = '&Save changes'
|
Caption = '&Save changes'
|
||||||
|
OnExecute = actSaveExecute
|
||||||
end
|
end
|
||||||
object actClose: TAction
|
object actClose: TAction
|
||||||
Caption = '&Close'
|
Caption = '&Close'
|
||||||
OnExecute = actCloseExecute
|
OnExecute = actCloseExecute
|
||||||
end
|
end
|
||||||
|
object actRevert: TAction
|
||||||
|
Caption = '&Revert changes'
|
||||||
|
OnExecute = actRevertExecute
|
||||||
|
end
|
||||||
end
|
end
|
||||||
object pmnLaunch: TPopupMenu
|
object pmnLaunch: TPopupMenu
|
||||||
Left = 144
|
Left = 144
|
||||||
|
@ -134,9 +134,15 @@ type
|
|||||||
btnMapListLoad: TButton;
|
btnMapListLoad: TButton;
|
||||||
actMapListLoad: TAction;
|
actMapListLoad: TAction;
|
||||||
actMapListSave: TAction;
|
actMapListSave: TAction;
|
||||||
|
btnRevert: TButton;
|
||||||
|
actRevert: TAction;
|
||||||
|
llWebsite: TLinkLabel;
|
||||||
|
lblProductName: TLabel;
|
||||||
|
lblCopyright: TLabel;
|
||||||
|
|
||||||
procedure FormCreate(Sender: TObject);
|
procedure FormCreate(Sender: TObject);
|
||||||
procedure FormDestroy(Sender: TObject);
|
procedure FormDestroy(Sender: TObject);
|
||||||
|
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
||||||
procedure mbMenuCollapsing(Sender: TObject; Group: TX2MenuBarGroup; var Allowed: Boolean);
|
procedure mbMenuCollapsing(Sender: TObject; Group: TX2MenuBarGroup; var Allowed: Boolean);
|
||||||
procedure mbMenuSelectedChanged(Sender: TObject; Item: TX2CustomMenuBarItem);
|
procedure mbMenuSelectedChanged(Sender: TObject; Item: TX2CustomMenuBarItem);
|
||||||
procedure llLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType);
|
procedure llLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType);
|
||||||
@ -159,6 +165,8 @@ type
|
|||||||
procedure actMapRemoveExecute(Sender: TObject);
|
procedure actMapRemoveExecute(Sender: TObject);
|
||||||
procedure actMapUpExecute(Sender: TObject);
|
procedure actMapUpExecute(Sender: TObject);
|
||||||
procedure actMapDownExecute(Sender: TObject);
|
procedure actMapDownExecute(Sender: TObject);
|
||||||
|
procedure actSaveExecute(Sender: TObject);
|
||||||
|
procedure actRevertExecute(Sender: TObject);
|
||||||
private type
|
private type
|
||||||
TBindingExpressionList = TList<TBindingExpression>;
|
TBindingExpressionList = TList<TBindingExpression>;
|
||||||
TPageMenuDictionary = TDictionary<TTabSheet, TX2MenuBarItem>;
|
TPageMenuDictionary = TDictionary<TTabSheet, TX2MenuBarItem>;
|
||||||
@ -167,10 +175,14 @@ type
|
|||||||
FPageMenuMap: TPageMenuDictionary;
|
FPageMenuMap: TPageMenuDictionary;
|
||||||
FUIBindings: TBindingExpressionList;
|
FUIBindings: TBindingExpressionList;
|
||||||
|
|
||||||
|
function GetModified: Boolean;
|
||||||
procedure SetActiveGame(const Value: TCustomGame);
|
procedure SetActiveGame(const Value: TCustomGame);
|
||||||
|
procedure SetModified(const Value: Boolean);
|
||||||
protected
|
protected
|
||||||
procedure EnablePageActions;
|
procedure EnablePageActions;
|
||||||
procedure ActiveGameChanged;
|
procedure ActiveGameChanged;
|
||||||
|
procedure SaveActiveGame;
|
||||||
|
procedure RevertActiveGame;
|
||||||
|
|
||||||
procedure ClearUIBindings;
|
procedure ClearUIBindings;
|
||||||
procedure Bind(const APropertyName: string; ADestObject: TObject; const ADestPropertyName: string);
|
procedure Bind(const APropertyName: string; ADestObject: TObject; const ADestPropertyName: string);
|
||||||
@ -181,6 +193,8 @@ type
|
|||||||
procedure UpdateGameList;
|
procedure UpdateGameList;
|
||||||
procedure UpdateMapList;
|
procedure UpdateMapList;
|
||||||
|
|
||||||
|
procedure SaveGameList;
|
||||||
|
|
||||||
function FindMapNode(AMap: TGameMap): PVirtualNode;
|
function FindMapNode(AMap: TGameMap): PVirtualNode;
|
||||||
|
|
||||||
procedure HandleMapSelection(ANodes: TNodeArray; ATargetIndex: Integer; ACopy: Boolean);
|
procedure HandleMapSelection(ANodes: TNodeArray; ATargetIndex: Integer; ACopy: Boolean);
|
||||||
@ -190,6 +204,8 @@ type
|
|||||||
property ActiveGame: TCustomGame read FActiveGame write SetActiveGame;
|
property ActiveGame: TCustomGame read FActiveGame write SetActiveGame;
|
||||||
property PageMenuMap: TPageMenuDictionary read FPageMenuMap;
|
property PageMenuMap: TPageMenuDictionary read FPageMenuMap;
|
||||||
property UIBindings: TBindingExpressionList read FUIBindings;
|
property UIBindings: TBindingExpressionList read FUIBindings;
|
||||||
|
public
|
||||||
|
property Modified: Boolean read GetModified write SetModified;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -204,6 +220,7 @@ uses
|
|||||||
Winapi.ShellAPI,
|
Winapi.ShellAPI,
|
||||||
Winapi.Windows,
|
Winapi.Windows,
|
||||||
|
|
||||||
|
X2UtApp,
|
||||||
X2UtGraphics,
|
X2UtGraphics,
|
||||||
|
|
||||||
Forms.Game,
|
Forms.Game,
|
||||||
@ -280,6 +297,9 @@ begin
|
|||||||
lightBtnFace := BlendColors(clBtnFace, clWindow, 196);
|
lightBtnFace := BlendColors(clBtnFace, clWindow, 196);
|
||||||
pnlGamesWarning.Color := lightBtnFace;
|
pnlGamesWarning.Color := lightBtnFace;
|
||||||
|
|
||||||
|
lblProductName.Caption := App.Version.FormatVersion(False, True);
|
||||||
|
lblCopyright.Caption := App.Version.Strings.LegalCopyright;
|
||||||
|
|
||||||
|
|
||||||
{ Load games }
|
{ Load games }
|
||||||
userGamesFileName := Resources.GetUserDataPath(Resources.UserGamesFileName);
|
userGamesFileName := Resources.GetUserDataPath(Resources.UserGamesFileName);
|
||||||
@ -310,6 +330,13 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
||||||
|
begin
|
||||||
|
if Modified then
|
||||||
|
CanClose := (MessageBox(Self.Handle, 'Your changes will not be saved. Do you want to exit?', 'Close', MB_YESNO or MB_ICONQUESTION) = ID_YES);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TMainForm.EnablePageActions;
|
procedure TMainForm.EnablePageActions;
|
||||||
const
|
const
|
||||||
ActionListState: array[Boolean] of TActionListState = (asSuspended, asNormal);
|
ActionListState: array[Boolean] of TActionListState = (asSuspended, asNormal);
|
||||||
@ -329,7 +356,14 @@ begin
|
|||||||
if Assigned(ActiveGame) and (not ActiveGame.Loaded) then
|
if Assigned(ActiveGame) and (not ActiveGame.Loaded) then
|
||||||
ActiveGame.Load;
|
ActiveGame.Load;
|
||||||
|
|
||||||
// #ToDo1 -oMvR: 30-6-2014: attach observer to monitor changes
|
{ Bind Modified property }
|
||||||
|
UIBindings.Add(TBindings.CreateManagedBinding(
|
||||||
|
[TBindings.CreateAssociationScope([Associate(ActiveGame, 'src')])],
|
||||||
|
'src.Modified',
|
||||||
|
[TBindings.CreateAssociationScope([Associate(Self, 'dst')])],
|
||||||
|
'dst.Modified',
|
||||||
|
nil, nil, [coNotifyOutput, coEvaluate]));
|
||||||
|
|
||||||
|
|
||||||
if Supports(ActiveGame, IGameNetwork) then
|
if Supports(ActiveGame, IGameNetwork) then
|
||||||
BindGameNetwork;
|
BindGameNetwork;
|
||||||
@ -344,6 +378,26 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.SaveActiveGame;
|
||||||
|
begin
|
||||||
|
if Assigned(ActiveGame) then
|
||||||
|
ActiveGame.Save;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.RevertActiveGame;
|
||||||
|
begin
|
||||||
|
if Assigned(ActiveGame) then
|
||||||
|
begin
|
||||||
|
ActiveGame.Load;
|
||||||
|
|
||||||
|
// #ToDo2 -oMvR: 2-7-2014: This should be based on the observer pattern used by TBindings,
|
||||||
|
// but I haven't figured out how that works yet. For now, manually refresh.
|
||||||
|
UpdateMapList;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TMainForm.ClearUIBindings;
|
procedure TMainForm.ClearUIBindings;
|
||||||
var
|
var
|
||||||
binding: TBindingExpression;
|
binding: TBindingExpression;
|
||||||
@ -425,12 +479,19 @@ var
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
if Supports(ActiveGame, IGameMapList, gameMapList) then
|
if Supports(ActiveGame, IGameMapList, gameMapList) then
|
||||||
vstMapList.RootNodeCount := gameMapList.GetMapList.Count
|
vstMapList.RootNodeCount := gameMapList.MapCount
|
||||||
else
|
else
|
||||||
vstMapList.Clear;
|
vstMapList.Clear;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.SaveGameList;
|
||||||
|
begin
|
||||||
|
TGameListPersist.Save(Resources.GetUserDataPath(Resources.UserGamesFileName), TGameList.Instance);
|
||||||
|
UpdateGameList;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
function TMainForm.FindMapNode(AMap: TGameMap): PVirtualNode;
|
function TMainForm.FindMapNode(AMap: TGameMap): PVirtualNode;
|
||||||
var
|
var
|
||||||
gameMapList: IGameMapList;
|
gameMapList: IGameMapList;
|
||||||
@ -443,7 +504,7 @@ begin
|
|||||||
Result := vstMapList.IterateSubtree(nil,
|
Result := vstMapList.IterateSubtree(nil,
|
||||||
procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean)
|
procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean)
|
||||||
begin
|
begin
|
||||||
Abort := (gameMapList.GetMapList[Node^.Index] = AMap);
|
Abort := (gameMapList.Map[Node^.Index] = AMap);
|
||||||
end,
|
end,
|
||||||
nil);
|
nil);
|
||||||
end;
|
end;
|
||||||
@ -483,8 +544,8 @@ begin
|
|||||||
{ Copy map nodes }
|
{ Copy map nodes }
|
||||||
Inc(sourceIndex, sourceShift);
|
Inc(sourceIndex, sourceShift);
|
||||||
|
|
||||||
map := TGameMap.Create(gameMapList.GetMapList[sourceIndex]);
|
map := TGameMap.Create(gameMapList.Map[sourceIndex]);
|
||||||
gameMapList.GetMapList.Insert(targetIndex, map);
|
gameMapList.InsertMap(targetIndex, map);
|
||||||
selectedMaps.Add(map);
|
selectedMaps.Add(map);
|
||||||
|
|
||||||
Inc(targetIndex);
|
Inc(targetIndex);
|
||||||
@ -507,8 +568,8 @@ begin
|
|||||||
Inc(targetIndex);
|
Inc(targetIndex);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
selectedMaps.Add(gameMapList.GetMapList[sourceIndex]);
|
selectedMaps.Add(gameMapList.Map[sourceIndex]);
|
||||||
gameMapList.GetMapList.Move(sourceIndex, newIndex);
|
gameMapList.MoveMap(sourceIndex, newIndex);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -517,7 +578,7 @@ begin
|
|||||||
vstMapList.IterateSubtree(nil,
|
vstMapList.IterateSubtree(nil,
|
||||||
procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean)
|
procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean)
|
||||||
begin
|
begin
|
||||||
if selectedMaps.Contains(gameMapList.GetMapList[Node^.Index]) then
|
if selectedMaps.Contains(gameMapList.Map[Node^.Index]) then
|
||||||
Sender.Selected[Node] := True;
|
Sender.Selected[Node] := True;
|
||||||
end,
|
end,
|
||||||
nil);
|
nil);
|
||||||
@ -542,6 +603,12 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TMainForm.GetModified: Boolean;
|
||||||
|
begin
|
||||||
|
Result := actSave.Enabled;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TMainForm.SetActiveGame(const Value: TCustomGame);
|
procedure TMainForm.SetActiveGame(const Value: TCustomGame);
|
||||||
begin
|
begin
|
||||||
if Value <> FActiveGame then
|
if Value <> FActiveGame then
|
||||||
@ -552,6 +619,13 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.SetModified(const Value: Boolean);
|
||||||
|
begin
|
||||||
|
actSave.Enabled := Value;
|
||||||
|
actRevert.Enabled := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TMainForm.mbMenuCollapsing(Sender: TObject; Group: TX2MenuBarGroup; var Allowed: Boolean);
|
procedure TMainForm.mbMenuCollapsing(Sender: TObject; Group: TX2MenuBarGroup; var Allowed: Boolean);
|
||||||
begin
|
begin
|
||||||
Allowed := False;
|
Allowed := False;
|
||||||
@ -589,6 +663,21 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.actSaveExecute(Sender: TObject);
|
||||||
|
begin
|
||||||
|
SaveActiveGame;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.actRevertExecute(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if MessageBox(Self.Handle, 'Do you want to revert your changes?', 'Revert', MB_YESNO or MB_ICONQUESTION) = ID_NO then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
RevertActiveGame;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TMainForm.actGameAddExecute(Sender: TObject);
|
procedure TMainForm.actGameAddExecute(Sender: TObject);
|
||||||
var
|
var
|
||||||
game: TCustomGame;
|
game: TCustomGame;
|
||||||
@ -597,10 +686,7 @@ begin
|
|||||||
if TGameForm.Insert(Self, game) then
|
if TGameForm.Insert(Self, game) then
|
||||||
begin
|
begin
|
||||||
TGameList.Instance.Add(game);
|
TGameList.Instance.Add(game);
|
||||||
|
SaveGameList;
|
||||||
// #ToDo1 -oMvR: 30-6-2014: move to shared spot
|
|
||||||
TGameListPersist.Save(Resources.GetUserDataPath(Resources.UserGamesFileName), TGameList.Instance);
|
|
||||||
UpdateGameList;
|
|
||||||
|
|
||||||
ActiveGame := game;
|
ActiveGame := game;
|
||||||
end;
|
end;
|
||||||
@ -633,10 +719,7 @@ begin
|
|||||||
ActiveGame := nil;
|
ActiveGame := nil;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// #ToDo1 -oMvR: 30-6-2014: move to shared spot
|
SaveGameList;
|
||||||
TGameListPersist.Save(Resources.GetUserDataPath(Resources.UserGamesFileName), TGameList.Instance);
|
|
||||||
|
|
||||||
UpdateGameList;
|
|
||||||
finally
|
finally
|
||||||
vstGames.EndUpdate;
|
vstGames.EndUpdate;
|
||||||
end;
|
end;
|
||||||
@ -648,6 +731,22 @@ procedure TMainForm.actGameActiveExecute(Sender: TObject);
|
|||||||
begin
|
begin
|
||||||
if Assigned(vstGames.FocusedNode) then
|
if Assigned(vstGames.FocusedNode) then
|
||||||
begin
|
begin
|
||||||
|
{ For the sake of clarity, always save or revert changes when switching }
|
||||||
|
if Modified then
|
||||||
|
begin
|
||||||
|
case MessageBox(Self.Handle, 'Do you want to save your changes before switching the active game?', 'Set active game',
|
||||||
|
MB_YESNOCANCEL or MB_ICONQUESTION) of
|
||||||
|
ID_YES:
|
||||||
|
SaveActiveGame;
|
||||||
|
|
||||||
|
ID_NO:
|
||||||
|
RevertActiveGame;
|
||||||
|
|
||||||
|
ID_CANCEL:
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
vstGames.BeginUpdate;
|
vstGames.BeginUpdate;
|
||||||
try
|
try
|
||||||
ActiveGame := TGameList.Instance[vstGames.FocusedNode^.Index];
|
ActiveGame := TGameList.Instance[vstGames.FocusedNode^.Index];
|
||||||
@ -698,7 +797,7 @@ begin
|
|||||||
if not Supports(ActiveGame, IGameMapList, gameMapList) then
|
if not Supports(ActiveGame, IGameMapList, gameMapList) then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
map := gameMapList.GetMapList[Node^.Index];
|
map := gameMapList.Map[Node^.Index];
|
||||||
|
|
||||||
case Column of
|
case Column of
|
||||||
MapColumnName:
|
MapColumnName:
|
||||||
@ -782,7 +881,10 @@ begin
|
|||||||
if not Supports(ActiveGame, IGameMapList, gameMapList) then
|
if not Supports(ActiveGame, IGameMapList, gameMapList) then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
frmMapPreview.Load(gameMapList.GetMapList[Node^.Index].Name);
|
if Assigned(Node) then
|
||||||
|
frmMapPreview.Load(gameMapList.Map[Node^.Index].Name)
|
||||||
|
else
|
||||||
|
frmMapPreview.Clear;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -798,7 +900,7 @@ begin
|
|||||||
|
|
||||||
if TMapForm.Insert(Self, gameMapList, map) then
|
if TMapForm.Insert(Self, gameMapList, map) then
|
||||||
begin
|
begin
|
||||||
gameMapList.GetMapList.Add(map);
|
gameMapList.AddMap(map);
|
||||||
UpdateMapList;
|
UpdateMapList;
|
||||||
|
|
||||||
node := FindMapNode(map);
|
node := FindMapNode(map);
|
||||||
@ -832,7 +934,7 @@ begin
|
|||||||
selectedNodes := vstMapList.GetSortedSelection(True);
|
selectedNodes := vstMapList.GetSortedSelection(True);
|
||||||
|
|
||||||
for nodeIndex := High(selectedNodes) downto Low(selectedNodes) do
|
for nodeIndex := High(selectedNodes) downto Low(selectedNodes) do
|
||||||
gameMapList.GetMapList.Delete(selectedNodes[nodeIndex]^.Index);
|
gameMapList.DeleteMap(selectedNodes[nodeIndex]^.Index);
|
||||||
|
|
||||||
UpdateMapList;
|
UpdateMapList;
|
||||||
finally
|
finally
|
||||||
|
@ -87,8 +87,6 @@ object MapForm: TMapForm
|
|||||||
OnFocusChanging = vstMapFocusChanging
|
OnFocusChanging = vstMapFocusChanging
|
||||||
OnGetText = vstMapGetText
|
OnGetText = vstMapGetText
|
||||||
OnPaintText = vstMapPaintText
|
OnPaintText = vstMapPaintText
|
||||||
ExplicitLeft = 8
|
|
||||||
ExplicitWidth = 561
|
|
||||||
Columns = <>
|
Columns = <>
|
||||||
end
|
end
|
||||||
object pnlMapName: TPanel
|
object pnlMapName: TPanel
|
||||||
@ -105,7 +103,6 @@ object MapForm: TMapForm
|
|||||||
AutoSize = True
|
AutoSize = True
|
||||||
BevelOuter = bvNone
|
BevelOuter = bvNone
|
||||||
TabOrder = 2
|
TabOrder = 2
|
||||||
ExplicitTop = 475
|
|
||||||
DesignSize = (
|
DesignSize = (
|
||||||
561
|
561
|
||||||
21)
|
21)
|
||||||
@ -173,8 +170,6 @@ object MapForm: TMapForm
|
|||||||
Align = alLeft
|
Align = alLeft
|
||||||
BevelOuter = bvNone
|
BevelOuter = bvNone
|
||||||
TabOrder = 4
|
TabOrder = 4
|
||||||
ExplicitLeft = 0
|
|
||||||
ExplicitHeight = 342
|
|
||||||
inline frmMapPreview: TMapPreviewFrame
|
inline frmMapPreview: TMapPreviewFrame
|
||||||
AlignWithMargins = True
|
AlignWithMargins = True
|
||||||
Left = 0
|
Left = 0
|
||||||
@ -187,7 +182,7 @@ object MapForm: TMapForm
|
|||||||
Margins.Bottom = 0
|
Margins.Bottom = 0
|
||||||
Align = alTop
|
Align = alTop
|
||||||
TabOrder = 0
|
TabOrder = 0
|
||||||
ExplicitWidth = 342
|
ExplicitTop = 8
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -47,7 +47,7 @@ type
|
|||||||
function GetMapName: string;
|
function GetMapName: string;
|
||||||
procedure SetMapName(const Value: string);
|
procedure SetMapName(const Value: string);
|
||||||
protected
|
protected
|
||||||
procedure LoadSupportedMapList(AGame: IGameMapList);
|
procedure LoadPredefinedMapList(AGame: IGameMapList);
|
||||||
function CreateMap: TGameMap;
|
function CreateMap: TGameMap;
|
||||||
|
|
||||||
function FindMapNode(const AMapName: string): PVirtualNode;
|
function FindMapNode(const AMapName: string): PVirtualNode;
|
||||||
@ -82,7 +82,7 @@ class function TMapForm.Insert(AOwner: TComponent; AGame: IGameMapList; out AMap
|
|||||||
begin
|
begin
|
||||||
with Self.Create(AOwner) do
|
with Self.Create(AOwner) do
|
||||||
try
|
try
|
||||||
LoadSupportedMapList(AGame);
|
LoadPredefinedMapList(AGame);
|
||||||
|
|
||||||
Result := (ShowModal = mrOk);
|
Result := (ShowModal = mrOk);
|
||||||
if Result then
|
if Result then
|
||||||
@ -99,7 +99,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TMapForm.LoadSupportedMapList(AGame: IGameMapList);
|
procedure TMapForm.LoadPredefinedMapList(AGame: IGameMapList);
|
||||||
var
|
var
|
||||||
map: TGameMap;
|
map: TGameMap;
|
||||||
categoryNodes: TDictionary<string, PVirtualNode>;
|
categoryNodes: TDictionary<string, PVirtualNode>;
|
||||||
@ -113,7 +113,7 @@ begin
|
|||||||
|
|
||||||
categoryNodes := TDictionary<string, PVirtualNode>.Create;
|
categoryNodes := TDictionary<string, PVirtualNode>.Create;
|
||||||
try
|
try
|
||||||
for map in AGame.GetSupportedMapList do
|
for map in AGame.PredefinedMapList do
|
||||||
begin
|
begin
|
||||||
if categoryNodes.ContainsKey(map.Category) then
|
if categoryNodes.ContainsKey(map.Category) then
|
||||||
parentNode := categoryNodes[map.Category]
|
parentNode := categoryNodes[map.Category]
|
||||||
@ -130,6 +130,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
finally
|
finally
|
||||||
vstMap.FullExpand;
|
vstMap.FullExpand;
|
||||||
|
vstMap.EndUpdate;
|
||||||
|
|
||||||
node := vstMap.GetFirstLevel(1);
|
node := vstMap.GetFirstLevel(1);
|
||||||
if Assigned(node) then
|
if Assigned(node) then
|
||||||
@ -137,8 +138,6 @@ begin
|
|||||||
vstMap.FocusedNode := node;
|
vstMap.FocusedNode := node;
|
||||||
vstMap.Selected[node] := True;
|
vstMap.Selected[node] := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
vstMap.EndUpdate;
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user