diff --git a/CHANGELOG b/CHANGELOG index 7c658c3056..b1b923a5bd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,10 @@ NEW: Improved the ingame chat interface and input, with it defaulting to Team Chat. Redesigned the settings panel. Re-added move flashes. + Asset Browser: + Fixed crashes when trying to load invalid filenames or sprites with just 1 frame. + Added support for browsing the folders for R8 files. + Added palette chooser and colorpicker dropdown boxes. Red Alert: Added MAD Tank. Fixed a crash in Monster Tank Madness. diff --git a/OpenRA.Mods.RA/PaletteFromFile.cs b/OpenRA.Mods.RA/PaletteFromFile.cs index fc1642eda7..49694df320 100644 --- a/OpenRA.Mods.RA/PaletteFromFile.cs +++ b/OpenRA.Mods.RA/PaletteFromFile.cs @@ -44,5 +44,15 @@ namespace OpenRA.Mods.RA if (info.Tileset == null || info.Tileset.ToLowerInvariant() == world.Map.Tileset.ToLowerInvariant()) wr.AddPalette(info.Name, new Palette(FileSystem.Open(info.Filename), info.ShadowIndex), info.AllowModifiers); } + + public string Filename + { + get { return info.Filename; } + } + + public string Name + { + get { return info.Name; } + } } } diff --git a/OpenRA.Mods.RA/Widgets/Logic/AssetBrowserLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/AssetBrowserLogic.cs index 3499d592f3..2c7adaaad3 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/AssetBrowserLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/AssetBrowserLogic.cs @@ -10,9 +10,11 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using OpenRA.FileFormats; +using OpenRA.Traits; using OpenRA.Widgets; namespace OpenRA.Mods.RA.Widgets.Logic @@ -21,15 +23,17 @@ namespace OpenRA.Mods.RA.Widgets.Logic { Widget panel; - ShpImageWidget spriteImage; + ShpImageWidget spriteWidget; TextFieldWidget filenameInput; SliderWidget frameSlider; ButtonWidget playButton, pauseButton; ScrollPanelWidget assetList; ScrollItemWidget template; - IFolder AssetSource = null; - List AvailableShps = new List(); + IFolder assetSource = null; + List availableShps = new List(); + + PaletteFromFile currentPalette; [ObjectCreator.UseCtor] public AssetBrowserLogic(Widget widget, Action onExit, World world) @@ -40,55 +44,70 @@ namespace OpenRA.Mods.RA.Widgets.Logic sourceDropdown.OnMouseDown = _ => ShowSourceDropdown(sourceDropdown); sourceDropdown.GetText = () => { - var name = AssetSource != null ? AssetSource.Name : "All Packages"; + var name = assetSource != null ? assetSource.Name.Replace(Platform.SupportDir, "^") : "All Packages"; + if (name.Length > 15) - name = "..."+name.Substring(name.Length - 15); + name = "..." + name.Substring(name.Length - 15); return name; }; - AssetSource = FileSystem.MountedFolders.First(); + assetSource = FileSystem.MountedFolders.First(); - spriteImage = panel.Get("SPRITE"); + spriteWidget = panel.Get("SPRITE"); + + currentPalette = world.WorldActor.TraitsImplementing().First(p => p.Name == spriteWidget.Palette); + + var paletteDropDown = panel.Get("PALETTE_SELECTOR"); + paletteDropDown.OnMouseDown = _ => ShowPaletteDropdown(paletteDropDown, world); + + var colorPreview = panel.Get("COLOR_MANAGER"); + colorPreview.Color = Game.Settings.Player.Color; + + var color = panel.Get("COLOR"); + color.IsDisabled = () => currentPalette.Name != colorPreview.Palette; + color.OnMouseDown = _ => ShowColorDropDown(color, colorPreview, world); + var block = panel.Get("COLORBLOCK"); + block.GetColor = () => Game.Settings.Player.Color.RGB; filenameInput = panel.Get("FILENAME_INPUT"); - filenameInput.Text = spriteImage.Image+".shp"; filenameInput.OnEnterKey = () => LoadAsset(filenameInput.Text); frameSlider = panel.Get("FRAME_SLIDER"); - frameSlider.MaximumValue = (float)spriteImage.FrameCount; - frameSlider.Ticks = spriteImage.FrameCount+1; - frameSlider.OnChange += x => { spriteImage.Frame = (int)Math.Round(x); }; - frameSlider.GetValue = () => spriteImage.Frame; + frameSlider.MaximumValue = (float)spriteWidget.FrameCount; + frameSlider.Ticks = spriteWidget.FrameCount + 1; + frameSlider.IsVisible = () => spriteWidget.FrameCount > 0; + frameSlider.OnChange += x => { spriteWidget.Frame = (int)Math.Round(x); }; + frameSlider.GetValue = () => spriteWidget.Frame; - panel.Get("FRAME_COUNT").GetText = () => "{0}/{1}".F(spriteImage.Frame, spriteImage.FrameCount); + panel.Get("FRAME_COUNT").GetText = () => "{0}/{1}".F(spriteWidget.Frame, spriteWidget.FrameCount); playButton = panel.Get("BUTTON_PLAY"); playButton.OnClick = () => { - spriteImage.LoopAnimation = true; + spriteWidget.LoopAnimation = true; playButton.Visible = false; pauseButton.Visible = true; }; pauseButton = panel.Get("BUTTON_PAUSE"); pauseButton.OnClick = () => { - spriteImage.LoopAnimation = false; + spriteWidget.LoopAnimation = false; playButton.Visible = true; pauseButton.Visible = false; }; panel.Get("BUTTON_STOP").OnClick = () => { - spriteImage.LoopAnimation = false; + spriteWidget.LoopAnimation = false; frameSlider.Value = 0; - spriteImage.Frame = 0; + spriteWidget.Frame = 0; playButton.Visible = true; pauseButton.Visible = false; }; - panel.Get("BUTTON_NEXT").OnClick = () => { spriteImage.RenderNextFrame(); }; - panel.Get("BUTTON_PREV").OnClick = () => { spriteImage.RenderPreviousFrame(); }; + panel.Get("BUTTON_NEXT").OnClick = () => { spriteWidget.RenderNextFrame(); }; + panel.Get("BUTTON_PREV").OnClick = () => { spriteWidget.RenderPreviousFrame(); }; panel.Get("LOAD_BUTTON").OnClick = () => { @@ -99,21 +118,21 @@ namespace OpenRA.Mods.RA.Widgets.Logic template = panel.Get("ASSET_TEMPLATE"); PopulateAssetList(); - // TODO: Horrible hack var modID = Game.modData.Manifest.Mod.Id; - var palette = (modID == "d2k") ? "d2k.pal" : "egopal.pal"; + // TODO: This should not invoke the OpenRA.Utility.exe, but use it's functions directly. + // TODO: Does not work with SHP(TS) yet?! panel.Get("EXPORT_BUTTON").OnClick = () => { var ExtractGameFiles = new string[][] { - new string[] {"--extract", modID, palette, "--userdir"}, - new string[] {"--extract", modID, "{0}.shp".F(spriteImage.Image), "--userdir"}, + new string[] { "--extract", modID, currentPalette.Filename, "--userdir" }, + new string[] { "--extract", modID, "{0}.shp".F(spriteWidget.Image), "--userdir" }, }; - + var ExportToPng = new string[][] { - new string[] {"--png", Platform.SupportDir+"{0}.shp".F(spriteImage.Image), Platform.SupportDir+palette}, + new string[] { "--png", Platform.SupportDir + "{0}.shp".F(spriteWidget.Image), Platform.SupportDir + currentPalette.Filename }, }; var ImportFromPng = new string[][] { }; @@ -122,9 +141,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic { { "ExtractGameFiles", ExtractGameFiles }, { "ExportToPng", ExportToPng }, - { "ImportFromPng", ImportFromPng} + { "ImportFromPng", ImportFromPng } }; - + Ui.OpenWindow("CONVERT_ASSETS_PANEL", args); }; @@ -133,13 +152,13 @@ namespace OpenRA.Mods.RA.Widgets.Logic var ExtractGameFilesList = new List(); var ExportToPngList = new List(); - ExtractGameFilesList.Add(new string[] { "--extract", modID, palette, "--userdir"} ); + ExtractGameFilesList.Add(new string[] { "--extract", modID, currentPalette.Filename, "--userdir" }); - foreach (var shp in AvailableShps) + foreach (var shp in availableShps) { - ExtractGameFilesList.Add(new string[] { "--extract", modID, shp, "--userdir" } ); - ExportToPngList.Add(new string[] { "--png", Platform.SupportDir+shp, Platform.SupportDir+palette } ); - Console.WriteLine(Platform.SupportDir+shp); + ExtractGameFilesList.Add(new string[] { "--extract", modID, shp, "--userdir" }); + ExportToPngList.Add(new string[] { "--png", Platform.SupportDir + shp, Platform.SupportDir + currentPalette.Filename }); + Console.WriteLine(Platform.SupportDir + shp); } var ExtractGameFiles = ExtractGameFilesList.ToArray(); @@ -152,11 +171,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic { "ExportToPng", ExportToPng }, { "ImportFromPng", ImportFromPng } }; - + Ui.OpenWindow("CONVERT_ASSETS_PANEL", args); }; - panel.Get("IMPORT_BUTTON").OnClick = () => { var imageSizeInput = panel.Get("IMAGE_SIZE_INPUT"); @@ -166,16 +184,16 @@ namespace OpenRA.Mods.RA.Widgets.Logic var ExportToPng = new string[][] { }; var ImportFromPng = new string[][] { - new string[] {"--shp", Platform.SupportDir+imageFilename.Text, imageSizeInput.Text}, + new string[] { "--shp", Platform.SupportDir + imageFilename.Text, imageSizeInput.Text }, }; var args = new WidgetArgs() { { "ExtractGameFiles", ExtractGameFiles }, { "ExportToPng", ExportToPng }, - { "ImportFromPng", ImportFromPng} + { "ImportFromPng", ImportFromPng } }; - + Ui.OpenWindow("CONVERT_ASSETS_PANEL", args); }; @@ -184,24 +202,32 @@ namespace OpenRA.Mods.RA.Widgets.Logic void AddAsset(ScrollPanelWidget list, string filepath, ScrollItemWidget template) { - var sprite = Path.GetFileNameWithoutExtension(filepath); + var r8 = filepath.EndsWith(".r8", true, CultureInfo.InvariantCulture); + var filename = Path.GetFileName(filepath); + var sprite = r8 ? filename : Path.GetFileNameWithoutExtension(filepath); var item = ScrollItemWidget.Setup(template, - () => spriteImage.Image == sprite, - () => LoadAsset(sprite)); + () => spriteWidget.Image == sprite, + () => {filenameInput.Text = filename; LoadAsset(filename); }); item.Get("TITLE").GetText = () => filepath; list.AddChild(item); } - bool LoadAsset(string sprite) + bool LoadAsset(string filename) { - if (sprite == null) + if (string.IsNullOrEmpty(filename)) return false; - spriteImage.Frame = 0; - spriteImage.Image = sprite; - frameSlider.MaximumValue = (float)spriteImage.FrameCount; - frameSlider.Ticks = spriteImage.FrameCount+1; + if (!FileSystem.Exists(filename)) + return false; + + var r8 = filename.EndsWith(".r8", true, CultureInfo.InvariantCulture); + var sprite = r8 ? filename : Path.GetFileNameWithoutExtension(filename); + + spriteWidget.Frame = 0; + spriteWidget.Image = sprite; + frameSlider.MaximumValue = (float)spriteWidget.FrameCount; + frameSlider.Ticks = spriteWidget.FrameCount + 1; return true; } @@ -210,41 +236,78 @@ namespace OpenRA.Mods.RA.Widgets.Logic Func setupItem = (source, itemTemplate) => { var item = ScrollItemWidget.Setup(itemTemplate, - () => AssetSource == source, - () => { AssetSource = source; PopulateAssetList(); }); - item.Get("LABEL").GetText = () => source != null ? source.Name : "All Packages"; + () => assetSource == source, + () => { assetSource = source; PopulateAssetList(); }); + item.Get("LABEL").GetText = () => source != null ? source.Name.Replace(Platform.SupportDir, "^") : "All Packages"; return item; }; // TODO: Re-enable "All Packages" once list generation is done in a background thread - //var sources = new[] { (IFolder)null }.Concat(FileSystem.MountedFolders); + // var sources = new[] { (IFolder)null }.Concat(FileSystem.MountedFolders); var sources = FileSystem.MountedFolders; - dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 250, sources, setupItem); + dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 280, sources, setupItem); return true; } void PopulateAssetList() { assetList.RemoveChildren(); - AvailableShps.Clear(); + availableShps.Clear(); // TODO: This is too slow to run in the main thread - //var files = AssetSource != null ? AssetSource.AllFileNames() : - // FileSystem.MountedFolders.SelectMany(f => f.AllFileNames()); + // var files = AssetSource != null ? AssetSource.AllFileNames() : + // FileSystem.MountedFolders.SelectMany(f => f.AllFileNames()); - if (AssetSource == null) + if (assetSource == null) return; - var files = AssetSource.AllFileNames(); + var files = assetSource.AllFileNames(); foreach (var file in files) { - if (file.EndsWith(".shp")) + if (file.EndsWith(".shp", true, CultureInfo.InvariantCulture) || file.EndsWith(".r8", true, CultureInfo.InvariantCulture)) { AddAsset(assetList, file, template); - AvailableShps.Add(file); + availableShps.Add(file); } } } + + bool ShowPaletteDropdown(DropDownButtonWidget dropdown, World world) + { + Func setupItem = (palette, itemTemplate) => + { + var item = ScrollItemWidget.Setup(itemTemplate, + () => currentPalette.Name == palette.Name, + () => { currentPalette = palette; spriteWidget.Palette = currentPalette.Name; }); + item.Get("LABEL").GetText = () => palette.Name; + return item; + }; + + var palettes = world.WorldActor.TraitsImplementing(); + dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 280, palettes, setupItem); + return true; + } + + void ShowColorDropDown(DropDownButtonWidget color, ColorPreviewManagerWidget preview, World world) + { + Action onExit = () => + { + Game.Settings.Player.Color = preview.Color; + Game.Settings.Save(); + }; + + color.RemovePanel(); + + Action onChange = c => preview.Color = c; + + var colorChooser = Game.LoadWidget(world, "COLOR_CHOOSER", null, new WidgetArgs() + { + { "onChange", onChange }, + { "initialColor", Game.Settings.Player.Color } + }); + + color.AttachPanel(colorChooser, onExit); + } } } diff --git a/mods/d2k/chrome/assetbrowser.yaml b/mods/d2k/chrome/assetbrowser.yaml index 65289f5767..270cdec6cd 100644 --- a/mods/d2k/chrome/assetbrowser.yaml +++ b/mods/d2k/chrome/assetbrowser.yaml @@ -5,9 +5,10 @@ Background@ASSETBROWSER_BG: Width:700 Height:410 Children: + ColorPreviewManager@COLOR_MANAGER: Label@ASSETBROWSER_TITLE: X:0 - Y:20 + Y:10 Width:PARENT_RIGHT Height:25 Text:Game Asset Viewer & Converter @@ -42,6 +43,7 @@ Background@ASSETBROWSER_BG: Y:280 Width:140 Height:25 + Text:mouse.r8 Button@LOAD_BUTTON: X:40 Y:310 @@ -50,13 +52,24 @@ Background@ASSETBROWSER_BG: Text:Load Font:Bold Key:return - Label@PREVIEW_TITLE: - X:320 + DropDownButton@PALETTE_SELECTOR: + X:230 Y:45 - Width:PARENT_RIGHT + Width:150 Height:25 - Text:Preview Font:Bold + Text:Palette + DropDownButton@COLOR: + X:380 + Y:45 + Width:80 + Height:25 + Children: + ColorBlock@COLORBLOCK: + X:5 + Y:6 + Width:PARENT_RIGHT-35 + Height:PARENT_BOTTOM-12 Background@SPRITE_BG: X:220 Y:80 @@ -69,8 +82,7 @@ Background@ASSETBROWSER_BG: Y:4 Width:246 Height:246 - Image:DATA.R8 - Palette:colorpicker + Image:mouse.r8 Label@ACTIONS_TITLE: X:PARENT_RIGHT - 150 Y:45 diff --git a/mods/ra/chrome/assetbrowser.yaml b/mods/ra/chrome/assetbrowser.yaml index fb77ec748c..bf313b1c9b 100644 --- a/mods/ra/chrome/assetbrowser.yaml +++ b/mods/ra/chrome/assetbrowser.yaml @@ -5,9 +5,10 @@ Background@ASSETBROWSER_BG: Width:700 Height:410 Children: + ColorPreviewManager@COLOR_MANAGER: Label@ASSETBROWSER_TITLE: X:0 - Y:20 + Y:10 Width:PARENT_RIGHT Height:25 Text:Game Asset Viewer & Converter @@ -42,6 +43,7 @@ Background@ASSETBROWSER_BG: Y:280 Width:140 Height:25 + Text:mouse.shp Button@LOAD_BUTTON: X:40 Y:310 @@ -50,13 +52,24 @@ Background@ASSETBROWSER_BG: Text:Load Font:Bold Key:return - Label@PREVIEW_TITLE: - X:320 + DropDownButton@PALETTE_SELECTOR: + X:230 Y:45 - Width:PARENT_RIGHT + Width:150 Height:25 - Text:Preview Font:Bold + Text:Palette + DropDownButton@COLOR: + X:380 + Y:45 + Width:80 + Height:25 + Children: + ColorBlock@COLORBLOCK: + X:5 + Y:6 + Width:PARENT_RIGHT-35 + Height:PARENT_BOTTOM-12 Background@SPRITE_BG: X:220 Y:80 @@ -70,7 +83,6 @@ Background@ASSETBROWSER_BG: Width:246 Height:246 Image:mouse - Palette:colorpicker Label@ACTIONS_TITLE: X:PARENT_RIGHT - 150 Y:45