diff --git a/ChivalryServerLauncher.dpr b/ChivalryServerLauncher.dpr index 1bb41a8..a1d972e 100644 --- a/ChivalryServerLauncher.dpr +++ b/ChivalryServerLauncher.dpr @@ -9,11 +9,11 @@ uses Game.Chivalry in 'source\model\Game.Chivalry.pas', Game.Registry in 'source\model\Game.Registry.pas', Resources in 'source\Resources.pas', - Game.Map in 'source\model\Game.Map.pas', Forms.Game in 'source\view\Forms.Game.pas' {GameForm}, Bindings.Converters in 'source\bindings\Bindings.Converters.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}; {$R *.res} diff --git a/ChivalryServerLauncher.dproj b/ChivalryServerLauncher.dproj index fa57723..949299a 100644 --- a/ChivalryServerLauncher.dproj +++ b/ChivalryServerLauncher.dproj @@ -6,7 +6,7 @@ ChivalryServerLauncher.dpr True Build - Win64 + Win32 3 Application @@ -89,7 +89,6 @@ -
GameForm
dfm @@ -97,6 +96,9 @@ + +
MapForm
+
Base diff --git a/resources/images/toolbar/down.png b/resources/images/toolbar/down.png new file mode 100644 index 0000000..d70cbf1 Binary files /dev/null and b/resources/images/toolbar/down.png differ diff --git a/resources/images/toolbar/up.png b/resources/images/toolbar/up.png new file mode 100644 index 0000000..3ca3afc Binary files /dev/null and b/resources/images/toolbar/up.png differ diff --git a/source/Resources.pas b/source/Resources.pas index d7cf7c9..7c67ffe 100644 --- a/source/Resources.pas +++ b/source/Resources.pas @@ -15,12 +15,17 @@ const implementation uses + System.SysUtils, + X2UtApp; function GetAssetPath(const AAsset: string): string; begin - Result := App.Path + AssetsPath + AAsset; + Result := App.UserPath + UserDataPath + AssetsPath + AAsset; + + if not FileExists(Result) then + Result := App.Path + AssetsPath + AAsset; end; diff --git a/source/model/Game.Chivalry.MedievalWarfare.pas b/source/model/Game.Chivalry.MedievalWarfare.pas index bdcae52..33b21d2 100644 --- a/source/model/Game.Chivalry.MedievalWarfare.pas +++ b/source/model/Game.Chivalry.MedievalWarfare.pas @@ -15,7 +15,7 @@ type in the base Chivalry class. } TChivalryMedievalWarfareGame = class(TChivalryGame) protected - procedure LoadSupportedMapList(AList: TList); override; + procedure LoadSupportedMapList(AList: TList); override; public class function GameName: string; override; class function AutoDetect: TCustomGame; override; @@ -31,7 +31,6 @@ uses Winapi.Windows, Resources, - Game.Map, Game.Registry; @@ -56,6 +55,7 @@ var serverPath: string; begin + Result := nil; steamPath := ''; registry := TRegistry.Create(KEY_READ); @@ -81,7 +81,7 @@ begin end; -procedure TChivalryMedievalWarfareGame.LoadSupportedMapList(AList: TList); +procedure TChivalryMedievalWarfareGame.LoadSupportedMapList(AList: TList); var mapListFileName: string; mapList: TMemIniFile; diff --git a/source/model/Game.Chivalry.pas b/source/model/Game.Chivalry.pas index f5702e1..b9505c2 100644 --- a/source/model/Game.Chivalry.pas +++ b/source/model/Game.Chivalry.pas @@ -18,17 +18,19 @@ type FServerName: string; FMessageOfTheDay: string; - FSupportedMapList: TList; - FMapList: TList; + FSupportedMapList: TObjectList; + FMapList: TObjectList; protected - procedure LoadSupportedMapList(AList: TList); virtual; abstract; + procedure DoLoad; override; + procedure DoSave; override; + + procedure LoadSupportedMapList(AList: TList); virtual; abstract; + + function CreateGameMap(const AMapName: string): TGameMap; virtual; public constructor Create(const ALocation: string); override; destructor Destroy; override; - procedure Load; override; - procedure Save; override; - { IGameNetwork } function GetServerPort: Integer; function GetPeerPort: Integer; @@ -53,13 +55,14 @@ type property MessageOfTheDay: string read GetMessageOfTheDay write SetMessageOfTheDay; { IGameMapList } - function GetMapList: System.Generics.Collections.TList; - function GetSupportedMapList: System.Generics.Collections.TList; + function GetSupportedMapList: TList; + function GetMapList: TList; end; implementation uses + System.Classes, System.IniFiles, System.SysUtils; @@ -74,6 +77,9 @@ const GameReplicationInfoServerName = 'ServerName'; GameReplicationInfoMessageOfTheDay = 'MessageOfTheDay'; + GameAOCGame = 'AOC.AOCGame'; + GameAOCGameMapList = 'Maplist'; + EngineSettingsFileName = 'UDKGame\Config\PCServer-UDKEngine.ini'; EngineSteam = 'OnlineSubsystemSteamworks.OnlineSubsystemSteamworks'; @@ -86,8 +92,8 @@ constructor TChivalryGame.Create(const ALocation: string); begin inherited Create(ALocation); - FSupportedMapList := TList.Create; - FMapList := TList.Create; + FSupportedMapList := TObjectList.Create(True); + FMapList := TObjectList.Create(True); LoadSupportedMapList(FSupportedMapList); end; @@ -101,10 +107,13 @@ begin inherited Destroy; end; -procedure TChivalryGame.Load; + +procedure TChivalryGame.DoLoad; var gameSettings: TMemIniFile; engineSettings: TMemIniFile; + mapListValues: TStringList; + valueIndex: Integer; begin gameSettings := TMemIniFile.Create(Location + GameSettingsFileName); @@ -114,6 +123,20 @@ begin FServerName := gameSettings.ReadString(GameReplicationInfo, GameReplicationInfoServerName, ''); FMessageOfTheDay := gameSettings.ReadString(GameReplicationInfo, GameReplicationInfoMessageOfTheDay, ''); + + { Maplist is special; it occurs multiple times and order matters } + mapListValues := TStringList.Create; + try + gameSettings.ReadSectionValues(GameAOCGame, mapListValues); + + for valueIndex := 0 to Pred(mapListValues.Count) do + begin + if SameText(mapListValues.Names[valueIndex], GameAOCGameMapList) then + FMapList.Add(CreateGameMap(mapListValues.ValueFromIndex[valueIndex])); + end; + finally + FreeAndNil(mapListValues); + end; finally FreeAndNil(gameSettings); end; @@ -124,17 +147,35 @@ begin finally FreeAndNil(engineSettings); end; - - // #ToDo1 -oMvR: 30-6-2014: load map list end; -procedure TChivalryGame.Save; +procedure TChivalryGame.DoSave; begin // #ToDo1 -oMvR: 30-6-2014: save to INI files end; +function TChivalryGame.CreateGameMap(const AMapName: string): TGameMap; +var + map: TGameMap; + +begin + Result := nil; + + for map in GetSupportedMapList do + if SameText(map.Name, AMapName) then + begin + Result := TGameMap.Create(map); + break; + end; + + + if not Assigned(Result) then + Result := TGameMap.Create(AMapName, '', ''); +end; + + function TChivalryGame.GetServerPort: Integer; begin Result := FServerPort; @@ -215,13 +256,13 @@ begin end; -function TChivalryGame.GetMapList: System.Generics.Collections.TList; +function TChivalryGame.GetMapList: TList; begin Result := FMapList; end; -function TChivalryGame.GetSupportedMapList: System.Generics.Collections.TList; +function TChivalryGame.GetSupportedMapList: TList; begin Result := FSupportedMapList; end; diff --git a/source/model/Game.Intf.pas b/source/model/Game.Intf.pas index 1680b93..d5ebfad 100644 --- a/source/model/Game.Intf.pas +++ b/source/model/Game.Intf.pas @@ -35,25 +35,55 @@ type end; - IGameMap = interface - ['{B50A090C-3731-401F-91B5-0895CBE99921}'] - function GetCategory: string; - function GetName: string; - function GetDisplayName: string; + TGameMap = class(TObject) + private + FCategory: string; + FName: string; + FDisplayName: string; + protected + function GetDisplayName: string; virtual; + public + constructor Create(const AName: string; const ADisplayName, ACategory: string); overload; + constructor Create(AClone: TGameMap); overload; - property Category: string read GetCategory; - property Name: string read GetName; + property Category: string read FCategory; + property Name: string read FName; property DisplayName: string read GetDisplayName; end; IGameMapList = interface ['{E8552B4C-9447-4FAD-BB20-C5EB3AF07B0E}'] - function GetSupportedMapList: TList; - function GetMapList: TList; + function GetSupportedMapList: TList; + function GetMapList: TList; end; implementation +uses + System.StrUtils; + + +{ TGameMap } +constructor TGameMap.Create(const AName, ADisplayName, ACategory: string); +begin + inherited Create; + + FName := AName; + FDisplayName := IfThen(Length(ADisplayName) > 0, ADisplayName, AName); + FCategory := ACategory; +end; + + +constructor TGameMap.Create(AClone: TGameMap); +begin + Create(AClone.Name, AClone.DisplayName, AClone.Category); +end; + + +function TGameMap.GetDisplayName: string; +begin + Result := FDisplayName; +end; end. diff --git a/source/model/Game.List.pas b/source/model/Game.List.pas index 103a322..cc3ed62 100644 --- a/source/model/Game.List.pas +++ b/source/model/Game.List.pas @@ -8,7 +8,7 @@ uses type - TGameList = class(TList) + TGameList = class(TObjectList) private class var SInstance: TGameList; protected @@ -37,7 +37,7 @@ end; class function TGameList.Instance: TGameList; begin if not Assigned(SInstance) then - SInstance := TGameList.Create; + SInstance := TGameList.Create(True); Result := SInstance; end; diff --git a/source/view/Forms.Main.dfm b/source/view/Forms.Main.dfm index 8355e5e..caf1dcb 100644 --- a/source/view/Forms.Main.dfm +++ b/source/view/Forms.Main.dfm @@ -21,32 +21,196 @@ object MainForm: TMainForm TextHeight = 13 object grdMenu: TJvGradient Left = 170 - Top = 75 + Top = 76 Width = 4 - Height = 472 + Height = 428 Align = alLeft StartColor = clBtnShadow EndColor = clBtnFace ExplicitLeft = 244 ExplicitTop = 67 + ExplicitHeight = 472 end object shpMenu: TShape Left = 169 - Top = 75 + Top = 76 Width = 1 - Height = 472 + Height = 428 Align = alLeft Pen.Color = clWindowFrame + ExplicitTop = 75 + ExplicitHeight = 472 + end + object bvlButtons: TBevel + Left = 0 + Top = 504 + Width = 739 + Height = 2 + Align = alBottom + Shape = bsTopLine + ExplicitTop = 498 + end + object shpLogo: TShape + Left = 0 + Top = 75 + Width = 739 + Height = 1 + Align = alTop + Pen.Color = clWindowFrame + ExplicitLeft = 174 + ExplicitWidth = 472 end object pcMain: TPageControl Left = 174 - Top = 75 + Top = 76 Width = 565 - Height = 472 - ActivePage = tsGames + Height = 428 + ActivePage = tsMapList Align = alClient Style = tsButtons TabOrder = 2 + object tsMapList: TTabSheet + Caption = 'Game - MapList' + ImageIndex = 2 + object vstMapList: TVirtualStringTree + AlignWithMargins = True + Left = 8 + Top = 30 + Width = 541 + Height = 359 + Margins.Left = 8 + Margins.Top = 0 + Margins.Right = 8 + Margins.Bottom = 8 + Align = alClient + Header.AutoSizeIndex = 0 + Header.Font.Charset = DEFAULT_CHARSET + Header.Font.Color = clWindowText + Header.Font.Height = -11 + Header.Font.Name = 'Tahoma' + Header.Font.Style = [] + Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible] + TabOrder = 0 + TreeOptions.MiscOptions = [toAcceptOLEDrop, toFullRepaintOnResize, toInitOnSave, toWheelPanning, toEditOnClick] + TreeOptions.PaintOptions = [toHideFocusRect, toShowDropmark, toThemeAware, toUseBlendedImages] + TreeOptions.SelectionOptions = [toFullRowSelect, toMiddleClickSelect, toMultiSelect, toRightClickSelect] + OnGetText = vstMapListGetText + Columns = < + item + Position = 0 + Width = 337 + WideText = 'Map name' + end + item + Position = 1 + Width = 200 + WideText = 'Category' + end> + end + object ToolBar1: TToolBar + AlignWithMargins = True + Left = 8 + Top = 8 + Width = 541 + Height = 22 + Margins.Left = 8 + Margins.Top = 8 + Margins.Right = 8 + Margins.Bottom = 0 + AutoSize = True + ButtonWidth = 89 + Images = glToolbar + List = True + ShowCaptions = True + TabOrder = 1 + object tbMapAdd: TToolButton + Left = 0 + Top = 0 + Action = actMapAdd + AutoSize = True + end + object tbMapRemove: TToolButton + Left = 73 + Top = 0 + Action = actMapRemove + AutoSize = True + end + object tbMapSep1: TToolButton + Left = 166 + Top = 0 + Width = 8 + Caption = 'tbMapSep1' + ImageIndex = 2 + Style = tbsSeparator + end + object tbMapUp: TToolButton + Left = 174 + Top = 0 + Action = actMapUp + AutoSize = True + end + object tbMapDown: TToolButton + Left = 246 + Top = 0 + Action = actMapDown + AutoSize = True + end + end + end + object tsConfiguration: TTabSheet + Caption = 'Server - Configuration' + ImageIndex = 1 + object gbServerName: TGroupBox + AlignWithMargins = True + Left = 8 + Top = 8 + Width = 541 + Height = 77 + Margins.Left = 8 + Margins.Top = 8 + Margins.Right = 8 + Margins.Bottom = 8 + Align = alTop + TabOrder = 0 + DesignSize = ( + 541 + 77) + object lblServerName: TLabel + Left = 12 + Top = 15 + Width = 65 + Height = 13 + Caption = 'Server name:' + end + object lblMessageOfTheDay: TLabel + Left = 12 + Top = 42 + Width = 99 + Height = 13 + Caption = 'Message of the day:' + end + object edtServerName: TEdit + Left = 132 + Top = 12 + Width = 401 + Height = 21 + Hint = 'INI:Engine.GameReplicationInfo>ServerName' + Anchors = [akLeft, akTop, akRight] + TabOrder = 0 + OnChange = EditChange + end + object edtMessageOfTheDay: TEdit + Left = 132 + Top = 39 + Width = 401 + Height = 21 + Hint = 'INI:Engine.GameReplicationInfo>MessageOfTheDay' + Anchors = [akLeft, akTop, akRight] + TabOrder = 1 + OnChange = EditChange + end + end + end object tsNetwork: TTabSheet Caption = 'Server - Network' object gbPorts: TGroupBox @@ -116,88 +280,6 @@ object MainForm: TMainForm end end end - object tsConfiguration: TTabSheet - Caption = 'Server - Configuration' - ImageIndex = 1 - object gbServerName: TGroupBox - AlignWithMargins = True - Left = 8 - Top = 8 - Width = 541 - Height = 77 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 - Align = alTop - TabOrder = 0 - DesignSize = ( - 541 - 77) - object lblServerName: TLabel - Left = 12 - Top = 15 - Width = 65 - Height = 13 - Caption = 'Server name:' - end - object lblMessageOfTheDay: TLabel - Left = 12 - Top = 42 - Width = 99 - Height = 13 - Caption = 'Message of the day:' - end - object edtServerName: TEdit - Left = 132 - Top = 12 - Width = 401 - Height = 21 - Hint = 'INI:Engine.GameReplicationInfo>ServerName' - Anchors = [akLeft, akTop, akRight] - TabOrder = 0 - OnChange = EditChange - end - object edtMessageOfTheDay: TEdit - Left = 132 - Top = 39 - Width = 401 - Height = 21 - Hint = 'INI:Engine.GameReplicationInfo>MessageOfTheDay' - Anchors = [akLeft, akTop, akRight] - TabOrder = 1 - OnChange = EditChange - end - end - end - object tsMapList: TTabSheet - Caption = 'Game - MapList' - ImageIndex = 2 - object vstMapList: TVirtualStringTree - AlignWithMargins = True - Left = 8 - Top = 8 - Width = 541 - Height = 425 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 - Align = alClient - Header.AutoSizeIndex = 0 - Header.Font.Charset = DEFAULT_CHARSET - Header.Font.Color = clWindowText - Header.Font.Height = -11 - Header.Font.Name = 'Tahoma' - Header.Font.Style = [] - Header.Options = [hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible] - TabOrder = 0 - Columns = < - item - Position = 0 - end> - end - end object tsGames: TTabSheet Caption = 'Launcher - Game locations' ImageIndex = 3 @@ -206,7 +288,7 @@ object MainForm: TMainForm Left = 8 Top = 30 Width = 541 - Height = 341 + Height = 297 Margins.Left = 8 Margins.Top = 0 Margins.Right = 8 @@ -224,7 +306,6 @@ object MainForm: TMainForm TreeOptions.SelectionOptions = [toFullRowSelect, toMiddleClickSelect, toRightClickSelect] OnFocusChanged = vstGamesFocusChanged OnGetText = vstGamesGetText - OnInitNode = vstGamesInitNode Columns = < item Position = 0 @@ -240,7 +321,7 @@ object MainForm: TMainForm object pnlGamesWarning: TPanel AlignWithMargins = True Left = 8 - Top = 379 + Top = 335 Width = 541 Height = 54 Margins.Left = 8 @@ -486,6 +567,7 @@ object MainForm: TMainForm Height = 17 Caption = 'www.delphi-jedi.org' TabOrder = 5 + TabStop = True OnLinkClick = llLinkClick end object llVirtualTreeview: TLinkLabel @@ -496,7 +578,8 @@ object MainForm: TMainForm Caption = 'www.soft-gems.net/index.php/controls/virtual-treeview' - TabOrder = 6 + TabOrder = 7 + TabStop = True OnLinkClick = llLinkClick end object llChivalry: TLinkLabel @@ -506,6 +589,7 @@ object MainForm: TMainForm Height = 17 Caption = 'www.tornbanner.com' TabOrder = 0 + TabStop = True OnLinkClick = llLinkClick end object llPixelophilia: TLinkLabel @@ -517,6 +601,7 @@ object MainForm: TMainForm 'omercetin.deviantart.' + 'com' TabOrder = 1 + TabStop = True OnLinkClick = llLinkClick end object llGentleface: TLinkLabel @@ -528,6 +613,7 @@ object MainForm: TMainForm 'gentleface.co' + 'm/free_icon_set.html' TabOrder = 3 + TabStop = True OnLinkClick = llLinkClick end object llPixelophiliaCC: TLinkLabel @@ -539,6 +625,7 @@ object MainForm: TMainForm 'Crea' + 'tive Commons Attribution-Noncommercial-No Derivate 3.0' TabOrder = 2 + TabStop = True OnLinkClick = llLinkClick end object llGentlefaceCC: TLinkLabel @@ -550,6 +637,7 @@ object MainForm: TMainForm 'Creativ' + 'e Commons Attribution-Noncommercial 3.0' TabOrder = 4 + TabStop = True OnLinkClick = llLinkClick end object llSuperObject: TLinkLabel @@ -560,33 +648,19 @@ object MainForm: TMainForm Caption = 'code.google.com' + '/p/superobject' - TabOrder = 7 + TabOrder = 6 + TabStop = True OnLinkClick = llLinkClick end end end object mbMenu: TX2MenuBar Left = 0 - Top = 75 + Top = 76 Width = 169 - Height = 472 + Height = 428 Align = alLeft Groups = < - item - Caption = 'Server' - Expanded = True - Items = < - item - Caption = 'Network' - Enabled = False - ImageIndex = 0 - end - item - Caption = 'Configuration' - Enabled = False - ImageIndex = 1 - end> - end item Caption = 'Game' Expanded = True @@ -597,6 +671,21 @@ object MainForm: TMainForm ImageIndex = 2 end> end + item + Caption = 'Server' + Expanded = True + Items = < + item + Caption = 'Configuration' + Enabled = False + ImageIndex = 1 + end + item + Caption = 'Network' + Enabled = False + ImageIndex = 0 + end> + end item Caption = 'Launcher' Expanded = True @@ -1476,13 +1565,70 @@ object MainForm: TMainForm ExplicitWidth = 307 end end + object pnlButtons: TPanel + AlignWithMargins = True + Left = 8 + Top = 514 + Width = 723 + Height = 25 + Margins.Left = 8 + Margins.Top = 8 + Margins.Right = 8 + Margins.Bottom = 8 + Align = alBottom + BevelOuter = bvNone + TabOrder = 3 + object btnLaunch: TButton + AlignWithMargins = True + Left = 0 + Top = 0 + Width = 113 + Height = 25 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 8 + Margins.Bottom = 0 + Action = actLaunch + Align = alLeft + DropDownMenu = pmnLaunch + Style = bsSplitButton + TabOrder = 0 + end + object btnClose: TButton + Left = 632 + Top = 0 + Width = 91 + Height = 25 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Action = actClose + Align = alRight + TabOrder = 1 + end + object btnSave: TButton + AlignWithMargins = True + Left = 533 + Top = 0 + Width = 91 + Height = 25 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 8 + Margins.Bottom = 0 + Action = actSave + Align = alRight + TabOrder = 2 + end + end object mbpMenuPainter: TX2MenuBarmusikCubePainter - Left = 52 - Top = 460 + Left = 44 + Top = 444 end object gcMenu: TX2GraphicContainer - Left = 52 - Top = 400 + Left = 44 + Top = 388 object gcMenuNetwork: TX2GraphicContainerItem Picture.Data = { 0954506E67496D61676589504E470D0A1A0A0000000D49484452000000100000 @@ -1656,13 +1802,13 @@ object MainForm: TMainForm object glMenu: TX2GraphicList Container = gcMenu Convert = False - Left = 112 - Top = 400 + Left = 104 + Top = 388 Bitmap = {} end object gcToolbar: TX2GraphicContainer - Left = 52 - Top = 340 + Left = 44 + Top = 328 object gcToolbaradd: TX2GraphicContainerItem Picture.Data = { 0954506E67496D61676589504E470D0A1A0A0000000D49484452000000100000 @@ -1879,17 +2025,247 @@ object MainForm: TMainForm 6CD9A8AA24D266B50000000049454E44AE426082} PictureName = 'remove' end + object gcToolbarup: TX2GraphicContainerItem + Picture.Data = { + 0954506E67496D61676589504E470D0A1A0A0000000D49484452000000100000 + 001008060000001FF3FF6100000B2769545874584D4C3A636F6D2E61646F6265 + 2E786D7000000000003C3F787061636B657420626567696E3D22EFBBBF222069 + 643D2257354D304D7043656869487A7265537A4E54637A6B633964223F3E0A3C + 783A786D706D65746120786D6C6E733A783D2261646F62653A6E733A6D657461 + 2F2220783A786D70746B3D2241646F626520584D5020436F726520352E302D63 + 3036302036312E3133343737372C20323031302F30322F31322D31373A33323A + 30302020202020202020223E0A203C7264663A52444620786D6C6E733A726466 + 3D22687474703A2F2F7777772E77332E6F72672F313939392F30322F32322D72 + 64662D73796E7461782D6E7323223E0A20203C7264663A446573637269707469 + 6F6E207264663A61626F75743D22220A20202020786D6C6E733A70686F746F73 + 686F703D22687474703A2F2F6E732E61646F62652E636F6D2F70686F746F7368 + 6F702F312E302F220A20202020786D6C6E733A786D705269676874733D226874 + 74703A2F2F6E732E61646F62652E636F6D2F7861702F312E302F726967687473 + 2F220A20202020786D6C6E733A786D703D22687474703A2F2F6E732E61646F62 + 652E636F6D2F7861702F312E302F220A20202020786D6C6E733A786D704D4D3D + 22687474703A2F2F6E732E61646F62652E636F6D2F7861702F312E302F6D6D2F + 220A20202020786D6C6E733A73744576743D22687474703A2F2F6E732E61646F + 62652E636F6D2F7861702F312E302F73547970652F5265736F75726365457665 + 6E7423220A20202020786D6C6E733A64633D22687474703A2F2F7075726C2E6F + 72672F64632F656C656D656E74732F312E312F220A20202020786D6C6E733A49 + 70746334786D70436F72653D22687474703A2F2F697074632E6F72672F737464 + 2F4970746334786D70436F72652F312E302F786D6C6E732F220A20202020786D + 6C6E733A706C75735F315F3D22687474703A2F2F6E732E757365706C75732E6F + 72672F6C64662F786D702F312E302F220A20202070686F746F73686F703A4865 + 61646C696E653D225573657220696E74657266616365206D616B65207570220A + 202020786D705269676874733A4D61726B65643D2254727565220A202020786D + 703A4D65746164617461446174653D22323031312D30312D32355431333A3535 + 3A31352B30313A3030220A202020786D704D4D3A496E7374616E636549443D22 + 786D702E6969643A393546364339354138323238453031313938394343304131 + 4144303242354332220A202020786D704D4D3A446F63756D656E7449443D2278 + 6D702E6469643A30314135323744354530323745303131393839434330413141 + 44303242354332220A202020786D704D4D3A4F726967696E616C446F63756D65 + 6E7449443D22786D702E6469643A303141353237443545303237453031313938 + 3943433041314144303242354332223E0A2020203C786D705269676874733A55 + 736167655465726D733E0A202020203C7264663A416C743E0A20202020203C72 + 64663A6C6920786D6C3A6C616E673D22782D64656661756C74223E4372656174 + 69766520436F6D6D6F6E73204174747269627574696F6E2D4E6F6E436F6D6D65 + 726369616C206C6963656E73653C2F7264663A6C693E0A202020203C2F726466 + 3A416C743E0A2020203C2F786D705269676874733A55736167655465726D733E + 0A2020203C786D704D4D3A486973746F72793E0A202020203C7264663A536571 + 3E0A20202020203C7264663A6C690A20202020202073744576743A616374696F + 6E3D227361766564220A20202020202073744576743A696E7374616E63654944 + 3D22786D702E6969643A30314135323744354530323745303131393839434330 + 41314144303242354332220A20202020202073744576743A7768656E3D223230 + 31312D30312D32345431383A33393A30322B30313A3030220A20202020202073 + 744576743A6368616E6765643D222F6D65746164617461222F3E0A2020202020 + 3C7264663A6C690A20202020202073744576743A616374696F6E3D2273617665 + 64220A20202020202073744576743A696E7374616E636549443D22786D702E69 + 69643A3935463643393541383232384530313139383943433041314144303242 + 354332220A20202020202073744576743A7768656E3D22323031312D30312D32 + 355431333A35353A31352B30313A3030220A20202020202073744576743A6368 + 616E6765643D222F6D65746164617461222F3E0A202020203C2F7264663A5365 + 713E0A2020203C2F786D704D4D3A486973746F72793E0A2020203C64633A6372 + 6561746F723E0A202020203C7264663A5365713E0A20202020203C7264663A6C + 693E47656E746C656661636520637573746F6D20746F6F6C6261722069636F6E + 732064657369676E3C2F7264663A6C693E0A202020203C2F7264663A5365713E + 0A2020203C2F64633A63726561746F723E0A2020203C64633A64657363726970 + 74696F6E3E0A202020203C7264663A416C743E0A20202020203C7264663A6C69 + 20786D6C3A6C616E673D22782D64656661756C74223E576972656672616D6520 + 6D6F6E6F20746F6F6C6261722069636F6E733C2F7264663A6C693E0A20202020 + 3C2F7264663A416C743E0A2020203C2F64633A6465736372697074696F6E3E0A + 2020203C64633A7375626A6563743E0A202020203C7264663A4261673E0A2020 + 2020203C7264663A6C693E637573746F6D2069636F6E2064657369676E3C2F72 + 64663A6C693E0A20202020203C7264663A6C693E746F6F6C6261722069636F6E + 733C2F7264663A6C693E0A20202020203C7264663A6C693E637573746F6D2069 + 636F6E733C2F7264663A6C693E0A20202020203C7264663A6C693E696E746572 + 666163652064657369676E3C2F7264663A6C693E0A20202020203C7264663A6C + 693E75692064657369676E3C2F7264663A6C693E0A20202020203C7264663A6C + 693E6775692064657369676E3C2F7264663A6C693E0A20202020203C7264663A + 6C693E7461736B6261722069636F6E733C2F7264663A6C693E0A202020203C2F + 7264663A4261673E0A2020203C2F64633A7375626A6563743E0A2020203C6463 + 3A7269676874733E0A202020203C7264663A416C743E0A20202020203C726466 + 3A6C6920786D6C3A6C616E673D22782D64656661756C74223E43726561746976 + 6520436F6D6D6F6E73204174747269627574696F6E2D4E6F6E436F6D6D657263 + 69616C206C6963656E73653C2F7264663A6C693E0A202020203C2F7264663A41 + 6C743E0A2020203C2F64633A7269676874733E0A2020203C4970746334786D70 + 436F72653A43726561746F72436F6E74616374496E666F0A2020202049707463 + 34786D70436F72653A436955726C576F726B3D22687474703A2F2F7777772E67 + 656E746C65666163652E636F6D222F3E0A2020203C706C75735F315F3A496D61 + 676543726561746F723E0A202020203C7264663A5365713E0A20202020203C72 + 64663A6C690A202020202020706C75735F315F3A496D61676543726561746F72 + 4E616D653D2267656E746C65666163652E636F6D222F3E0A202020203C2F7264 + 663A5365713E0A2020203C2F706C75735F315F3A496D61676543726561746F72 + 3E0A2020203C706C75735F315F3A436F707972696768744F776E65723E0A2020 + 20203C7264663A5365713E0A20202020203C7264663A6C690A20202020202070 + 6C75735F315F3A436F707972696768744F776E65724E616D653D2267656E746C + 65666163652E636F6D222F3E0A202020203C2F7264663A5365713E0A2020203C + 2F706C75735F315F3A436F707972696768744F776E65723E0A20203C2F726466 + 3A4465736372697074696F6E3E0A203C2F7264663A5244463E0A3C2F783A786D + 706D6574613E0A3C3F787061636B657420656E643D2272223F3E2642F45B0000 + 001974455874536F6674776172650041646F626520496D616765526561647971 + C9653C0000003C74455874414C54546167005468697320697320746865206963 + 6F6E2066726F6D2047656E746C65666163652E636F6D20667265652069636F6E + 73207365742E20D86BE8C40000004474455874436F7079726967687400437265 + 617469766520436F6D6D6F6E73204174747269627574696F6E204E6F6E2D436F + 6D6D65726369616C204E6F2044657269766174697665737BDDB0A00000004569 + 5458744465736372697074696F6E000000000054686973206973207468652069 + 636F6E2066726F6D2047656E746C65666163652E636F6D20667265652069636F + 6E73207365742E20BC11F81A0000004869545874436F70797269676874000000 + 0000437265617469766520436F6D6D6F6E73204174747269627574696F6E204E + 6F6E2D436F6D6D65726369616C204E6F2044657269766174697665735882CB05 + 000000F64944415478DACD923B0A83401086B3821622B8888556A6B0B2DA23E8 + 0D921B2427CB11921B98236C6565112B2D442CAC2C343361577C850402C18581 + 9D61FE6F666796EC7E3C64BB00C61825845CF0DEF7FD99735E7F0D0882802A8A + 1223478478D775519224F54780EFFB7B105FA13A1BC7A10B841CD3347DBC0578 + 9EC744652A42B2E2E063275996F105C075DD8518FC082F208AE7903CCFF900B0 + 6DFB80030393491CC54551BC3A701C870A0813CFA971B06559DE88699A2739ED + B1B8AAAAC9C02CCB9A40E47688AEEB180CC7E2A66956576618C61C7227AAAA86 + 408AA10B0E16B56DBB2A9647D3348AF9600CF337FC95FF067802BD7672CA72B0 + 05AE0000000049454E44AE426082} + PictureName = 'up' + end + object gcToolbardown: TX2GraphicContainerItem + Picture.Data = { + 0954506E67496D61676589504E470D0A1A0A0000000D49484452000000100000 + 001008060000001FF3FF6100000B2769545874584D4C3A636F6D2E61646F6265 + 2E786D7000000000003C3F787061636B657420626567696E3D22EFBBBF222069 + 643D2257354D304D7043656869487A7265537A4E54637A6B633964223F3E0A3C + 783A786D706D65746120786D6C6E733A783D2261646F62653A6E733A6D657461 + 2F2220783A786D70746B3D2241646F626520584D5020436F726520352E302D63 + 3036302036312E3133343737372C20323031302F30322F31322D31373A33323A + 30302020202020202020223E0A203C7264663A52444620786D6C6E733A726466 + 3D22687474703A2F2F7777772E77332E6F72672F313939392F30322F32322D72 + 64662D73796E7461782D6E7323223E0A20203C7264663A446573637269707469 + 6F6E207264663A61626F75743D22220A20202020786D6C6E733A70686F746F73 + 686F703D22687474703A2F2F6E732E61646F62652E636F6D2F70686F746F7368 + 6F702F312E302F220A20202020786D6C6E733A786D705269676874733D226874 + 74703A2F2F6E732E61646F62652E636F6D2F7861702F312E302F726967687473 + 2F220A20202020786D6C6E733A786D703D22687474703A2F2F6E732E61646F62 + 652E636F6D2F7861702F312E302F220A20202020786D6C6E733A786D704D4D3D + 22687474703A2F2F6E732E61646F62652E636F6D2F7861702F312E302F6D6D2F + 220A20202020786D6C6E733A73744576743D22687474703A2F2F6E732E61646F + 62652E636F6D2F7861702F312E302F73547970652F5265736F75726365457665 + 6E7423220A20202020786D6C6E733A64633D22687474703A2F2F7075726C2E6F + 72672F64632F656C656D656E74732F312E312F220A20202020786D6C6E733A49 + 70746334786D70436F72653D22687474703A2F2F697074632E6F72672F737464 + 2F4970746334786D70436F72652F312E302F786D6C6E732F220A20202020786D + 6C6E733A706C75735F315F3D22687474703A2F2F6E732E757365706C75732E6F + 72672F6C64662F786D702F312E302F220A20202070686F746F73686F703A4865 + 61646C696E653D225573657220696E74657266616365206D616B65207570220A + 202020786D705269676874733A4D61726B65643D2254727565220A202020786D + 703A4D65746164617461446174653D22323031312D30312D32355431333A3535 + 3A31352B30313A3030220A202020786D704D4D3A496E7374616E636549443D22 + 786D702E6969643A444134444346354138323238453031313938394343304131 + 4144303242354332220A202020786D704D4D3A446F63756D656E7449443D2278 + 6D702E6469643A33373930333144354530323745303131393839434330413141 + 44303242354332220A202020786D704D4D3A4F726967696E616C446F63756D65 + 6E7449443D22786D702E6469643A333739303331443545303237453031313938 + 3943433041314144303242354332223E0A2020203C786D705269676874733A55 + 736167655465726D733E0A202020203C7264663A416C743E0A20202020203C72 + 64663A6C6920786D6C3A6C616E673D22782D64656661756C74223E4372656174 + 69766520436F6D6D6F6E73204174747269627574696F6E2D4E6F6E436F6D6D65 + 726369616C206C6963656E73653C2F7264663A6C693E0A202020203C2F726466 + 3A416C743E0A2020203C2F786D705269676874733A55736167655465726D733E + 0A2020203C786D704D4D3A486973746F72793E0A202020203C7264663A536571 + 3E0A20202020203C7264663A6C690A20202020202073744576743A616374696F + 6E3D227361766564220A20202020202073744576743A696E7374616E63654944 + 3D22786D702E6969643A33373930333144354530323745303131393839434330 + 41314144303242354332220A20202020202073744576743A7768656E3D223230 + 31312D30312D32345431383A33393A30322B30313A3030220A20202020202073 + 744576743A6368616E6765643D222F6D65746164617461222F3E0A2020202020 + 3C7264663A6C690A20202020202073744576743A616374696F6E3D2273617665 + 64220A20202020202073744576743A696E7374616E636549443D22786D702E69 + 69643A4441344443463541383232384530313139383943433041314144303242 + 354332220A20202020202073744576743A7768656E3D22323031312D30312D32 + 355431333A35353A31352B30313A3030220A20202020202073744576743A6368 + 616E6765643D222F6D65746164617461222F3E0A202020203C2F7264663A5365 + 713E0A2020203C2F786D704D4D3A486973746F72793E0A2020203C64633A6372 + 6561746F723E0A202020203C7264663A5365713E0A20202020203C7264663A6C + 693E47656E746C656661636520637573746F6D20746F6F6C6261722069636F6E + 732064657369676E3C2F7264663A6C693E0A202020203C2F7264663A5365713E + 0A2020203C2F64633A63726561746F723E0A2020203C64633A64657363726970 + 74696F6E3E0A202020203C7264663A416C743E0A20202020203C7264663A6C69 + 20786D6C3A6C616E673D22782D64656661756C74223E576972656672616D6520 + 6D6F6E6F20746F6F6C6261722069636F6E733C2F7264663A6C693E0A20202020 + 3C2F7264663A416C743E0A2020203C2F64633A6465736372697074696F6E3E0A + 2020203C64633A7375626A6563743E0A202020203C7264663A4261673E0A2020 + 2020203C7264663A6C693E637573746F6D2069636F6E2064657369676E3C2F72 + 64663A6C693E0A20202020203C7264663A6C693E746F6F6C6261722069636F6E + 733C2F7264663A6C693E0A20202020203C7264663A6C693E637573746F6D2069 + 636F6E733C2F7264663A6C693E0A20202020203C7264663A6C693E696E746572 + 666163652064657369676E3C2F7264663A6C693E0A20202020203C7264663A6C + 693E75692064657369676E3C2F7264663A6C693E0A20202020203C7264663A6C + 693E6775692064657369676E3C2F7264663A6C693E0A20202020203C7264663A + 6C693E7461736B6261722069636F6E733C2F7264663A6C693E0A202020203C2F + 7264663A4261673E0A2020203C2F64633A7375626A6563743E0A2020203C6463 + 3A7269676874733E0A202020203C7264663A416C743E0A20202020203C726466 + 3A6C6920786D6C3A6C616E673D22782D64656661756C74223E43726561746976 + 6520436F6D6D6F6E73204174747269627574696F6E2D4E6F6E436F6D6D657263 + 69616C206C6963656E73653C2F7264663A6C693E0A202020203C2F7264663A41 + 6C743E0A2020203C2F64633A7269676874733E0A2020203C4970746334786D70 + 436F72653A43726561746F72436F6E74616374496E666F0A2020202049707463 + 34786D70436F72653A436955726C576F726B3D22687474703A2F2F7777772E67 + 656E746C65666163652E636F6D222F3E0A2020203C706C75735F315F3A496D61 + 676543726561746F723E0A202020203C7264663A5365713E0A20202020203C72 + 64663A6C690A202020202020706C75735F315F3A496D61676543726561746F72 + 4E616D653D2267656E746C65666163652E636F6D222F3E0A202020203C2F7264 + 663A5365713E0A2020203C2F706C75735F315F3A496D61676543726561746F72 + 3E0A2020203C706C75735F315F3A436F707972696768744F776E65723E0A2020 + 20203C7264663A5365713E0A20202020203C7264663A6C690A20202020202070 + 6C75735F315F3A436F707972696768744F776E65724E616D653D2267656E746C + 65666163652E636F6D222F3E0A202020203C2F7264663A5365713E0A2020203C + 2F706C75735F315F3A436F707972696768744F776E65723E0A20203C2F726466 + 3A4465736372697074696F6E3E0A203C2F7264663A5244463E0A3C2F783A786D + 706D6574613E0A3C3F787061636B657420656E643D2272223F3E5B45C3580000 + 001974455874536F6674776172650041646F626520496D616765526561647971 + C9653C0000003C74455874414C54546167005468697320697320746865206963 + 6F6E2066726F6D2047656E746C65666163652E636F6D20667265652069636F6E + 73207365742E20D86BE8C40000004474455874436F7079726967687400437265 + 617469766520436F6D6D6F6E73204174747269627574696F6E204E6F6E2D436F + 6D6D65726369616C204E6F2044657269766174697665737BDDB0A00000004569 + 5458744465736372697074696F6E000000000054686973206973207468652069 + 636F6E2066726F6D2047656E746C65666163652E636F6D20667265652069636F + 6E73207365742E20BC11F81A0000004869545874436F70797269676874000000 + 0000437265617469766520436F6D6D6F6E73204174747269627574696F6E204E + 6F6E2D436F6D6D65726369616C204E6F2044657269766174697665735882CB05 + 000001064944415478DADD523B0A83401075051BAB6DAC4488082208EA11DC1B + 24378827CB11921B982388958D18102B9BAD6C04CDCCB28A623E854DC854B333 + F3DEBC9D19A2EC34F207046118C6849014FC6C180696E739FF04088280AAAA8A + F5D1388E8CF8BE8F8F18931010244551BC24F13C4F80A161244377E2BAEE1980 + 97A9084990B92CCB1589E33814952EC00AF88998816DDB47E88C2454E68492BA + AE05896559B36C99E7F04EAAAABACD43344D330250BA20E1A844765AC501CC9A + A6C9365B300C0307331783CF25C10C069FB56D9BBD5D23A5F4004AAE0BB99365 + D0F9C4397F7CBD035DD7A9FC4EB400B3AEEB36DB797B489AA65139580507D6F7 + FDCBD5FEC029EF257802B99873F02768CFEC0000000049454E44AE426082} + PictureName = 'down' + end end object glToolbar: TX2GraphicList Container = gcToolbar - Left = 112 - Top = 340 + Left = 104 + Top = 328 Bitmap = {} end object alMain: TActionList Images = glToolbar - Left = 52 - Top = 288 + Left = 44 + Top = 276 object actGameAdd: TAction Caption = '&Add game' ImageIndex = 0 @@ -1901,5 +2277,45 @@ object MainForm: TMainForm ImageIndex = 1 OnExecute = actGameRemoveExecute end + object actMapAdd: TAction + Caption = '&Add map' + ImageIndex = 0 + OnExecute = actMapAddExecute + end + object actMapRemove: TAction + Caption = '&Remove map' + Enabled = False + ImageIndex = 1 + end + object actMapUp: TAction + Caption = 'Move &up' + Enabled = False + ImageIndex = 2 + end + object actMapDown: TAction + Caption = 'Move &down' + Enabled = False + ImageIndex = 3 + end + object actLaunch: TAction + Caption = '&Launch server' + end + object actCopyCmdLine: TAction + Caption = '&Copy command line' + end + object actSave: TAction + Caption = '&Save changes' + end + object actClose: TAction + Caption = '&Close' + OnExecute = actCloseExecute + end + end + object pmnLaunch: TPopupMenu + Left = 144 + Top = 500 + object pmnLaunchCopyCmdLine: TMenuItem + Action = actCopyCmdLine + end end end diff --git a/source/view/Forms.Main.pas b/source/view/Forms.Main.pas index 10d8f5c..c853c31 100644 --- a/source/view/Forms.Main.pas +++ b/source/view/Forms.Main.pas @@ -13,6 +13,7 @@ uses Vcl.Imaging.pngimage, Vcl.ImgList, Vcl.Mask, + Vcl.Menus, Vcl.StdCtrls, Vcl.ToolWin, @@ -32,6 +33,17 @@ uses type TMainForm = class(TForm) + actGameAdd: TAction; + actGameRemove: TAction; + actMapAdd: TAction; + actMapDown: TAction; + actMapRemove: TAction; + actMapUp: TAction; + alMain: TActionList; + btnClose: TButton; + btnLaunch: TButton; + btnSave: TButton; + bvlButtons: TBevel; edtMessageOfTheDay: TEdit; edtServerName: TEdit; gbPorts: TGroupBox; @@ -44,54 +56,67 @@ type gcMenuSettings: TX2GraphicContainerItem; gcToolbar: TX2GraphicContainer; gcToolbaradd: TX2GraphicContainerItem; + gcToolbardown: TX2GraphicContainerItem; gcToolbarremove: TX2GraphicContainerItem; + gcToolbarup: TX2GraphicContainerItem; glMenu: TX2GraphicList; glToolbar: TX2GraphicList; grdMenu: TJvGradient; imgGamesWarning: TImage; imgLogo: TImage; lblChivalry: TLabel; - lblPixelophilia: TLabel; lblGamesWarning: TLabel; lblGentleface: TLabel; lblJCL: TLabel; lblMessageOfTheDay: TLabel; lblPeerPort: TLabel; + lblPixelophilia: TLabel; lblQueryPort: TLabel; lblServerName: TLabel; lblServerPort: TLabel; + lblSuperObject: TLabel; lblVirtualTreeview: TLabel; llChivalry: TLinkLabel; - llPixelophilia: TLinkLabel; llGentleface: TLinkLabel; - llPixelophiliaCC: TLinkLabel; llGentlefaceCC: TLinkLabel; llJCL: TLinkLabel; + llPixelophilia: TLinkLabel; + llPixelophiliaCC: TLinkLabel; + llSuperObject: TLinkLabel; llVirtualTreeview: TLinkLabel; mbMenu: TX2MenuBar; mbpMenuPainter: TX2MenuBarmusikCubePainter; pcMain: TPageControl; - pnlLogo: TPanel; + pmnLaunch: TPopupMenu; + pmnLaunchCopyCmdLine: TMenuItem; + pnlButtons: TPanel; pnlGamesWarning: TPanel; + pnlLogo: TPanel; sePeerPort: TJvSpinEdit; seQueryPort: TJvSpinEdit; seServerPort: TJvSpinEdit; + shpLogo: TShape; shpMenu: TShape; - tbGames: TToolBar; tbGameAdd: TToolButton; tbGameRemove: TToolButton; + tbGames: TToolBar; + tbMapAdd: TToolButton; + tbMapDown: TToolButton; + tbMapRemove: TToolButton; + tbMapSep1: TToolButton; + tbMapUp: TToolButton; + ToolBar1: TToolBar; tsAbout: TTabSheet; tsConfiguration: TTabSheet; tsGames: TTabSheet; tsMapList: TTabSheet; tsNetwork: TTabSheet; - vstMapList: TVirtualStringTree; vstGames: TVirtualStringTree; - alMain: TActionList; - actGameAdd: TAction; - actGameRemove: TAction; - lblSuperObject: TLabel; - llSuperObject: TLinkLabel; + vstMapList: TVirtualStringTree; + actLaunch: TAction; + actCopyCmdLine: TAction; + actSave: TAction; + actClose: TAction; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); @@ -102,9 +127,11 @@ type procedure EditChange(Sender: TObject); procedure actGameAddExecute(Sender: TObject); procedure actGameRemoveExecute(Sender: TObject); - procedure vstGamesInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); procedure vstGamesGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); procedure vstGamesFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); + procedure vstMapListGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); + procedure actMapAddExecute(Sender: TObject); + procedure actCloseExecute(Sender: TObject); private type TBindingExpressionList = TList; TPageMenuMap = TDictionary; @@ -121,10 +148,10 @@ type procedure Bind(const APropertyName: string; ADestObject: TObject; const ADestPropertyName: string); procedure BindGameNetwork; procedure BindGameName; - procedure BindGameMapList; - procedure UpdateMenu; + procedure UpdateMenu; procedure UpdateGameList; + procedure UpdateMapList; property ActiveGame: TCustomGame read FActiveGame write SetActiveGame; property PageMenuMap: TPageMenuMap read FPageMenuMap; @@ -148,6 +175,7 @@ uses X2UtGraphics, Forms.Game, + Forms.Map, Game.Chivalry.MedievalWarfare, Game.List, Persist.GameList, @@ -166,9 +194,6 @@ type end; - PCustomGame = ^TCustomGame; - - const INIHintPrefix = 'INI:'; INIHintSeparator = '>'; @@ -177,6 +202,9 @@ const GameColumnName = 0; GameColumnLocation = 1; + MapColumnName = 0; + MapColumnCategory = 1; + {$R *.dfm} @@ -195,6 +223,10 @@ begin FPageMenuMap := TPageMenuMap.Create(pcMain.PageCount); + vstGames.NodeDataSize := SizeOf(TCustomGame); + vstMapList.NodeDataSize := SizeOf(TCustomGame); + + { Configure pages } for pageIndex := 0 to Pred(pcMain.PageCount) do pcMain.Pages[pageIndex].TabVisible := False; @@ -263,7 +295,7 @@ begin BindGameName; if Supports(ActiveGame, IGameMapList) then - BindGameMapList; + UpdateMapList; UpdateMenu; end; @@ -320,12 +352,6 @@ begin end; -procedure TMainForm.BindGameMapList; -begin - // #ToDo1 -oMvR: 30-6-2014: load map list -end; - - procedure TMainForm.UpdateMenu; procedure EnablePageByInterface(APage: TTabSheet; AInterface: TGUID); @@ -345,13 +371,20 @@ end; procedure TMainForm.UpdateGameList; begin - vstGames.NodeDataSize := SizeOf(TCustomGame); vstGames.RootNodeCount := TGameList.Instance.Count; - pnlGamesWarning.Visible := (TGameList.Instance.Count = 0); end; +procedure TMainForm.UpdateMapList; +begin + if Assigned(ActiveGame) then + vstMapList.RootNodeCount := (ActiveGame as IGameMapList).GetMapList.Count + else + vstMapList.Clear; +end; + + procedure TMainForm.SetActiveGame(const Value: TCustomGame); begin if Value <> FActiveGame then @@ -412,20 +445,23 @@ end; procedure TMainForm.actGameRemoveExecute(Sender: TObject); var - nodeData: PCustomGame; + gameIndex: Integer; + game: TCustomGame; begin if not Assigned(vstGames.FocusedNode) then exit; - nodeData := vstGames.GetNodeData(vstGames.FocusedNode); + gameIndex := vstGames.FocusedNode^.Index; + if MessageBox(Self.Handle, 'Do you want to remove the selected game?', 'Remove', MB_YESNO or MB_ICONQUESTION) = ID_YES then begin vstGames.BeginUpdate; try - TGameList.Instance.Remove(nodeData^); + game := TGameList.Instance[gameIndex]; + TGameList.Instance.Delete(gameIndex); - if nodeData^ = ActiveGame then + if game = ActiveGame then begin if TGameList.Instance.Count > 0 then ActiveGame := TGameList.Instance.First @@ -444,29 +480,19 @@ begin end; -procedure TMainForm.vstGamesInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); -var - nodeData: PCustomGame; - -begin - nodeData := Sender.GetNodeData(Node); - nodeData^ := TGameList.Instance[Node^.Index]; -end; - - procedure TMainForm.vstGamesGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); var - nodeData: PCustomGame; + game: TCustomGame; begin - nodeData := Sender.GetNodeData(Node); + game := TGameList.Instance[Node^.Index]; case Column of GameColumnName: - CellText := nodeData^.GameName; + CellText := game.GameName; GameColumnLocation: - CellText := nodeData^.Location; + CellText := game.Location; end; end; @@ -477,6 +503,49 @@ begin end; +procedure TMainForm.vstMapListGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); +var + map: TGameMap; + +begin + if not Assigned(ActiveGame) then + exit; + + map := (ActiveGame as IGameMapList).GetMapList[Node^.Index]; + + case Column of + MapColumnName: + CellText := map.DisplayName; + + MapColumnCategory: + CellText := map.Category; + end; +end; + + +procedure TMainForm.actMapAddExecute(Sender: TObject); +var + mapList: IGameMapList; + map: TGameMap; + +begin + mapList := ActiveGame as IGameMapList; + + if TMapForm.Insert(Self, mapList, map) then + begin + mapList.GetMapList.Add(map); + UpdateMapList; + end; +end; + + +procedure TMainForm.actCloseExecute(Sender: TObject); +begin + Close; +end; + + + { TINIHintWindow } function TINIHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: string; AData: Pointer): TRect; var diff --git a/source/view/Forms.Map.dfm b/source/view/Forms.Map.dfm new file mode 100644 index 0000000..12a6e85 --- /dev/null +++ b/source/view/Forms.Map.dfm @@ -0,0 +1,171 @@ +object MapForm: TMapForm + Left = 0 + Top = 0 + ActiveControl = vstMap + BorderIcons = [biSystemMenu] + BorderStyle = bsDialog + Caption = 'Map' + ClientHeight = 540 + ClientWidth = 577 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + Position = poOwnerFormCenter + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object pnlButtons: TPanel + AlignWithMargins = True + Left = 8 + Top = 507 + Width = 561 + Height = 25 + Margins.Left = 8 + Margins.Top = 0 + Margins.Right = 8 + Margins.Bottom = 8 + Align = alBottom + AutoSize = True + BevelOuter = bvNone + TabOrder = 3 + ExplicitTop = 91 + ExplicitWidth = 503 + object btnCancel: TButton + Left = 486 + Top = 0 + Width = 75 + Height = 25 + Align = alRight + Cancel = True + Caption = 'Cancel' + ModalResult = 2 + TabOrder = 1 + ExplicitLeft = 428 + end + object btnOK: TButton + AlignWithMargins = True + Left = 403 + Top = 0 + Width = 75 + Height = 25 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 8 + Margins.Bottom = 0 + Align = alRight + Caption = 'OK' + Default = True + TabOrder = 0 + OnClick = btnOKClick + ExplicitLeft = 345 + end + end + object vstMap: TVirtualStringTree + AlignWithMargins = True + Left = 8 + Top = 37 + Width = 561 + Height = 433 + Margins.Left = 8 + Margins.Top = 8 + Margins.Right = 8 + Margins.Bottom = 8 + Align = alClient + Header.AutoSizeIndex = 0 + Header.Font.Charset = DEFAULT_CHARSET + Header.Font.Color = clWindowText + Header.Font.Height = -11 + Header.Font.Name = 'Tahoma' + Header.Font.Style = [] + Header.MainColumn = -1 + TabOrder = 1 + TreeOptions.PaintOptions = [toHideFocusRect, toShowDropmark, toShowTreeLines, toThemeAware, toUseBlendedImages] + TreeOptions.SelectionOptions = [toFullRowSelect] + OnCollapsing = vstMapCollapsing + OnCompareNodes = vstMapCompareNodes + OnFocusChanged = vstMapFocusChanged + OnFocusChanging = vstMapFocusChanging + OnGetText = vstMapGetText + OnPaintText = vstMapPaintText + ExplicitTop = 13 + ExplicitHeight = 457 + Columns = <> + end + object pnlMapName: TPanel + AlignWithMargins = True + Left = 8 + Top = 478 + Width = 561 + Height = 21 + Margins.Left = 8 + Margins.Top = 0 + Margins.Right = 8 + Margins.Bottom = 8 + Align = alBottom + AutoSize = True + BevelOuter = bvNone + TabOrder = 2 + ExplicitTop = 468 + ExplicitWidth = 689 + DesignSize = ( + 561 + 21) + object lblMapName: TLabel + Left = 0 + Top = 3 + Width = 53 + Height = 13 + Caption = 'Map name:' + end + object edtMapName: TEdit + Left = 76 + Top = 0 + Width = 485 + Height = 21 + Anchors = [akLeft, akTop, akRight] + TabOrder = 0 + OnChange = edtMapNameChange + ExplicitWidth = 613 + end + end + object pnlFilter: TPanel + AlignWithMargins = True + Left = 8 + Top = 8 + Width = 561 + Height = 21 + Margins.Left = 8 + Margins.Top = 8 + Margins.Right = 8 + Margins.Bottom = 0 + Align = alTop + AutoSize = True + BevelOuter = bvNone + TabOrder = 0 + ExplicitLeft = 12 + ExplicitTop = 482 + DesignSize = ( + 561 + 21) + object lblFilter: TLabel + Left = 0 + Top = 3 + Width = 28 + Height = 13 + Caption = 'Filter:' + end + object edtFilter: TEdit + Left = 76 + Top = 0 + Width = 485 + Height = 21 + Anchors = [akLeft, akTop, akRight] + TabOrder = 0 + OnChange = edtFilterChange + end + end +end diff --git a/source/view/Forms.Map.pas b/source/view/Forms.Map.pas new file mode 100644 index 0000000..7ae18fc --- /dev/null +++ b/source/view/Forms.Map.pas @@ -0,0 +1,360 @@ +unit Forms.Map; + +interface +uses + System.Classes, + Vcl.Controls, + Vcl.ExtCtrls, + Vcl.Forms, + Vcl.Graphics, + Vcl.StdCtrls, + + VirtualTrees, + + Game.Base, + Game.Intf; + + +type + TMapForm = class(TForm) + btnCancel: TButton; + btnOK: TButton; + edtMapName: TEdit; + lblMapName: TLabel; + pnlButtons: TPanel; + pnlMapName: TPanel; + vstMap: TVirtualStringTree; + pnlFilter: TPanel; + lblFilter: TLabel; + edtFilter: TEdit; + + procedure btnOKClick(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure vstMapGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); + procedure vstMapPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); + procedure vstMapFocusChanging(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean); + procedure vstMapFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); + procedure vstMapCollapsing(Sender: TBaseVirtualTree; Node: PVirtualNode; var Allowed: Boolean); + procedure vstMapCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer); + procedure edtMapNameChange(Sender: TObject); + procedure edtFilterChange(Sender: TObject); + private + FLockMapChange: Boolean; + + function GetMapName: string; + procedure SetMapName(const Value: string); + protected + procedure LoadSupportedMapList(AGame: IGameMapList); + function CreateMap: TGameMap; + + function FindMapNode(const AMapName: string): PVirtualNode; + + property MapName: string read GetMapName write SetMapName; + public + class function Insert(AOwner: TComponent; AGame: IGameMapList; out AMap: TGameMap): Boolean; + end; + + +implementation +uses + System.Generics.Collections, + System.StrUtils, + System.SysUtils, + Winapi.Windows; + + +type + PGameMap = ^TGameMap; + + +const + EmptyCategory = 'Other'; + + +{$R *.dfm} + + +{ TMapForm } +class function TMapForm.Insert(AOwner: TComponent; AGame: IGameMapList; out AMap: TGameMap): Boolean; +begin + with Self.Create(AOwner) do + try + LoadSupportedMapList(AGame); + + Result := (ShowModal = mrOk); + if Result then + AMap := CreateMap; + finally + Free; + end; +end; + + +procedure TMapForm.FormCreate(Sender: TObject); +begin + vstMap.NodeDataSize := SizeOf(PGameMap); +end; + + +procedure TMapForm.LoadSupportedMapList(AGame: IGameMapList); +var + map: TGameMap; + categoryNodes: TDictionary; + parentNode: PVirtualNode; + node: PVirtualNode; + +begin + vstMap.BeginUpdate; + try + vstMap.Clear; + + categoryNodes := TDictionary.Create; + try + for map in AGame.GetSupportedMapList do + begin + if categoryNodes.ContainsKey(map.Category) then + parentNode := categoryNodes[map.Category] + else + begin + parentNode := vstMap.AddChild(nil, map); + categoryNodes.Add(map.Category, parentNode); + end; + + vstMap.AddChild(parentNode, map); + end; + finally + FreeAndNil(categoryNodes); + end; + finally + vstMap.FullExpand; + + node := vstMap.GetFirstLevel(1); + if Assigned(node) then + begin + vstMap.FocusedNode := node; + vstMap.Selected[node] := True; + end; + + vstMap.EndUpdate; + end; +end; + + +function TMapForm.CreateMap: TGameMap; +var + node: PVirtualNode; + nodeData: PGameMap; + +begin + node := FindMapNode(MapName); + if Assigned(node) then + begin + nodeData := vstMap.GetNodeData(node); + Result := TGameMap.Create(nodeData^); + end else + Result := TGameMap.Create(MapName, '', ''); +end; + + +function TMapForm.GetMapName: string; +begin + Result := Trim(edtMapName.Text); +end; + + +procedure TMapForm.SetMapName(const Value: string); +begin + edtMapName.Text := Value; +end; + + +function TMapForm.FindMapNode(const AMapName: string): PVirtualNode; +begin + Result := vstMap.IterateSubtree(nil, + procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean) + var + nodeData: PGameMap; + + begin + nodeData := Sender.GetNodeData(Node); + Abort := SameText(nodeData^.Name, AMapName); + end, + nil); +end; + + +procedure TMapForm.btnOKClick(Sender: TObject); +begin + if Length(MapName) = 0 then + begin + MessageBox(Self.Handle, 'Please enter a map name', 'Error', MB_OK or MB_ICONERROR); + ActiveControl := edtMapName; + exit; + end; + + ModalResult := mrOk; +end; + + +procedure TMapForm.vstMapGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); +var + nodeData: PGameMap; + +begin + nodeData := Sender.GetNodeData(Node); + + if Sender.GetNodeLevel(Node) = 0 then + begin + if Length(nodeData^.Category) > 0 then + CellText := nodeData^.Category + else + CellText := EmptyCategory; + end else + CellText := nodeData^.DisplayName; +end; + + +procedure TMapForm.vstMapPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); +begin + if (TextType = ttNormal) and (Sender.GetNodeLevel(Node) = 0) then + TargetCanvas.Font.Style := [fsBold]; +end; + + +procedure TMapForm.vstMapFocusChanging(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean); +var + sibling: PVirtualNode; + +begin + Allowed := True; + + if not Assigned(NewNode) then + exit; + + { Prevent selection of categories while being keyboard-friendly } + if Sender.GetNodeLevel(NewNode) = 0 then + begin + if Assigned(OldNode) then + begin + if Sender.AbsoluteIndex(OldNode) < Sender.AbsoluteIndex(NewNode) then + begin + { Moving forwards } + Sender.FocusedNode := Sender.GetFirstChild(NewNode); + end else + begin + { Moving backwards } + sibling := Sender.GetPreviousSibling(NewNode); + if Assigned(sibling) then + Sender.FocusedNode := Sender.GetLastChild(sibling); + end; + end; + + Allowed := False; + end; +end; + + +procedure TMapForm.vstMapFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); +var + nodeData: PGameMap; + +begin + if Assigned(Node) then + begin + nodeData := Sender.GetNodeData(Node); + + FLockMapChange := True; + try + edtMapName.Text := nodeData^.Name; + finally + FLockMapChange := False; + end; + end; +end; + + +procedure TMapForm.vstMapCollapsing(Sender: TBaseVirtualTree; Node: PVirtualNode; var Allowed: Boolean); +begin + Allowed := False; +end; + + +procedure TMapForm.vstMapCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer); +var + nodeData1: PGameMap; + nodeData2: PGameMap; + +begin + nodeData1 := Sender.GetNodeData(Node1); + nodeData2 := Sender.GetNodeData(Node2); + + if Sender.GetNodeLevel(Node1) = 0 then + Result := CompareText(nodeData1^.Category, nodeData2^.Category) + else + Result := CompareText(nodeData1^.DisplayName, nodeData2^.DisplayName); +end; + + +procedure TMapForm.edtFilterChange(Sender: TObject); +var + node: PVirtualNode; + filtered: Boolean; + childNode: PVirtualNode; + +begin + vstMap.BeginUpdate; + try + { First run; set visibility of map nodes } + vstMap.IterateSubtree(nil, + procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean) + var + nodeData: PGameMap; + + begin + if Sender.GetNodeLevel(Node) > 0 then + begin + nodeData := Sender.GetNodeData(Node); + Sender.IsFiltered[Node] := (Length(edtFilter.Text) > 0) and (not ContainsText(nodeData^.DisplayName, edtFilter.Text)); + end; + end, + nil); + + { Second run; hide empty categories } + node := vstMap.GetFirst; + while Assigned(node) do + begin + vstMap.IsFiltered[Node] := not Assigned(vstMap.IterateSubtree(node, + procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean) + begin + Abort := not Sender.IsFiltered[Node]; + end, + nil, [], False, True)); + + node := vstMap.GetNextSibling(node); + end; + finally + vstMap.EndUpdate; + end; +end; + + +procedure TMapForm.edtMapNameChange(Sender: TObject); +var + node: PVirtualNode; + +begin + if FLockMapChange then + exit; + + node := FindMapNode(MapName); + vstMap.FocusedNode := node; + + if Assigned(node) then + begin + vstMap.Selected[node] := True; + vstMap.ScrollIntoView(node, True); + end else + vstMap.ClearSelection; +end; + +end.