diff --git a/OpenRA.Game/Graphics/IGraphicsDevice.cs b/OpenRA.Game/Graphics/IGraphicsDevice.cs index d8214007f7..a66c199165 100755 --- a/OpenRA.Game/Graphics/IGraphicsDevice.cs +++ b/OpenRA.Game/Graphics/IGraphicsDevice.cs @@ -79,6 +79,7 @@ namespace OpenRA void Render(Action a); } + public enum TextureScaleFilter { Nearest, Linear } public interface ITexture { void SetData(Bitmap bitmap); @@ -86,6 +87,7 @@ namespace OpenRA void SetData(byte[] colors, int width, int height); byte[] GetData(); Size Size { get; } + TextureScaleFilter ScaleFilter { get; set; } } public interface IFrameBuffer diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index 30b8751024..ed276aa344 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -70,6 +70,7 @@ namespace OpenRA public string Title; public string Type = "Conquest"; + public string PreviewVideo; public string Description; public string Author; public string Tileset; diff --git a/OpenRA.Game/Widgets/VqaPlayerWidget.cs b/OpenRA.Game/Widgets/VqaPlayerWidget.cs index eb52a87396..10731a1d27 100644 --- a/OpenRA.Game/Widgets/VqaPlayerWidget.cs +++ b/OpenRA.Game/Widgets/VqaPlayerWidget.cs @@ -18,6 +18,13 @@ namespace OpenRA.Widgets { public class VqaPlayerWidget : Widget { + public Hotkey CancelKey = new Hotkey(Keycode.ESCAPE, Modifiers.None); + public float AspectRatio = 1.2f; + public bool DrawOverlay = true; + + public bool Paused { get { return paused; } } + public VqaReader Video { get { return video; } } + Sprite videoSprite, overlaySprite; VqaReader video = null; string cachedVideo; @@ -29,17 +36,14 @@ namespace OpenRA.Widgets Action onComplete; - public bool Paused { get { return paused; } } - public VqaReader Video { get { return video; } } - readonly World world; + [ObjectCreator.UseCtor] public VqaPlayerWidget(World world) { this.world = world; } - public bool DrawOverlay = true; public void Load(string filename) { if (filename == cachedVideo) @@ -58,25 +62,29 @@ namespace OpenRA.Widgets var size = Math.Max(video.Width, video.Height); var textureSize = Exts.NextPowerOf2(size); var videoSheet = new Sheet(new Size(textureSize, textureSize), false); + + videoSheet.Texture.ScaleFilter = TextureScaleFilter.Linear; videoSheet.Texture.SetData(video.FrameData); videoSprite = new Sprite(videoSheet, new Rectangle(0, 0, video.Width, video.Height), TextureChannel.Alpha); - var scale = Math.Min(RenderBounds.Width / video.Width, RenderBounds.Height / video.Height); - videoOrigin = new float2(RenderBounds.X + (RenderBounds.Width - scale * video.Width) / 2, RenderBounds.Y + (RenderBounds.Height - scale * video.Height) / 2); - videoSize = new float2(video.Width * scale, video.Height * scale); + var scale = Math.Min(RenderBounds.Width / video.Width, RenderBounds.Height / (video.Height * AspectRatio)); + videoOrigin = new float2(RenderBounds.X + (RenderBounds.Width - scale * video.Width) / 2, RenderBounds.Y + (RenderBounds.Height - scale * AspectRatio * video.Height) / 2); + + // Round size to integer pixels. Round up to be consistent with the scale calcuation. + videoSize = new float2((int)Math.Ceiling(video.Width * scale), (int)Math.Ceiling(video.Height * scale * AspectRatio)); if (!DrawOverlay) return; - overlay = new uint[2 * textureSize, 2 * textureSize]; + var scaledHeight = (int)videoSize.Y; + overlay = new uint[Exts.NextPowerOf2(scaledHeight), 1]; var black = (uint)255 << 24; - for (var y = 0; y < video.Height; y++) - for (var x = 0; x < video.Width; x++) - overlay[2 * y, x] = black; + for (var y = 0; y < scaledHeight; y += 2) + overlay[y, 0] = black; - var overlaySheet = new Sheet(new Size(2 * textureSize, 2 * textureSize), false); + var overlaySheet = new Sheet(new Size(1, Exts.NextPowerOf2(scaledHeight)), false); overlaySheet.Texture.SetData(overlay); - overlaySprite = new Sprite(overlaySheet, new Rectangle(0, 0, video.Width, 2 * video.Height), TextureChannel.Alpha); + overlaySprite = new Sprite(overlaySheet, new Rectangle(0, 0, 1, scaledHeight), TextureChannel.Alpha); } public override void Draw() @@ -84,7 +92,7 @@ namespace OpenRA.Widgets if (video == null) return; - if (!(stopped || paused)) + if (!stopped && !paused) { var nextFrame = (int)float2.Lerp(0, video.Frames, Sound.VideoSeekPosition * invLength); if (nextFrame > video.Frames) @@ -109,16 +117,16 @@ namespace OpenRA.Widgets public override bool HandleKeyPress(KeyInput e) { - if (e.Event == KeyInputEvent.Down) - { - if (e.Key == Keycode.ESCAPE) - { - Stop(); - return true; - } - } + if (Hotkey.FromKeyInput(e) != CancelKey || e.Event != KeyInputEvent.Down) + return false; - return false; + Stop(); + return true; + } + + public override bool HandleMouseInput(MouseInput mi) + { + return RenderBounds.Contains(mi.Location); } public void Play() diff --git a/OpenRA.Mods.RA/Widgets/Logic/MainMenuLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/MainMenuLogic.cs index fe6a885022..43d78833cf 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/MainMenuLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/MainMenuLogic.cs @@ -79,13 +79,18 @@ namespace OpenRA.Mods.RA.Widgets.Logic missionsButton.OnClick = () => { menuType = MenuType.None; - Ui.OpenWindow("MISSIONBROWSER_PANEL", new WidgetArgs + Game.OpenWindow("MISSIONBROWSER_PANEL", new WidgetArgs { { "onExit", () => menuType = MenuType.Singleplayer }, { "onStart", RemoveShellmapUI } }); }; - missionsButton.Disabled = !Game.modData.Manifest.Missions.Any(); + + var hasCampaign = Game.modData.Manifest.Missions.Any(); + var hasMissions = Game.modData.MapCache + .Any(p => p.Status == MapStatus.Available && p.Map.Type == "Mission" && !p.Map.Selectable); + + missionsButton.Disabled = !hasCampaign && !hasMissions; singleplayerMenu.Get("SKIRMISH_BUTTON").OnClick = StartSkirmishGame; diff --git a/OpenRA.Mods.RA/Widgets/Logic/MissionBrowserLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/MissionBrowserLogic.cs index 8b02492d51..50ddbdee52 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/MissionBrowserLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/MissionBrowserLogic.cs @@ -9,9 +9,11 @@ #endregion using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; +using OpenRA.FileSystem; using OpenRA.Graphics; using OpenRA.Network; using OpenRA.Widgets; @@ -24,35 +26,104 @@ namespace OpenRA.Mods.RA.Widgets.Logic readonly ScrollPanelWidget descriptionPanel; readonly LabelWidget description; readonly SpriteFont descriptionFont; + readonly ButtonWidget startVideoButton; + readonly ButtonWidget stopVideoButton; + readonly VqaPlayerWidget videoPlayer; + + readonly ScrollPanelWidget missionList; + readonly ScrollItemWidget headerTemplate; + readonly ScrollItemWidget template; MapPreview selectedMapPreview; + bool showVideoPlayer; + [ObjectCreator.UseCtor] public MissionBrowserLogic(Widget widget, Action onStart, Action onExit) { this.onStart = onStart; - var missionList = widget.Get("MISSION_LIST"); - var template = widget.Get("MISSION_TEMPLATE"); + missionList = widget.Get("MISSION_LIST"); + + headerTemplate = widget.Get("HEADER"); + template = widget.Get("TEMPLATE"); + + var title = widget.GetOrNull("MISSIONBROWSER_TITLE"); + if (title != null) + title.GetText = () => showVideoPlayer ? selectedMapPreview.Title : title.Text; widget.Get("MISSION_INFO").IsVisible = () => selectedMapPreview != null; var previewWidget = widget.Get("MISSION_PREVIEW"); previewWidget.Preview = () => selectedMapPreview; + previewWidget.IsVisible = () => !showVideoPlayer; + + videoPlayer = widget.Get("MISSION_VIDEO"); + widget.Get("MISSION_BIN").IsVisible = () => showVideoPlayer; descriptionPanel = widget.Get("MISSION_DESCRIPTION_PANEL"); - description = widget.Get("MISSION_DESCRIPTION"); + + description = descriptionPanel.Get("MISSION_DESCRIPTION"); descriptionFont = Game.Renderer.Fonts[description.Font]; - var yaml = new MiniYaml(null, Game.modData.Manifest.Missions.Select(MiniYaml.FromFile).Aggregate(MiniYaml.MergeLiberal)).ToDictionary(); + startVideoButton = widget.Get("START_VIDEO_BUTTON"); + stopVideoButton = widget.Get("STOP_VIDEO_BUTTON"); + stopVideoButton.IsVisible = () => showVideoPlayer; + stopVideoButton.OnClick = StopVideo; - var missionMapPaths = yaml["Missions"].Nodes.Select(n => Platform.ResolvePath(n.Key)); + var allMaps = new List(); + missionList.RemoveChildren(); - var maps = Game.modData.MapCache - .Where(p => p.Status == MapStatus.Available && missionMapPaths.Contains(Path.GetFullPath(p.Map.Path))) + // Add a group for each campaign + if (Game.modData.Manifest.Missions.Any()) + { + var yaml = Game.modData.Manifest.Missions.Select(MiniYaml.FromFile).Aggregate(MiniYaml.MergeLiberal); + + foreach (var kv in yaml) + { + var missionMapPaths = kv.Value.Nodes.Select(n => Path.GetFullPath(n.Key)); + + var maps = Game.modData.MapCache + .Where(p => p.Status == MapStatus.Available && missionMapPaths.Contains(Path.GetFullPath(p.Map.Path))) + .Select(p => p.Map); + + CreateMissionGroup(kv.Key, maps); + allMaps.AddRange(maps); + } + } + + // Add an additional group for loose missions + // Loose missions must define Type: Mission and Selectable: false. + var looseMissions = Game.modData.MapCache + .Where(p => p.Status == MapStatus.Available && p.Map.Type == "Mission" && !p.Map.Selectable && !allMaps.Contains(p.Map)) .Select(p => p.Map); - missionList.RemoveChildren(); + if (looseMissions.Any()) + { + CreateMissionGroup("Missions", looseMissions); + allMaps.AddRange(looseMissions); + } + + if (allMaps.Any()) + SelectMap(allMaps.First()); + + widget.Get("STARTGAME_BUTTON").OnClick = StartMission; + + widget.Get("BACK_BUTTON").OnClick = () => + { + StopVideo(); + Game.Disconnect(); + Ui.CloseWindow(); + onExit(); + }; + } + + void CreateMissionGroup(string title, IEnumerable maps) + { + var header = ScrollItemWidget.Setup(headerTemplate, () => true, () => {}); + header.Get("LABEL").GetText = () => title; + missionList.AddChild(header); + foreach (var m in maps) { var map = m; @@ -65,23 +136,32 @@ namespace OpenRA.Mods.RA.Widgets.Logic item.Get("TITLE").GetText = () => map.Title; missionList.AddChild(item); } - - if (maps.Any()) - SelectMap(maps.First()); - - widget.Get("STARTGAME_BUTTON").OnClick = StartMission; - - widget.Get("BACK_BUTTON").OnClick = () => - { - Game.Disconnect(); - Ui.CloseWindow(); - onExit(); - }; } + float cachedSoundVolume; + float cachedMusicVolume; void SelectMap(Map map) { + StopVideo(); + selectedMapPreview = Game.modData.MapCache[map.Uid]; + var video = selectedMapPreview.Map.PreviewVideo; + var videoVisible = video != null; + var videoDisabled = !(videoVisible && GlobalFileSystem.Exists(video)); + + startVideoButton.IsVisible = () => videoVisible && !showVideoPlayer; + startVideoButton.IsDisabled = () => videoDisabled; + startVideoButton.OnClick = () => + { + showVideoPlayer = true; + videoPlayer.Load(video); + videoPlayer.PlayThen(StopVideo); + + // Mute other distracting sounds + cachedSoundVolume = Sound.SoundVolume; + cachedMusicVolume = Sound.MusicVolume; + Sound.SoundVolume = Sound.MusicVolume = 0; + }; var text = map.Description != null ? map.Description.Replace("\\n", "\n") : ""; text = WidgetUtils.WrapText(text, description.Bounds.Width, descriptionFont); @@ -91,8 +171,22 @@ namespace OpenRA.Mods.RA.Widgets.Logic descriptionPanel.Layout.AdjustChildren(); } + void StopVideo() + { + if (!showVideoPlayer) + return; + + Sound.SoundVolume = cachedSoundVolume; + Sound.MusicVolume = cachedMusicVolume; + + videoPlayer.Stop(); + showVideoPlayer = false; + } + void StartMission() { + StopVideo(); + OrderManager om = null; Action lobbyReady = null; diff --git a/OpenRA.Renderer.Null/NullGraphicsDevice.cs b/OpenRA.Renderer.Null/NullGraphicsDevice.cs index 3e2d8d6f67..5e2da9c886 100644 --- a/OpenRA.Renderer.Null/NullGraphicsDevice.cs +++ b/OpenRA.Renderer.Null/NullGraphicsDevice.cs @@ -80,6 +80,7 @@ namespace OpenRA.Renderer.Null public class NullTexture : ITexture { + public TextureScaleFilter ScaleFilter { get { return TextureScaleFilter.Nearest; } set { } } public void SetData(Bitmap bitmap) { } public void SetData(uint[,] colors) { } public void SetData(byte[] colors, int width, int height) { } diff --git a/OpenRA.Renderer.Sdl2/Texture.cs b/OpenRA.Renderer.Sdl2/Texture.cs index 8590a6242e..de82a06e8e 100644 --- a/OpenRA.Renderer.Sdl2/Texture.cs +++ b/OpenRA.Renderer.Sdl2/Texture.cs @@ -19,11 +19,29 @@ namespace OpenRA.Renderer.Sdl2 public class Texture : ITexture { int texture; + TextureScaleFilter scaleFilter; + Size size; + public int ID { get { return texture; } } - Size size; public Size Size { get { return size; } } + public TextureScaleFilter ScaleFilter + { + get + { + return scaleFilter; + } + set + { + if (scaleFilter == value) + return; + + scaleFilter = value; + PrepareTexture(); + } + } + public Texture() { GL.GenTextures(1, out texture); @@ -45,9 +63,11 @@ namespace OpenRA.Renderer.Sdl2 ErrorHandler.CheckGlError(); GL.BindTexture(TextureTarget.Texture2D, texture); ErrorHandler.CheckGlError(); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMinFilter.Nearest); + + var filter = scaleFilter == TextureScaleFilter.Linear ? (int)TextureMinFilter.Linear : (int)TextureMinFilter.Nearest; + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, filter); ErrorHandler.CheckGlError(); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, filter); ErrorHandler.CheckGlError(); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (float)TextureWrapMode.ClampToEdge); diff --git a/artsrc/cnc/campaign/gdi01.psd b/artsrc/cnc/campaign/gdi01.psd new file mode 100644 index 0000000000..55b08e3a4d Binary files /dev/null and b/artsrc/cnc/campaign/gdi01.psd differ diff --git a/artsrc/cnc/campaign/gdi02.psd b/artsrc/cnc/campaign/gdi02.psd new file mode 100644 index 0000000000..03e5b04cee Binary files /dev/null and b/artsrc/cnc/campaign/gdi02.psd differ diff --git a/artsrc/cnc/campaign/gdi03.psd b/artsrc/cnc/campaign/gdi03.psd new file mode 100644 index 0000000000..8b47dc7e2d Binary files /dev/null and b/artsrc/cnc/campaign/gdi03.psd differ diff --git a/artsrc/cnc/campaign/gdi04.psd b/artsrc/cnc/campaign/gdi04.psd new file mode 100644 index 0000000000..50b500655c Binary files /dev/null and b/artsrc/cnc/campaign/gdi04.psd differ diff --git a/artsrc/cnc/campaign/nod01.psd b/artsrc/cnc/campaign/nod01.psd new file mode 100644 index 0000000000..7a970e7160 Binary files /dev/null and b/artsrc/cnc/campaign/nod01.psd differ diff --git a/artsrc/cnc/campaign/nod03.psd b/artsrc/cnc/campaign/nod03.psd new file mode 100644 index 0000000000..b51c9ced79 Binary files /dev/null and b/artsrc/cnc/campaign/nod03.psd differ diff --git a/artsrc/ra/campaign/generic-allies.psd b/artsrc/ra/campaign/generic-allies.psd new file mode 100644 index 0000000000..1e3fa61b81 Binary files /dev/null and b/artsrc/ra/campaign/generic-allies.psd differ diff --git a/artsrc/ra/campaign/generic-soviets.psd b/artsrc/ra/campaign/generic-soviets.psd new file mode 100644 index 0000000000..e3f6b53a35 Binary files /dev/null and b/artsrc/ra/campaign/generic-soviets.psd differ diff --git a/mods/cnc/chrome/ingame-infobriefing.yaml b/mods/cnc/chrome/ingame-infobriefing.yaml index 5151240779..57e4d87ff6 100644 --- a/mods/cnc/chrome/ingame-infobriefing.yaml +++ b/mods/cnc/chrome/ingame-infobriefing.yaml @@ -6,27 +6,26 @@ Container@MAP_PANEL: Background@PREVIEW_BG: X: (PARENT_RIGHT - WIDTH) / 2 Y: 15 - Width: 324 - Height: 160 - Background: panel-gray + Width: 362 + Height: 202 + Background: panel-black Children: MapPreview@MAP_PREVIEW: - Width: 320 - Height: 156 - X: 2 - Y: 2 + Width: 360 + Height: 200 + X: 1 + Y: 1 IgnoreMouseOver: True IgnoreMouseInput: True ShowSpawnPoints: False ScrollPanel@MAP_DESCRIPTION_PANEL: X: 15 - Y: 190 + Y: 228 Width: 482 - Height: 170 + Height: 132 Children: Label@MAP_DESCRIPTION: - X: 5 - Y: 195 - Width: 452 - Height: 160 + X: 4 + Y: 1 + Width: PARENT_RIGHT - 32 diff --git a/mods/cnc/chrome/missionbrowser.yaml b/mods/cnc/chrome/missionbrowser.yaml index 3a6a20199c..c4e27df95b 100644 --- a/mods/cnc/chrome/missionbrowser.yaml +++ b/mods/cnc/chrome/missionbrowser.yaml @@ -2,31 +2,42 @@ Container@MISSIONBROWSER_PANEL: Logic: MissionBrowserLogic X: (WINDOW_RIGHT - WIDTH)/2 Y: (WINDOW_BOTTOM - HEIGHT)/2 - Width: 629 - Height: 399 + Width: 642 + Height: 377 Children: - Label@MISSIONBROWSER_LABEL_TITLE: + Label@MISSIONBROWSER_TITLE: Y: 0-25 Width: PARENT_RIGHT Text: Missions Align: Center + Contrast: true Font: BigBold Background@BG: - Width: 629 - Height: 360 + Width: 642 + Height: 377 Background: panel-black Children: ScrollPanel@MISSION_LIST: X: 15 Y: 15 - Width: 260 - Height: 330 + Width: 239 + Height: 347 Children: - ScrollItem@MISSION_TEMPLATE: + ScrollItem@HEADER: + Width: PARENT_RIGHT-27 + Height: 13 + X: 2 + Visible: false + Children: + Label@LABEL: + Font: TinyBold + Width: PARENT_RIGHT + Height: 10 + Align: Center + ScrollItem@TEMPLATE: Width: PARENT_RIGHT-27 Height: 25 X: 2 - Y: 0 Visible: False Children: Label@TITLE: @@ -34,50 +45,67 @@ Container@MISSIONBROWSER_PANEL: Width: PARENT_RIGHT-20 Height: 25 Container@MISSION_INFO: - X: 290 + X: 265 Y: 15 - Width: 324 - Height: 334 + Width: 362 + Height: 349 Children: Background@MISSION_BG: - X: 0 - Y: 0 - Width: 324 - Height: 160 - Background: panel-gray + Width: PARENT_RIGHT + Height: 202 + Background: panel-black Children: MapPreview@MISSION_PREVIEW: - X: 2 - Y: 2 - Width: PARENT_RIGHT-4 - Height: PARENT_BOTTOM-4 + X: 1 + Y: 1 + Width: PARENT_RIGHT-2 + Height: PARENT_BOTTOM-2 IgnoreMouseOver: True IgnoreMouseInput: True ShowSpawnPoints: False ScrollPanel@MISSION_DESCRIPTION_PANEL: - X: 0 - Y: 171 - Width: 324 - Height: 159 + Y: 213 + Width: PARENT_RIGHT + Height: 134 Children: Label@MISSION_DESCRIPTION: - X: 5 - Y: 5 - Width: 290 + X: 4 + Y: 1 + Width: PARENT_RIGHT - 32 VAlign: Top + Font: Small Button@BACK_BUTTON: - X: 0 - Y: 359 + Y: 376 Width: 140 Height: 35 Text: Back Font: Bold Key: escape - Button@STARTGAME_BUTTON: - X: PARENT_RIGHT - 140 - Y: 359 + Button@START_VIDEO_BUTTON: + X: PARENT_RIGHT - 290 + Y: 376 Width: 140 Height: 35 - Text: Start Game + Text: View Briefing Font: Bold - + Button@STOP_VIDEO_BUTTON: + X: PARENT_RIGHT - 290 + Y: 376 + Width: 140 + Height: 35 + Text: Stop Briefing + Font: Bold + Button@STARTGAME_BUTTON: + X: PARENT_RIGHT - 140 + Y: 376 + Width: 140 + Height: 35 + Text: Play + Font: Bold + Container@MISSION_BIN: + Children: + VqaPlayer@MISSION_VIDEO: + X: 1 + Y: 1 + Width: 640 + Height: 375 diff --git a/mods/cnc/maps/gdi01/gdi01.lua b/mods/cnc/maps/gdi01/gdi01.lua index 88f6de7228..705a060c9a 100644 --- a/mods/cnc/maps/gdi01/gdi01.lua +++ b/mods/cnc/maps/gdi01/gdi01.lua @@ -39,7 +39,7 @@ CheckForBase = function() end WorldLoaded = function() - Media.PlayMovieFullscreen("gdi1.vqa", function() Media.PlayMovieFullscreen("landing.vqa") end) + Media.PlayMovieFullscreen("landing.vqa") player = Player.GetPlayer("GDI") enemy = Player.GetPlayer("Nod") diff --git a/mods/cnc/maps/gdi01/map.png b/mods/cnc/maps/gdi01/map.png index e0f20803e4..516b843424 100644 Binary files a/mods/cnc/maps/gdi01/map.png and b/mods/cnc/maps/gdi01/map.png differ diff --git a/mods/cnc/maps/gdi01/map.yaml b/mods/cnc/maps/gdi01/map.yaml index cddcee6915..cb528ed011 100644 --- a/mods/cnc/maps/gdi01/map.yaml +++ b/mods/cnc/maps/gdi01/map.yaml @@ -8,6 +8,8 @@ Title: Storm the Beachhead Description: Use the units provided to protect the Mobile Construction Vehicle. (MCV)\n\nYou should then deploy the MCV by double clicking on it.\n\nThen you can begin to build up a base. Start with a Power Plant.\n\nFinally, search out and destroy all enemy Nod units in the surrounding area. +PreviewVideo: gdi1.vqa + Author: Westwood Studios Tileset: TEMPERAT diff --git a/mods/cnc/maps/gdi02/gdi02.lua b/mods/cnc/maps/gdi02/gdi02.lua index 1fd1bc5e09..62942563ec 100644 --- a/mods/cnc/maps/gdi02/gdi02.lua +++ b/mods/cnc/maps/gdi02/gdi02.lua @@ -65,8 +65,6 @@ WorldLoaded = function() Trigger.OnKilled(NodRefinery, function() player.MarkFailedObjective(gdiObjective2) end) Trigger.OnAllKilled(nodInBaseTeam, BridgeheadSecured) - - Media.PlayMovieFullscreen("gdi2.vqa") end Tick = function() diff --git a/mods/cnc/maps/gdi02/map.png b/mods/cnc/maps/gdi02/map.png index 6b2ae51c7c..6d6d1b6ef4 100644 Binary files a/mods/cnc/maps/gdi02/map.png and b/mods/cnc/maps/gdi02/map.png differ diff --git a/mods/cnc/maps/gdi02/map.yaml b/mods/cnc/maps/gdi02/map.yaml index 9ef2308f9e..41949c011a 100644 --- a/mods/cnc/maps/gdi02/map.yaml +++ b/mods/cnc/maps/gdi02/map.yaml @@ -8,6 +8,8 @@ Title: Knock out the Refinery Description: Defend your position, deploy the MCV, then build a sizable force to search out and destroy the Nod base in the area.\n\nAll Nod units and structures must be either destroyed or captured to complete objective. +PreviewVideo: gdi2.vqa + Author: Westwood Studios Tileset: TEMPERAT diff --git a/mods/cnc/maps/gdi03/gdi03.lua b/mods/cnc/maps/gdi03/gdi03.lua index 988d060522..942a931a7e 100644 --- a/mods/cnc/maps/gdi03/gdi03.lua +++ b/mods/cnc/maps/gdi03/gdi03.lua @@ -24,7 +24,7 @@ WorldLoaded = function() player = OpenRA.GetPlayer("GDI") enemy = OpenRA.GetPlayer("Nod") - Media.PlayMovieFullscreen("gdi3.vqa", function() Media.PlayMovieFullscreen("samdie.vqa") end) + Media.PlayMovieFullscreen("samdie.vqa") samSites = Team.New({ Sam1, Sam2, Sam3, Sam4 }) Team.AddEventHandler(samSites.OnAllKilled, function() Actor.Create("PowerProxy.AirSupport", { Owner = player }) end) diff --git a/mods/cnc/maps/gdi03/map.png b/mods/cnc/maps/gdi03/map.png index bed8513e57..aa595926ed 100644 Binary files a/mods/cnc/maps/gdi03/map.png and b/mods/cnc/maps/gdi03/map.png differ diff --git a/mods/cnc/maps/gdi03/map.yaml b/mods/cnc/maps/gdi03/map.yaml index 858950a204..60fa5d3f1d 100644 --- a/mods/cnc/maps/gdi03/map.yaml +++ b/mods/cnc/maps/gdi03/map.yaml @@ -8,6 +8,8 @@ Title: Destroy The SAM Sites Description: Build up forces to destroy Nod base.\n\nOnce all Nod SAM sites are neutralized then air support will be provided to combat obstacles such as turrets.\n\nDestroy all units and structures to complete the mission objective. +PreviewVideo: gdi3.vqa + Author: Westwood Studios Tileset: TEMPERAT diff --git a/mods/cnc/maps/gdi04a/gdi04a.lua b/mods/cnc/maps/gdi04a/gdi04a.lua index c911c0ecdf..a9f733763e 100644 --- a/mods/cnc/maps/gdi04a/gdi04a.lua +++ b/mods/cnc/maps/gdi04a/gdi04a.lua @@ -111,7 +111,7 @@ SetupWorld = function() end WorldLoaded = function() - Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("gdi4b.vqa", function() Media.PlayMovieFullscreen("nitejump.vqa") end) end) + Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("nitejump.vqa") end) player = OpenRA.GetPlayer("GDI") nod = OpenRA.GetPlayer("Nod") diff --git a/mods/cnc/maps/gdi04a/map.png b/mods/cnc/maps/gdi04a/map.png index ff6a235a34..078691db24 100644 Binary files a/mods/cnc/maps/gdi04a/map.png and b/mods/cnc/maps/gdi04a/map.png differ diff --git a/mods/cnc/maps/gdi04a/map.yaml b/mods/cnc/maps/gdi04a/map.yaml index 47c4e9ce7f..dc99884152 100644 --- a/mods/cnc/maps/gdi04a/map.yaml +++ b/mods/cnc/maps/gdi04a/map.yaml @@ -8,6 +8,8 @@ Title: Get the Rods back (a) Description: Nod has captured classified GDI property.\n\nYou must find and retrieve the stolen equipment.\n\nIt is being transported in a shipping crate.\n\nUse the new APC to strategically transport infantry through Nod forces. +PreviewVideo: gdi4b.vqa + Author: Westwood Studios Tileset: TEMPERAT diff --git a/mods/cnc/maps/gdi04b/gdi04b.lua b/mods/cnc/maps/gdi04b/gdi04b.lua index ba071d47e7..8f539d2073 100644 --- a/mods/cnc/maps/gdi04b/gdi04b.lua +++ b/mods/cnc/maps/gdi04b/gdi04b.lua @@ -151,7 +151,7 @@ SetupWorld = function() end WorldLoaded = function() - Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("gdi4b.vqa", function() Media.PlayMovieFullscreen("nitejump.vqa") end) end) + Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("nitejump.vqa") end) player = OpenRA.GetPlayer("GDI") nod = OpenRA.GetPlayer("Nod") diff --git a/mods/cnc/maps/gdi04b/map.png b/mods/cnc/maps/gdi04b/map.png index 2fd72f67a9..7884cb3264 100644 Binary files a/mods/cnc/maps/gdi04b/map.png and b/mods/cnc/maps/gdi04b/map.png differ diff --git a/mods/cnc/maps/gdi04b/map.yaml b/mods/cnc/maps/gdi04b/map.yaml index e20f86bfdc..6041dc7d15 100644 --- a/mods/cnc/maps/gdi04b/map.yaml +++ b/mods/cnc/maps/gdi04b/map.yaml @@ -10,6 +10,8 @@ Author: Westwood Studios Description: Nod has captured classified GDI property.\n\nYou must find and retrieve the stolen equipment.\n\nIt is being transported in a shipping crate.\n\nUse the new APC to strategically transport infantry through Nod forces. +PreviewVideo: gdi4b.vqa + Tileset: TEMPERAT MapSize: 64,64 diff --git a/mods/cnc/maps/gdi04c/gdi04c.lua b/mods/cnc/maps/gdi04c/gdi04c.lua index d9372a1854..7be48e9aad 100644 --- a/mods/cnc/maps/gdi04c/gdi04c.lua +++ b/mods/cnc/maps/gdi04c/gdi04c.lua @@ -122,7 +122,7 @@ WorldLoaded = function() Camera.Position = Actor141.CenterPosition - Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("gdi4a.vqa", function() Media.PlayMovieFullscreen("nodsweep.vqa") end) end) + Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("nodsweep.vqa") end) end Tick = function() diff --git a/mods/cnc/maps/gdi04c/map.png b/mods/cnc/maps/gdi04c/map.png index 54891042db..4c734350ee 100644 Binary files a/mods/cnc/maps/gdi04c/map.png and b/mods/cnc/maps/gdi04c/map.png differ diff --git a/mods/cnc/maps/gdi04c/map.yaml b/mods/cnc/maps/gdi04c/map.yaml index 7863beb31c..608b51b8ce 100644 --- a/mods/cnc/maps/gdi04c/map.yaml +++ b/mods/cnc/maps/gdi04c/map.yaml @@ -10,6 +10,8 @@ Author: Westwood Studios Description: Nod is moving to capture and hold a civilian town.\n\nYour mission is to reach the town first and hold off invading Nod units until GDI reinforcements can arrive.\n\nAll invading Nod units must be destroyed. +PreviewVideo: gdi4a.vqa + Tileset: TEMPERAT MapSize: 64,64 diff --git a/mods/cnc/maps/nod01/map.png b/mods/cnc/maps/nod01/map.png index 0b920ef21d..d09050d776 100644 Binary files a/mods/cnc/maps/nod01/map.png and b/mods/cnc/maps/nod01/map.png differ diff --git a/mods/cnc/maps/nod01/map.yaml b/mods/cnc/maps/nod01/map.yaml index ff684131f0..0a49beab55 100644 --- a/mods/cnc/maps/nod01/map.yaml +++ b/mods/cnc/maps/nod01/map.yaml @@ -8,6 +8,8 @@ Title: Nikoomba's Demise Description: In order for the Brotherhood to gain a foothold, we must begin by eliminating certain elements.\n\nNikoomba, the nearby village's leader, is one such element.\n\nHis views and ours do not coincide.\n\nHe and his whole group must be eliminated. +PreviewVideo: nod1.vqa + Author: Westwood Studios Tileset: DESERT diff --git a/mods/cnc/maps/nod01/nod01.lua b/mods/cnc/maps/nod01/nod01.lua index 4ed638fae0..72a25292d7 100644 --- a/mods/cnc/maps/nod01/nod01.lua +++ b/mods/cnc/maps/nod01/nod01.lua @@ -56,8 +56,6 @@ WorldLoaded = function() Trigger.AfterDelay(Utils.Seconds(30), SendFirstInfantryReinforcements) Trigger.AfterDelay(Utils.Seconds(60), SendSecondInfantryReinforcements) - - Media.PlayMovieFullscreen("nod1pre.vqa", function() Media.PlayMovieFullscreen("nod1.vqa") end) end Tick = function() diff --git a/mods/cnc/maps/nod03a/map.png b/mods/cnc/maps/nod03a/map.png index 7ea08b2445..de09c696d3 100644 Binary files a/mods/cnc/maps/nod03a/map.png and b/mods/cnc/maps/nod03a/map.png differ diff --git a/mods/cnc/maps/nod03a/map.yaml b/mods/cnc/maps/nod03a/map.yaml index dea3bd2b6a..3622916d75 100644 --- a/mods/cnc/maps/nod03a/map.yaml +++ b/mods/cnc/maps/nod03a/map.yaml @@ -8,6 +8,8 @@ Title: Sudanese Prison Break (a) Description: GDI has established a prison camp, where they are detaining some of the local political leaders.\n\nKane wishes to liberate these victims.\n\nDestroy the GDI forces and capture the prison, do not destroy it. +PreviewVideo: nod3.vqa + Author: Westwood Studios Tileset: DESERT diff --git a/mods/cnc/maps/nod03a/nod03a.lua b/mods/cnc/maps/nod03a/nod03a.lua index 8f7e2eb9fd..d340c7c1bc 100644 --- a/mods/cnc/maps/nod03a/nod03a.lua +++ b/mods/cnc/maps/nod03a/nod03a.lua @@ -61,8 +61,6 @@ WorldLoaded = function() Trigger.AfterDelay(Utils.Seconds(20), function() SendAttackWave(FirstAttackWave, AttackWaveSpawnA.Location) end) Trigger.AfterDelay(Utils.Seconds(50), function() SendAttackWave(SecondThirdAttackWave, AttackWaveSpawnB.Location) end) Trigger.AfterDelay(Utils.Seconds(100), function() SendAttackWave(SecondThirdAttackWave, AttackWaveSpawnC.Location) end) - - Media.PlayMovieFullscreen("nod3.vqa") end Tick = function() diff --git a/mods/cnc/maps/nod03b/map.png b/mods/cnc/maps/nod03b/map.png index f1bf88f868..7c4d3077a3 100644 Binary files a/mods/cnc/maps/nod03b/map.png and b/mods/cnc/maps/nod03b/map.png differ diff --git a/mods/cnc/maps/nod03b/map.yaml b/mods/cnc/maps/nod03b/map.yaml index 77d0b361fc..d2698f872a 100644 --- a/mods/cnc/maps/nod03b/map.yaml +++ b/mods/cnc/maps/nod03b/map.yaml @@ -8,6 +8,8 @@ Title: Sudanese Prison Break (b) Description: GDI has established a prison camp, where they are detaining some of the local political leaders.\n\nKane wishes to liberate these victims.\n\nDestroy the GDI forces and capture the prison, do not destroy it. +PreviewVideo: nod3.vqa + Author: Westwood Studios Tileset: DESERT diff --git a/mods/cnc/maps/nod03b/nod03b.lua b/mods/cnc/maps/nod03b/nod03b.lua index 37cc32c461..e9efa6fb7b 100644 --- a/mods/cnc/maps/nod03b/nod03b.lua +++ b/mods/cnc/maps/nod03b/nod03b.lua @@ -75,7 +75,6 @@ WorldLoaded = function() Trigger.AfterDelay(Utils.Seconds(80), function() SendAttackWave(SecondAttackWaveUnits, SecondAttackWave) end) Trigger.AfterDelay(Utils.Seconds(140), function() SendAttackWave(ThirdAttackWaveUnits, FirstAttackWave) end) - Media.PlayMovieFullscreen("nod3.vqa") end Tick = function() diff --git a/mods/cnc/missions.yaml b/mods/cnc/missions.yaml index 395b49e2c6..b36e7bb26f 100644 --- a/mods/cnc/missions.yaml +++ b/mods/cnc/missions.yaml @@ -1,10 +1,12 @@ -Missions: +GDI Campaign: ./mods/cnc/maps/gdi01 ./mods/cnc/maps/gdi02 ./mods/cnc/maps/gdi03 ./mods/cnc/maps/gdi04a ./mods/cnc/maps/gdi04b ./mods/cnc/maps/gdi04c + +Nod Campaign: ./mods/cnc/maps/nod01 ./mods/cnc/maps/nod03a ./mods/cnc/maps/nod03b diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index f6704dbba1..6a26411a6a 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -170,6 +170,9 @@ ChromeMetrics: ./mods/cnc/metrics.yaml Fonts: + Small: + Font:FreeSans.ttf + Size:12 Regular: Font:./FreeSans.ttf Size:14 diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index 353c509d7f..a7b8a80ab8 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -166,6 +166,9 @@ Fonts: BigBold: Font:./FreeSansBold.ttf Size:24 + Small: + Font:FreeSans.ttf + Size:12 Tiny: Font:./FreeSans.ttf Size:10 diff --git a/mods/ra/chrome/ingame-infobriefing.yaml b/mods/ra/chrome/ingame-infobriefing.yaml index de0813f7fe..1a8f22f713 100644 --- a/mods/ra/chrome/ingame-infobriefing.yaml +++ b/mods/ra/chrome/ingame-infobriefing.yaml @@ -6,27 +6,27 @@ Container@MAP_PANEL: Background@PREVIEW_BG: X: (PARENT_RIGHT - WIDTH) / 2 Y: 20 - Width: 324 - Height: 160 - Background: panel-gray + Width: 362 + Height: 202 + Background: dialog3 Children: MapPreview@MAP_PREVIEW: - Width: 320 - Height: 156 - X: 2 - Y: 2 + X: 1 + Y: 1 + Width: PARENT_RIGHT-2 + Height: PARENT_BOTTOM-2 IgnoreMouseOver: True IgnoreMouseInput: True ShowSpawnPoints: False ScrollPanel@MAP_DESCRIPTION_PANEL: X: 20 - Y: 195 - Width: 482 - Height: 175 + Y: 232 + Width: PARENT_RIGHT - 40 + Height: 138 Children: Label@MAP_DESCRIPTION: - X: 5 - Y: 180 - Width: 452 - Height: 145 + X: 4 + Y: 1 + Width: PARENT_RIGHT - 32 + diff --git a/mods/ra/chrome/missionbrowser.yaml b/mods/ra/chrome/missionbrowser.yaml index 388dc2287e..65bfb17581 100644 --- a/mods/ra/chrome/missionbrowser.yaml +++ b/mods/ra/chrome/missionbrowser.yaml @@ -2,11 +2,10 @@ Background@MISSIONBROWSER_PANEL: Logic: MissionBrowserLogic X: (WINDOW_RIGHT - WIDTH)/2 Y: (WINDOW_BOTTOM - HEIGHT)/2 - Width: 634 - Height: 440 + Width: 682 + Height: 487 Children: - Label@MISSIONBROWSER_LABEL_TITLE: - X: 0 + Label@MISSIONBROWSER_TITLE: Y: 20 Width: PARENT_RIGHT Height: 25 @@ -16,57 +15,80 @@ Background@MISSIONBROWSER_PANEL: ScrollPanel@MISSION_LIST: X: 20 Y: 50 - Width: 260 - Height: 330 + Width: 270 + Height: 377 Children: - ScrollItem@MISSION_TEMPLATE: + ScrollItem@HEADER: + BaseName: scrollheader + Width: PARENT_RIGHT-27 + Height: 13 + X: 2 + Visible: false + Children: + Label@LABEL: + Font: TinyBold + Width: PARENT_RIGHT + Height: 10 + Align: Center + ScrollItem@TEMPLATE: Width: PARENT_RIGHT-27 Height: 25 X: 2 - Y: 0 Children: Label@TITLE: X: 10 Width: PARENT_RIGHT-20 Height: 25 Container@MISSION_INFO: - X: 290 + X: 300 Y: 50 - Width: 324 - Height: 330 + Width: 362 + Height: 363 Children: Background@MISSION_BG: - X: 0 - Y: 0 - Width: 324 - Height: 160 + Width: PARENT_RIGHT + Height: 202 Background: dialog3 Children: MapPreview@MISSION_PREVIEW: - X: 2 - Y: 2 - Width: PARENT_RIGHT-4 - Height: PARENT_BOTTOM-4 + X: 1 + Y: 1 + Width: PARENT_RIGHT-2 + Height: PARENT_BOTTOM-2 IgnoreMouseOver: True IgnoreMouseInput: True ShowSpawnPoints: False ScrollPanel@MISSION_DESCRIPTION_PANEL: - X: 0 - Y: 170 - Width: 324 - Height: 160 + Y: 212 + Width: PARENT_RIGHT + Height: 165 Children: Label@MISSION_DESCRIPTION: - X: 5 - Y: 5 - Width: 290 + X: 4 + Y: 1 + Width: PARENT_RIGHT - 32 VAlign: Top + Font: Small + Button@START_VIDEO_BUTTON: + X: 20 + Y: PARENT_BOTTOM - 45 + Width: 120 + Height: 25 + Text: Play Briefing + Font: Bold + Button@STOP_VIDEO_BUTTON: + X: 20 + Y: PARENT_BOTTOM - 45 + Width: 120 + Height: 25 + Text: Stop Briefing + Font: Bold Button@STARTGAME_BUTTON: X: PARENT_RIGHT - 140 - 130 Y: PARENT_BOTTOM - 45 Width: 120 Height: 25 - Text: Start Game + Text: Play Font: Bold Button@BACK_BUTTON: X: PARENT_RIGHT - 140 @@ -76,4 +98,15 @@ Background@MISSIONBROWSER_PANEL: Text: Back Font: Bold Key: escape - + Background@MISSION_BIN: + X: 20 + Y: 50 + Width: 642 + Height: 377 + Background: dialog3 + Children: + VqaPlayer@MISSION_VIDEO: + X: 1 + Y: 1 + Width: 640 + Height: 375 diff --git a/mods/ra/maps/allies-01-classic/allies01.lua b/mods/ra/maps/allies-01-classic/allies01.lua index 55509cb099..c218fb8773 100644 --- a/mods/ra/maps/allies-01-classic/allies01.lua +++ b/mods/ra/maps/allies-01-classic/allies01.lua @@ -201,5 +201,5 @@ WorldLoaded = function() Camera.Position = InsertionLZ.CenterPosition - Media.PlayMovieFullscreen("ally1.vqa", function() Media.PlayMovieFullscreen("landing.vqa") end) + Media.PlayMovieFullscreen("landing.vqa") end diff --git a/mods/ra/maps/allies-01-classic/map.png b/mods/ra/maps/allies-01-classic/map.png index 20080e725c..aaa1507d66 100644 Binary files a/mods/ra/maps/allies-01-classic/map.png and b/mods/ra/maps/allies-01-classic/map.png differ diff --git a/mods/ra/maps/allies-01-classic/map.yaml b/mods/ra/maps/allies-01-classic/map.yaml index 02686b6bcf..9d2ac96690 100644 --- a/mods/ra/maps/allies-01-classic/map.yaml +++ b/mods/ra/maps/allies-01-classic/map.yaml @@ -8,6 +8,8 @@ Title: Allies 01: In the thick of it Description: Rescue Einstein from the Headquarters inside this Soviet complex.\n\nOnce found, evacuate him via the helicopter at the signal flare.\n\nEinstein and Tanya must be kept alive at all costs.\n\nBeware the Soviet's Tesla Coils.\n\nDirect Tanya to destroy the westmost power plants to take them off-line. +PreviewVideo: ally1.vqa + Author: Westwood Studios Tileset: SNOW diff --git a/mods/ra/maps/allies-02-classic/allies02.lua b/mods/ra/maps/allies-02-classic/allies02.lua index 9fabd9ec51..b57a7e0319 100644 --- a/mods/ra/maps/allies-02-classic/allies02.lua +++ b/mods/ra/maps/allies-02-classic/allies02.lua @@ -123,7 +123,7 @@ WorldLoaded = function() Camera.Position = ReinforcementsEntryPoint.CenterPosition - Media.PlayMovieFullscreen("ally2.vqa", function() Media.PlayMovieFullscreen("mcv.vqa") end) + Media.PlayMovieFullscreen("mcv.vqa") ConvoyTimer(Utils.Seconds(3), "TenMinutesRemaining") ConvoyTimer(Utils.Minutes(5), "WarningFiveMinutesRemaining") diff --git a/mods/ra/maps/allies-02-classic/map.png b/mods/ra/maps/allies-02-classic/map.png index e9dcb2a0a4..aaa1507d66 100644 Binary files a/mods/ra/maps/allies-02-classic/map.png and b/mods/ra/maps/allies-02-classic/map.png differ diff --git a/mods/ra/maps/allies-02-classic/map.yaml b/mods/ra/maps/allies-02-classic/map.yaml index 18b4d8771c..b2cf3f5303 100644 --- a/mods/ra/maps/allies-02-classic/map.yaml +++ b/mods/ra/maps/allies-02-classic/map.yaml @@ -8,6 +8,8 @@ Title: Allies 02: Five to one Description: A critical supply convoy is due through this area in 10 minutes, but Soviet forces have blocked the road in several places.\n\nUnless you can clear them out, those supplies will never make it to the front.\n\nThe convoy will come from the northwest, and time is short so work quickly. +PreviewVideo: ally2.vqa + Author: Westwood Studios Tileset: SNOW diff --git a/mods/ra/maps/intervention/map.png b/mods/ra/maps/intervention/map.png index 8c3df0baf7..aaa1507d66 100644 Binary files a/mods/ra/maps/intervention/map.png and b/mods/ra/maps/intervention/map.png differ diff --git a/mods/ra/missions.yaml b/mods/ra/missions.yaml index f161e07770..9777f0d2cc 100644 --- a/mods/ra/missions.yaml +++ b/mods/ra/missions.yaml @@ -1,4 +1,3 @@ -Missions: +Allied Campaign: ./mods/ra/maps/allies-01-classic ./mods/ra/maps/allies-02-classic - ./mods/ra/maps/intervention diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index 4781530520..ce28346587 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -182,6 +182,9 @@ Fonts: BigBold: Font:./FreeSansBold.ttf Size:24 + Small: + Font:FreeSans.ttf + Size:12 Tiny: Font:./FreeSans.ttf Size:10 diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index 002a037ebd..3eef8e96d1 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -207,6 +207,9 @@ Fonts: BigBold: Font:./FreeSansBold.ttf Size:24 + Small: + Font:FreeSans.ttf + Size:12 Tiny: Font:./FreeSans.ttf Size:10