diff --git a/OpenRA.FileFormats/Filesystem/FileSystem.cs b/OpenRA.FileFormats/Filesystem/FileSystem.cs index 396b719763..c2e1004c45 100644 --- a/OpenRA.FileFormats/Filesystem/FileSystem.cs +++ b/OpenRA.FileFormats/Filesystem/FileSystem.cs @@ -18,19 +18,21 @@ namespace OpenRA.FileFormats { public static class FileSystem { - static List mountedFolders = new List(); + static List MountedFolders = new List(); static Cache> allFiles = new Cache>( _ => new List() ); + public static List FolderPaths = new List(); + static void MountInner(IFolder folder) { - mountedFolders.Add(folder); + MountedFolders.Add(folder); - foreach( var hash in folder.AllFileHashes() ) + foreach (var hash in folder.AllFileHashes()) { var l = allFiles[hash]; - if( !l.Contains( folder ) ) - l.Add( folder ); + if (!l.Contains(folder)) + l.Add(folder); } } @@ -78,6 +80,9 @@ namespace OpenRA.FileFormats if (name.StartsWith("^")) name = Platform.SupportDir+name.Substring(1); + if (Directory.Exists(name)) + FolderPaths.Add(name); + var a = (Action)(() => FileSystem.MountInner(OpenPackage(name))); if (optional) @@ -89,28 +94,29 @@ namespace OpenRA.FileFormats public static void UnmountAll() { - mountedFolders.Clear(); + MountedFolders.Clear(); + FolderPaths.Clear(); allFiles = new Cache>( _ => new List() ); } public static bool Unmount(IFolder mount) { - return (mountedFolders.RemoveAll(f => f == mount) > 0); + return (MountedFolders.RemoveAll(f => f == mount) > 0); } public static void Mount(IFolder mount) { - if (!mountedFolders.Contains(mount)) mountedFolders.Add(mount); + if (!MountedFolders.Contains(mount)) MountedFolders.Add(mount); } - public static void LoadFromManifest( Manifest manifest ) + public static void LoadFromManifest(Manifest manifest) { UnmountAll(); foreach (var dir in manifest.Folders) Mount(dir); foreach (var pkg in manifest.Packages) Mount(pkg); } - static Stream GetFromCache( Cache> index, string filename ) + static Stream GetFromCache(Cache> index, string filename) { var folder = index[PackageEntry.HashFilename(filename)] .Where(x => x.Exists(filename)) @@ -125,21 +131,21 @@ namespace OpenRA.FileFormats public static Stream Open(string filename) { return OpenWithExts(filename, ""); } - public static Stream OpenWithExts( string filename, params string[] exts ) + public static Stream OpenWithExts(string filename, params string[] exts) { if( filename.IndexOfAny( new char[] { '/', '\\' } ) == -1 ) { foreach( var ext in exts ) { - var s = GetFromCache( allFiles, filename + ext ); - if( s != null ) + var s = GetFromCache(allFiles, filename + ext); + if (s != null) return s; } } - foreach( var ext in exts ) + foreach (var ext in exts) { - var folder = mountedFolders + var folder = MountedFolders .Where(x => x.Exists(filename + ext)) .OrderByDescending(x => x.Priority) .FirstOrDefault(); @@ -151,7 +157,7 @@ namespace OpenRA.FileFormats throw new FileNotFoundException("File not found: {0}".F(filename), filename); } - public static bool Exists(string filename) { return mountedFolders.Any(f => f.Exists(filename)); } + public static bool Exists(string filename) { return MountedFolders.Any(f => f.Exists(filename)); } static Dictionary assemblyCache = new Dictionary(); diff --git a/OpenRA.Game/Graphics/SpriteLoader.cs b/OpenRA.Game/Graphics/SpriteLoader.cs index 97134fc646..ca6e7f49b9 100644 --- a/OpenRA.Game/Graphics/SpriteLoader.cs +++ b/OpenRA.Game/Graphics/SpriteLoader.cs @@ -16,11 +16,11 @@ namespace OpenRA.Graphics { public class SpriteLoader { - public SpriteLoader( string[] exts, SheetBuilder sheetBuilder ) + public SpriteLoader(string[] exts, SheetBuilder sheetBuilder) { SheetBuilder = sheetBuilder; this.exts = exts; - sprites = new Cache( LoadSprites ); + sprites = new Cache(LoadSprites); } readonly SheetBuilder SheetBuilder; diff --git a/OpenRA.Game/Widgets/ShpImageWidget.cs b/OpenRA.Game/Widgets/ShpImageWidget.cs index 88388f1aca..7198598ccb 100644 --- a/OpenRA.Game/Widgets/ShpImageWidget.cs +++ b/OpenRA.Game/Widgets/ShpImageWidget.cs @@ -18,6 +18,7 @@ namespace OpenRA.Widgets public string Image = ""; public int Frame = 0; public string Palette = "chrome"; + public bool LoopAnimation = false; public Func GetImage; public Func GetFrame; @@ -26,12 +27,13 @@ namespace OpenRA.Widgets readonly WorldRenderer worldRenderer; [ObjectCreator.UseCtor] - public ShpImageWidget( WorldRenderer worldRenderer) + public ShpImageWidget(WorldRenderer worldRenderer) : base() { GetImage = () => { return Image; }; GetFrame = () => { return Frame; }; GetPalette = () => { return Palette; }; + this.worldRenderer = worldRenderer; } @@ -41,9 +43,12 @@ namespace OpenRA.Widgets Image = other.Image; Frame = other.Frame; Palette = other.Palette; + LoopAnimation = other.LoopAnimation; + GetImage = other.GetImage; GetFrame = other.GetFrame; GetPalette = other.GetPalette; + worldRenderer = other.worldRenderer; } @@ -68,5 +73,32 @@ namespace OpenRA.Widgets Game.Renderer.SpriteRenderer.DrawSprite(sprite, RenderOrigin, worldRenderer, palette); } + + public int FrameCount + { + get { return Game.modData.SpriteLoader.LoadAllSprites(Image).Length-1; } + } + + public void RenderNextFrame() + { + if (Frame < FrameCount) + Frame++; + else + Frame = 0; + } + + public void RenderPreviousFrame() + { + if (Frame > 0) + Frame--; + else + Frame = FrameCount; + } + + public override void Tick() + { + if (LoopAnimation) + RenderNextFrame(); + } } } diff --git a/OpenRA.Game/Widgets/SliderWidget.cs b/OpenRA.Game/Widgets/SliderWidget.cs index 545cfd161f..d172914f8f 100755 --- a/OpenRA.Game/Widgets/SliderWidget.cs +++ b/OpenRA.Game/Widgets/SliderWidget.cs @@ -24,10 +24,15 @@ namespace OpenRA.Widgets public float MinimumValue = 0; public float MaximumValue = 1; public float Value = 0; + public Func GetValue; protected bool isMoving = false; - public SliderWidget() : base() {} + public SliderWidget() + : base() + { + GetValue = () => Value; + } public SliderWidget(SliderWidget other) : base(other) @@ -38,6 +43,7 @@ namespace OpenRA.Widgets MaximumValue = other.MaximumValue; Value = other.Value; TrackHeight = other.TrackHeight; + GetValue = other.GetValue; } void UpdateValue(float newValue) @@ -53,7 +59,7 @@ namespace OpenRA.Widgets if (mi.Event == MouseInputEvent.Down && !TakeFocus(mi)) return false; if (!Focused) return false; - switch( mi.Event ) + switch(mi.Event) { case MouseInputEvent.Up: isMoving = false; @@ -99,6 +105,8 @@ namespace OpenRA.Widgets if (!IsVisible()) return; + Value = GetValue(); + var tr = ThumbRect; var rb = RenderBounds; var trackWidth = rb.Width; diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index f1ef383574..9bcf55ea19 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -436,6 +436,7 @@ + diff --git a/OpenRA.Mods.RA/Widgets/Logic/AssetBrowserLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/AssetBrowserLogic.cs new file mode 100644 index 0000000000..7bd85985b2 --- /dev/null +++ b/OpenRA.Mods.RA/Widgets/Logic/AssetBrowserLogic.cs @@ -0,0 +1,124 @@ +#region Copyright & License Information +/* + * Copyright 2007-2013 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.IO; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.GameRules; +using OpenRA.Graphics; +using OpenRA.Widgets; + +namespace OpenRA.Mods.RA.Widgets.Logic +{ + public class AssetBrowserLogic + { + Widget panel; + + ShpImageWidget spriteImage; + TextFieldWidget filenameInput; + SliderWidget frameSlider; + ButtonWidget playButton; + ButtonWidget pauseButton; + + [ObjectCreator.UseCtor] + public AssetBrowserLogic(Widget widget, Action onExit, World world) + { + panel = widget; + + spriteImage = panel.Get("SPRITE"); + + filenameInput = panel.Get("FILENAME_INPUT"); + filenameInput.Text = spriteImage.Image; + filenameInput.OnEnterKey = () => LoadAsset(filenameInput.Text); + + var assetList = panel.Get("ASSET_LIST"); + var template = panel.Get("ASSET_TEMPLATE"); + + assetList.RemoveChildren(); + foreach (var folder in FileSystem.FolderPaths) + { + if (Directory.Exists(folder)) + { + var shps = Directory.GetFiles(folder, "*.shp"); + foreach (var shp in shps) + AddAsset(assetList, shp, template); + } + } + + 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; + + panel.Get("FRAME_COUNT").GetText = () => spriteImage.Frame.ToString(); + + playButton = panel.Get("BUTTON_PLAY"); + playButton.OnClick = () => + { + spriteImage.LoopAnimation = true; + playButton.Visible = false; + pauseButton.Visible = true; + }; + pauseButton = panel.Get("BUTTON_PAUSE"); + pauseButton.OnClick = () => + { + spriteImage.LoopAnimation = false; + playButton.Visible = true; + pauseButton.Visible = false; + }; + + panel.Get("BUTTON_STOP").OnClick = () => + { + spriteImage.LoopAnimation = false; + frameSlider.Value = 0; + spriteImage.Frame = 0; + playButton.Visible = true; + pauseButton.Visible = false; + }; + + panel.Get("BUTTON_NEXT").OnClick = () => { spriteImage.RenderNextFrame(); }; + panel.Get("BUTTON_PREV").OnClick = () => { spriteImage.RenderPreviousFrame(); }; + + panel.Get("LOAD_BUTTON").OnClick = () => + { + LoadAsset(filenameInput.Text); + }; + + panel.Get("CLOSE_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); }; + } + + void AddAsset(ScrollPanelWidget list, string filepath, ScrollItemWidget template) + { + var sprite = Path.GetFileNameWithoutExtension(filepath); + + var item = ScrollItemWidget.Setup(template, + () => spriteImage != null && spriteImage.Image == sprite, + () => LoadAsset(sprite)); + item.Get("TITLE").GetText = () => sprite; + + list.AddChild(item); + } + + bool LoadAsset(string filename) + { + if (filename == null) + return false; + + filenameInput.Text = filename; + spriteImage.Frame = 0; + spriteImage.Image = filename; + frameSlider.MaximumValue = (float)spriteImage.FrameCount; + frameSlider.Ticks = spriteImage.FrameCount+1; + return true; + } + } +} diff --git a/OpenRA.Mods.RA/Widgets/Logic/MainMenuButtonsLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/MainMenuButtonsLogic.cs index 46f713596b..2ad69c8871 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/MainMenuButtonsLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/MainMenuButtonsLogic.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, @@ -14,7 +14,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic { public class MainMenuButtonsLogic { - enum MenuType { Main, None } MenuType Menu = MenuType.Main; @@ -73,6 +72,15 @@ namespace OpenRA.Mods.RA.Widgets.Logic }); }; + widget.Get("MAINMENU_BUTTON_ASSET_BROWSER").OnClick = () => + { + Menu = MenuType.None; + Game.OpenWindow("ASSETBROWSER_BG", new WidgetArgs() + { + { "onExit", () => Menu = MenuType.Main } + }); + }; + widget.Get("MAINMENU_BUTTON_QUIT").OnClick = () => Game.Exit(); } diff --git a/mods/ra/chrome/assetbrowser.yaml b/mods/ra/chrome/assetbrowser.yaml new file mode 100644 index 0000000000..21c62fbbd6 --- /dev/null +++ b/mods/ra/chrome/assetbrowser.yaml @@ -0,0 +1,196 @@ +Background@ASSETBROWSER_BG: + Logic:AssetBrowserLogic + X:(WINDOW_RIGHT - WIDTH)/2 + Y:(WINDOW_BOTTOM - HEIGHT)/2 + Width:700 + Height:410 + Children: + Label@ASSETBROWSER_TITLE: + X:0 + Y:20 + Width:PARENT_RIGHT + Height:25 + Text:Game Asset Viewer & Converter + Align:Center + Font:Bold + DropDownButton@SOURCE_SELECTOR: + X:40 + Y:45 + Width:160 + Height:25 + Font:Bold + Text:Folders + ScrollPanel@ASSET_LIST: + X:40 + Y:80 + Width:160 + Height:190 + Children: + ScrollItem@ASSET_TEMPLATE: + Width:PARENT_RIGHT-27 + Height:25 + X:2 + Y:0 + Visible:false + Children: + Label@TITLE: + X:10 + Width:PARENT_RIGHT-20 + Height:25 + TextField@FILENAME_INPUT: + X:40 + Y:280 + Width:140 + Height:25 + Button@LOAD_BUTTON: + X:40 + Y:310 + Width:140 + Height:25 + Text:Load + Font:Bold + Key:return + Label@PREVIEW_TITLE: + X:320 + Y:45 + Width:PARENT_RIGHT + Height:25 + Text:Preview + Background@SPRITE_BG: + X:220 + Y:80 + Width:250 + Height:250 + Background:dialog3 + Children: + ShpImage@SPRITE: + X:80 + Y:80 + Width:246 + Height:246 + Image:fact + Palette:player + Label@ACTIONS_TITLE: + X:PARENT_RIGHT - 150 + Y:45 + Width:PARENT_RIGHT + Height:25 + Text:Actions + Button@REMAP_BUTTON: + X:PARENT_RIGHT - 200 + Y:PARENT_BOTTOM - 305 + Width:160 + Height:25 + Text:Change Palette + Font:Bold + Button@IMPORT_BUTTON: + X:PARENT_RIGHT - 200 + Y:PARENT_BOTTOM - 270 + Width:160 + Height:25 + Text:Import from PNG + Font:Bold + Button@EXTRACT_BUTTON: + X:PARENT_RIGHT - 200 + Y:PARENT_BOTTOM - 235 + Width:160 + Height:25 + Text:Extract to Folder + Font:Bold + Button@EXPORT_BUTTON: + X:PARENT_RIGHT - 200 + Y:PARENT_BOTTOM - 200 + Width:160 + Height:25 + Text:Export as PNG + Font:Bold + Button@CLOSE_BUTTON: + X:PARENT_RIGHT - 200 + Y:PARENT_BOTTOM - 115 + Width:160 + Height:25 + Text:Close + Font:Bold + Key:escape + Container@FRAME_SELECTOR: + X:45 + Y:360 + Children: + Button@BUTTON_PREV: + X:0 + Y:0 + Width:25 + Height:25 + Children: + Image@IMAGE_PREV: + X:0 + Y:0 + Width:25 + Height:25 + ImageCollection:music + ImageName:prev + Button@BUTTON_PLAY: + X:35 + Y:0 + Width:25 + Height:25 + Children: + Image@IMAGE_PLAY: + X:0 + Y:0 + Width:25 + Height:25 + ImageCollection:music + ImageName:play + Button@BUTTON_PAUSE: + Visible: no + X:35 + Y:0 + Width:25 + Height:25 + Children: + Image@IMAGE_PAUSE: + X:0 + Y:0 + Width:25 + Height:25 + ImageCollection:music + ImageName:pause + Button@BUTTON_STOP: + X:70 + Y:0 + Width:25 + Height:25 + Children: + Image@IMAGE_STOP: + X:0 + Y:0 + Width:25 + Height:25 + ImageCollection:music + ImageName:stop + Button@BUTTON_NEXT: + X:105 + Y:0 + Width:25 + Height:25 + Children: + Image@IMAGE_NEXT: + X:0 + Y:0 + Width:25 + Height:25 + ImageCollection:music + ImageName:next + Slider@FRAME_SLIDER: + X:175 + Y:0 + Width:410 + Height:20 + MinimumValue: 0 + Label@FRAME_COUNT: + X:610 + Y:0 + Width:25 + Height:25 + Font:Bold \ No newline at end of file diff --git a/mods/ra/chrome/mainmenu.yaml b/mods/ra/chrome/mainmenu.yaml index 08b9df2ef7..caac30da30 100644 --- a/mods/ra/chrome/mainmenu.yaml +++ b/mods/ra/chrome/mainmenu.yaml @@ -23,7 +23,7 @@ Container@MAINMENU: X:(WINDOW_RIGHT - WIDTH)/8 Y:(WINDOW_BOTTOM - HEIGHT)/2 Width:250 - Height:505 + Height:555 Logic:MainMenuButtonsLogic Children: Label@MAINMENU_LABEL_TITLE: @@ -83,11 +83,18 @@ Container@MAINMENU: Height:35 Text:Replay Viewer Font:Bold - Button@MAINMENU_BUTTON_QUIT: + Button@MAINMENU_BUTTON_ASSET_BROWSER: X:45 Y:430 Width:160 Height:35 + Text:Asset Browser + Font:Bold + Button@MAINMENU_BUTTON_QUIT: + X:45 + Y:480 + Width:160 + Height:35 Text:Quit Font:Bold Background@PERF_BG: diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index 9be827a088..f7137f7323 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -73,6 +73,7 @@ ChromeLayout: mods/ra/chrome/cheats.yaml mods/ra/chrome/musicplayer.yaml mods/ra/chrome/tooltips.yaml + mods/ra/chrome/assetbrowser.yaml Weapons: mods/ra/weapons.yaml