diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorActorBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorActorBrush.cs new file mode 100644 index 0000000000..0bf3e7cec9 --- /dev/null +++ b/OpenRA.Mods.Common/EditorBrushes/EditorActorBrush.cs @@ -0,0 +1,134 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Mods.Common; +using OpenRA.Mods.Common.Graphics; +using OpenRA.Mods.Common.Traits; +using OpenRA.Orders; +using OpenRA.Primitives; +using OpenRA.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets +{ + public class EditorActorBrush : IEditorBrush + { + public readonly ActorInfo Actor; + + readonly WorldRenderer worldRenderer; + readonly World world; + readonly EditorActorLayer editorLayer; + readonly EditorViewportControllerWidget editorWidget; + readonly ActorPreviewWidget preview; + readonly CVec locationOffset; + readonly WVec previewOffset; + readonly PlayerReference owner; + + int facing = 92; + + public EditorActorBrush(EditorViewportControllerWidget editorWidget, ActorInfo actor, PlayerReference owner, WorldRenderer wr) + { + this.editorWidget = editorWidget; + worldRenderer = wr; + world = wr.World; + editorLayer = world.WorldActor.Trait(); + + Actor = actor; + this.owner = owner; + + preview = editorWidget.Get("DRAG_ACTOR_PREVIEW"); + preview.GetScale = () => worldRenderer.Viewport.Zoom; + preview.IsVisible = () => editorWidget.CurrentBrush == this; + + var buildingInfo = actor.Traits.GetOrDefault(); + if (buildingInfo != null) + { + locationOffset = -FootprintUtils.AdjustForBuildingSize(buildingInfo); + previewOffset = FootprintUtils.CenterOffset(world, buildingInfo); + } + + var td = new TypeDictionary(); + td.Add(new FacingInit(facing)); + td.Add(new TurretFacingInit(facing)); + td.Add(new OwnerInit(owner.Name)); + td.Add(new RaceInit(owner.Race)); + preview.SetPreview(actor, td); + + // The preview widget may be rendered by the higher-level code before it is ticked. + // Force a manual tick to ensure the bounds are set correctly for this first draw. + Tick(); + } + + public bool HandleMouseInput(MouseInput mi) + { + // Exclusively uses left and right mouse buttons, but nothing else + if (mi.Button != MouseButton.Left && mi.Button != MouseButton.Right) + return false; + + if (mi.Button == MouseButton.Right) + { + editorWidget.ClearBrush(); + return true; + } + + var cell = worldRenderer.Viewport.ViewToWorld(mi.Location); + if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Down && world.Map.Contains(cell)) + { + var newActorReference = new ActorReference(Actor.Name); + newActorReference.Add(new OwnerInit(owner.Name)); + + cell += locationOffset; + newActorReference.Add(new LocationInit(cell)); + + var ios = Actor.Traits.GetOrDefault(); + if (ios != null && ios.SharesCell) + { + var subcell = editorLayer.FreeSubCellAt(cell); + if (subcell != SubCell.Invalid) + newActorReference.Add(new SubCellInit(subcell)); + } + + var initDict = newActorReference.InitDict; + + if (Actor.Traits.Contains()) + initDict.Add(new FacingInit(facing)); + + if (Actor.Traits.Contains()) + initDict.Add(new TurretFacingInit(facing)); + + editorLayer.Add(newActorReference); + } + + return true; + } + + public void Tick() + { + var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos); + var pos = world.Map.CenterOfCell(cell + locationOffset) + previewOffset; + + var origin = worldRenderer.Viewport.WorldToViewPx(worldRenderer.ScreenPxPosition(pos)); + + var zoom = worldRenderer.Viewport.Zoom; + var s = preview.IdealPreviewSize; + var o = preview.PreviewOffset; + preview.Bounds.X = origin.X - (int)(zoom * (o.X + s.X / 2)); + preview.Bounds.Y = origin.Y - (int)(zoom * (o.Y + s.Y / 2)); + preview.Bounds.Width = (int)(zoom * s.X); + preview.Bounds.Height = (int)(zoom * s.Y); + } + } +} diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorDefaultBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorDefaultBrush.cs new file mode 100644 index 0000000000..5a8178192a --- /dev/null +++ b/OpenRA.Mods.Common/EditorBrushes/EditorDefaultBrush.cs @@ -0,0 +1,114 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Mods.Common; +using OpenRA.Mods.Common.Graphics; +using OpenRA.Mods.Common.Traits; +using OpenRA.Orders; +using OpenRA.Primitives; +using OpenRA.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets +{ + public interface IEditorBrush + { + bool HandleMouseInput(MouseInput mi); + void Tick(); + } + + public class EditorDefaultBrush : IEditorBrush + { + public readonly ActorInfo Actor; + + readonly WorldRenderer worldRenderer; + readonly World world; + readonly EditorViewportControllerWidget editorWidget; + readonly EditorActorLayer editorLayer; + readonly Dictionary resources; + + public EditorDefaultBrush(EditorViewportControllerWidget editorWidget, WorldRenderer wr) + { + this.editorWidget = editorWidget; + worldRenderer = wr; + world = wr.World; + + editorLayer = world.WorldActor.Trait(); + resources = world.WorldActor.TraitsImplementing() + .ToDictionary(r => r.Info.ResourceType, r => r); + } + + public bool HandleMouseInput(MouseInput mi) + { + // Exclusively uses left and right mouse buttons, but nothing else + // Mouse move events are important for tooltips, so we always allow these through + if (mi.Button != MouseButton.Left && mi.Button != MouseButton.Right && mi.Event != MouseInputEvent.Move) + return false; + + var cell = worldRenderer.Viewport.ViewToWorld(mi.Location); + if (mi.Event == MouseInputEvent.Up) + return true; + + var underCursor = editorLayer.PreviewsAt(worldRenderer.Viewport.ViewToWorldPx(mi.Location)) + .FirstOrDefault(); + + ResourceType type; + if (underCursor != null) + editorWidget.SetTooltip(underCursor.Tooltip); + else if (world.Map.Contains(cell) && resources.TryGetValue(world.Map.MapResources.Value[cell].Type, out type)) + editorWidget.SetTooltip(type.Info.Name); + else + editorWidget.SetTooltip(null); + + // Finished with mouse move events, so let them bubble up the widget tree + if (mi.Event == MouseInputEvent.Move) + return false; + + if (mi.Button == MouseButton.Right) + { + editorWidget.SetTooltip(null); + + if (underCursor != null) + editorLayer.Remove(underCursor); + + if (world.Map.MapResources.Value[cell].Type != 0) + world.Map.MapResources.Value[cell] = new ResourceTile(); + } + else if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Down) + { + if (underCursor != null) + { + // Test case / demonstration of how to edit an existing actor + var facing = underCursor.Init(); + if (facing != null) + underCursor.ReplaceInit(new FacingInit((facing.Value(world) + 32) % 256)); + else if (underCursor.Info.Traits.WithInterface>().Any()) + underCursor.ReplaceInit(new FacingInit(32)); + + var turret = underCursor.Init(); + if (turret != null) + underCursor.ReplaceInit(new TurretFacingInit((turret.Value(world) + 32) % 256)); + else if (underCursor.Info.Traits.WithInterface>().Any()) + underCursor.ReplaceInit(new TurretFacingInit(32)); + } + } + + return true; + } + + public void Tick() { } + } +} diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorResourceBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorResourceBrush.cs new file mode 100644 index 0000000000..bf94b01a86 --- /dev/null +++ b/OpenRA.Mods.Common/EditorBrushes/EditorResourceBrush.cs @@ -0,0 +1,115 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Mods.Common; +using OpenRA.Mods.Common.Graphics; +using OpenRA.Mods.Common.Traits; +using OpenRA.Orders; +using OpenRA.Primitives; +using OpenRA.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets +{ + public class EditorResourceBrush : IEditorBrush + { + public readonly ResourceTypeInfo ResourceType; + + readonly WorldRenderer worldRenderer; + readonly World world; + readonly EditorViewportControllerWidget editorWidget; + readonly SpriteWidget preview; + + public EditorResourceBrush(EditorViewportControllerWidget editorWidget, ResourceTypeInfo resource, WorldRenderer wr) + { + this.editorWidget = editorWidget; + ResourceType = resource; + worldRenderer = wr; + world = wr.World; + + preview = editorWidget.Get("DRAG_LAYER_PREVIEW"); + preview.Palette = resource.Palette; + preview.GetScale = () => worldRenderer.Viewport.Zoom; + preview.IsVisible = () => editorWidget.CurrentBrush == this; + + var variant = resource.Variants.FirstOrDefault(); + var sequenceProvider = wr.World.Map.Rules.Sequences[world.TileSet.Id]; + var sequence = sequenceProvider.GetSequence("resources", variant); + var sprite = sequence.GetSprite(resource.MaxDensity - 1); + preview.GetSprite = () => sprite; + + // The preview widget may be rendered by the higher-level code before it is ticked. + // Force a manual tick to ensure the bounds are set correctly for this first draw. + Tick(); + } + + public bool HandleMouseInput(MouseInput mi) + { + // Exclusively uses left and right mouse buttons, but nothing else + if (mi.Button != MouseButton.Left && mi.Button != MouseButton.Right) + return false; + + if (mi.Button == MouseButton.Right) + { + editorWidget.ClearBrush(); + return true; + } + + var cell = worldRenderer.Viewport.ViewToWorld(mi.Location); + + if (mi.Button == MouseButton.Left && AllowResourceAt(cell)) + { + var type = (byte)ResourceType.ResourceType; + var index = (byte)ResourceType.MaxDensity; + world.Map.MapResources.Value[cell] = new ResourceTile(type, index); + } + + return true; + } + + public bool AllowResourceAt(CPos cell) + { + if (!world.Map.Contains(cell)) + return false; + + var tile = world.Map.MapTiles.Value[cell]; + var tileInfo = world.TileSet.GetTileInfo(tile); + var terrainType = world.TileSet.TerrainInfo[tileInfo.TerrainType]; + + if (world.Map.MapResources.Value[cell].Type == ResourceType.ResourceType) + return false; + + if (!ResourceType.AllowedTerrainTypes.Contains(terrainType.Type)) + return false; + + return ResourceType.AllowOnRamps || tileInfo == null || tileInfo.RampType == 0; + } + + public void Tick() + { + var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos); + var offset = WVec.Zero; + var location = world.Map.CenterOfCell(cell) + offset; + + var cellScreenPosition = worldRenderer.ScreenPxPosition(location); + var cellScreenPixel = worldRenderer.Viewport.WorldToViewPx(cellScreenPosition); + var zoom = worldRenderer.Viewport.Zoom; + + preview.Bounds.X = cellScreenPixel.X; + preview.Bounds.Y = cellScreenPixel.Y; + } + } +} diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs new file mode 100644 index 0000000000..47fcd26704 --- /dev/null +++ b/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs @@ -0,0 +1,151 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Mods.Common; +using OpenRA.Mods.Common.Graphics; +using OpenRA.Mods.Common.Traits; +using OpenRA.Orders; +using OpenRA.Primitives; +using OpenRA.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets +{ + public class EditorTileBrush : IEditorBrush + { + public readonly ushort Template; + + readonly WorldRenderer worldRenderer; + readonly World world; + readonly EditorViewportControllerWidget editorWidget; + readonly TerrainTemplatePreviewWidget preview; + readonly Rectangle bounds; + + bool painting; + + public EditorTileBrush(EditorViewportControllerWidget editorWidget, ushort template, WorldRenderer wr) + { + this.editorWidget = editorWidget; + Template = template; + worldRenderer = wr; + world = wr.World; + + preview = editorWidget.Get("DRAG_TILE_PREVIEW"); + preview.GetScale = () => worldRenderer.Viewport.Zoom; + preview.IsVisible = () => editorWidget.CurrentBrush == this; + + preview.Template = world.TileSet.Templates.First(t => t.Value.Id == template).Value; + bounds = worldRenderer.Theater.TemplateBounds(preview.Template, Game.ModData.Manifest.TileSize, world.Map.TileShape); + + // The preview widget may be rendered by the higher-level code before it is ticked. + // Force a manual tick to ensure the bounds are set correctly for this first draw. + Tick(); + } + + public bool HandleMouseInput(MouseInput mi) + { + // Exclusively uses left and right mouse buttons, but nothing else + if (mi.Button != MouseButton.Left && mi.Button != MouseButton.Right) + return false; + + if (mi.Button == MouseButton.Right) + { + editorWidget.ClearBrush(); + return true; + } + + if (mi.Button == MouseButton.Left) + { + if (mi.Event == MouseInputEvent.Down) + painting = true; + else if (mi.Event == MouseInputEvent.Up) + painting = false; + } + + if (!painting) + return true; + + var map = world.Map; + var cell = worldRenderer.Viewport.ViewToWorld(mi.Location); + + if (mi.Event != MouseInputEvent.Down && mi.Event != MouseInputEvent.Move) + return true; + + var rules = map.Rules; + var tileset = rules.TileSets[map.Tileset]; + var template = tileset.Templates[Template]; + var baseHeight = map.Contains(cell) ? map.MapHeight.Value[cell] : 0; + if (mi.Event == MouseInputEvent.Move && PlacementOverlapsSameTemplate(template, cell)) + return true; + + var i = 0; + for (var y = 0; y < template.Size.Y; y++) + { + for (var x = 0; x < template.Size.X; x++, i++) + { + if (template.Contains(i) && template[i] != null) + { + var index = template.PickAny ? (byte)Game.CosmeticRandom.Next(0, template.TilesCount) : (byte)i; + var c = cell + new CVec(x, y); + if (!map.Contains(c)) + continue; + + map.MapTiles.Value[c] = new TerrainTile(Template, index); + map.MapHeight.Value[c] = (byte)(baseHeight + template[index].Height).Clamp(0, world.TileSet.MaxGroundHeight); + } + } + } + + return true; + } + + bool PlacementOverlapsSameTemplate(TerrainTemplateInfo template, CPos cell) + { + var map = world.Map; + var i = 0; + for (var y = 0; y < template.Size.Y; y++) + { + for (var x = 0; x < template.Size.X; x++, i++) + { + if (template.Contains(i) && template[i] != null) + { + var c = cell + new CVec(x, y); + if (map.Contains(c) && map.MapTiles.Value[c].Type == template.Id) + return true; + } + } + } + + return false; + } + + public void Tick() + { + var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos); + var offset = WVec.Zero; + var location = world.Map.CenterOfCell(cell) + offset; + + var cellScreenPosition = worldRenderer.ScreenPxPosition(location); + var cellScreenPixel = worldRenderer.Viewport.WorldToViewPx(cellScreenPosition); + var zoom = worldRenderer.Viewport.Zoom; + + preview.Bounds.X = cellScreenPixel.X + (int)(zoom * bounds.X); + preview.Bounds.Y = cellScreenPixel.Y + (int)(zoom * bounds.Y); + preview.Bounds.Width = (int)(zoom * bounds.Width); + preview.Bounds.Height = (int)(zoom * bounds.Height); + } + } +} diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index d109070c18..cc1b29a043 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -539,6 +539,7 @@ + @@ -559,6 +560,13 @@ + + + + + + + @@ -628,8 +636,13 @@ + + + + + @@ -644,4 +657,7 @@ cd "$(SolutionDir)" --> + + + \ No newline at end of file diff --git a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs index e38dd1403e..c7608ed245 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs @@ -98,26 +98,33 @@ namespace OpenRA.Mods.Common.Traits return t.Variants.Keys.Random(Game.CosmeticRandom); } + public int ResourceDensityAt(CPos c) + { + // Set density based on the number of neighboring resources + var adjacent = 0; + var type = Tiles[c].Type; + for (var u = -1; u < 2; u++) + for (var v = -1; v < 2; v++) + if (Map.MapResources.Value[c + new CVec(u, v)].Type == type.Info.ResourceType) + adjacent++; + + return Math.Max(int2.Lerp(0, type.Info.MaxDensity, adjacent, 9), 1); + } + public virtual CellContents UpdateDirtyTile(CPos c) { var t = Tiles[c]; + var type = t.Type; // Empty tile - if (t.Type == null) + if (type == null) { t.Sprite = null; return t; } // Set density based on the number of neighboring resources - var adjacent = 0; - var type = t.Type; - for (var u = -1; u < 2; u++) - for (var v = -1; v < 2; v++) - if (Map.MapResources.Value[c + new CVec(u, v)].Type == type.Info.ResourceType) - adjacent++; - - t.Density = Math.Max(int2.Lerp(0, type.Info.MaxDensity, adjacent, 9), 1); + t.Density = ResourceDensityAt(c); var sprites = type.Variants[t.Variant]; var frame = int2.Lerp(0, sprites.Length - 1, t.Density - 1, type.Info.MaxDensity); diff --git a/OpenRA.Mods.Common/Traits/World/LoadWidgetAtGameStart.cs b/OpenRA.Mods.Common/Traits/World/LoadWidgetAtGameStart.cs index 8ab785161f..75462811b8 100644 --- a/OpenRA.Mods.Common/Traits/World/LoadWidgetAtGameStart.cs +++ b/OpenRA.Mods.Common/Traits/World/LoadWidgetAtGameStart.cs @@ -23,6 +23,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("The widget tree to open when a regular map is loaded (i.e. the ingame UI).")] public readonly string IngameRoot = "INGAME_ROOT"; + [Desc("The widget tree to open when the map editor is loaded.")] + public readonly string EditorRoot = "EDITOR_ROOT"; + [Desc("Remove any existing UI when a map is loaded.")] public readonly bool ClearRoot = true; @@ -44,7 +47,9 @@ namespace OpenRA.Mods.Common.Traits if (info.ClearRoot) Ui.ResetAll(); - var widget = world.Type == WorldType.Shellmap ? info.ShellmapRoot : info.IngameRoot; + var widget = world.Type == WorldType.Shellmap ? info.ShellmapRoot : + world.Type == WorldType.Editor ? info.EditorRoot : info.IngameRoot; + Game.LoadWidget(world, widget, Ui.Root, new WidgetArgs()); } } diff --git a/OpenRA.Mods.Common/Widgets/EditorViewportControllerWidget.cs b/OpenRA.Mods.Common/Widgets/EditorViewportControllerWidget.cs new file mode 100644 index 0000000000..78ceb84c71 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/EditorViewportControllerWidget.cs @@ -0,0 +1,98 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Mods.Common; +using OpenRA.Mods.Common.Graphics; +using OpenRA.Mods.Common.Traits; +using OpenRA.Orders; +using OpenRA.Primitives; +using OpenRA.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets +{ + public class EditorViewportControllerWidget : Widget + { + public IEditorBrush CurrentBrush { get; private set; } + + public readonly string TooltipContainer; + public readonly string TooltipTemplate; + + readonly Lazy tooltipContainer; + readonly EditorDefaultBrush defaultBrush; + readonly WorldRenderer worldRenderer; + + bool enableTooltips; + + [ObjectCreator.UseCtor] + public EditorViewportControllerWidget(World world, WorldRenderer worldRenderer) + { + this.worldRenderer = worldRenderer; + tooltipContainer = Exts.Lazy(() => Ui.Root.Get(TooltipContainer)); + CurrentBrush = defaultBrush = new EditorDefaultBrush(this, worldRenderer); + } + + public void ClearBrush() { SetBrush(null); } + public void SetBrush(IEditorBrush brush) + { + CurrentBrush = brush ?? defaultBrush; + } + + public override void MouseEntered() + { + enableTooltips = true; + } + + public override void MouseExited() + { + tooltipContainer.Value.RemoveTooltip(); + enableTooltips = false; + } + + public void SetTooltip(string tooltip) + { + if (!enableTooltips) + return; + + if (tooltip != null) + { + Func getTooltip = () => tooltip; + tooltipContainer.Value.SetTooltip(TooltipTemplate, new WidgetArgs() { { "getText", getTooltip } }); + } + else + tooltipContainer.Value.RemoveTooltip(); + } + + public override bool HandleMouseInput(MouseInput mi) + { + if (CurrentBrush.HandleMouseInput(mi)) + return true; + + return base.HandleMouseInput(mi); + } + + WPos cachedViewportPosition; + public override void Tick() + { + // Clear any tooltips when the viewport is scrolled using the keyboard + if (worldRenderer.Viewport.CenterPosition != cachedViewportPosition) + SetTooltip(null); + + cachedViewportPosition = worldRenderer.Viewport.CenterPosition; + CurrentBrush.Tick(); + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/ActorSelectorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/ActorSelectorLogic.cs new file mode 100644 index 0000000000..909c9513ba --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/ActorSelectorLogic.cs @@ -0,0 +1,150 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Mods.Common; +using OpenRA.Mods.Common.Graphics; +using OpenRA.Mods.Common.Traits; +using OpenRA.Mods.Common.Widgets; +using OpenRA.Primitives; +using OpenRA.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class ActorSelectorLogic + { + readonly EditorViewportControllerWidget editor; + readonly DropDownButtonWidget ownersDropDown; + readonly ScrollPanelWidget panel; + readonly ScrollItemWidget itemTemplate; + readonly Ruleset modRules; + readonly World world; + readonly WorldRenderer worldRenderer; + + PlayerReference selectedOwner; + + [ObjectCreator.UseCtor] + public ActorSelectorLogic(Widget widget, World world, WorldRenderer worldRenderer, Ruleset modRules) + { + this.modRules = modRules; + this.world = world; + this.worldRenderer = worldRenderer; + + editor = widget.Parent.Get("MAP_EDITOR"); + ownersDropDown = widget.Get("OWNERS_DROPDOWN"); + + panel = widget.Get("ACTORTEMPLATE_LIST"); + itemTemplate = panel.Get("ACTORPREVIEW_TEMPLATE"); + panel.Layout = new GridLayout(panel); + + var editorLayer = world.WorldActor.Trait(); + + selectedOwner = editorLayer.Players.Players.Values.First(); + Func setupItem = (option, template) => + { + var item = ScrollItemWidget.Setup(template, () => selectedOwner == option, () => + { + selectedOwner = option; + + ownersDropDown.Text = selectedOwner.Name; + ownersDropDown.TextColor = selectedOwner.Color.RGB; + + IntializeActorPreviews(); + }); + + item.Get("LABEL").GetText = () => option.Name; + item.GetColor = () => option.Color.RGB; + + return item; + }; + + ownersDropDown.OnClick = () => + { + var owners = editorLayer.Players.Players.Values.OrderBy(p => p.Name); + ownersDropDown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 270, owners, setupItem); + }; + + ownersDropDown.Text = selectedOwner.Name; + ownersDropDown.TextColor = selectedOwner.Color.RGB; + + IntializeActorPreviews(); + } + + void IntializeActorPreviews() + { + panel.RemoveChildren(); + + var actors = modRules.Actors.Where(a => !a.Value.Name.Contains('^')) + .Select(a => a.Value); + + foreach (var a in actors) + { + var actor = a; + if (actor.Traits.Contains()) // bridge layer takes care about that automatically + continue; + + if (!actor.Traits.Contains()) + continue; + + var filter = actor.Traits.GetOrDefault(); + if (filter != null) + { + if (filter.ExcludeTilesets != null && filter.ExcludeTilesets.Contains(world.TileSet.Id)) + continue; + if (filter.RequireTilesets != null && !filter.RequireTilesets.Contains(world.TileSet.Id)) + continue; + } + + var td = new TypeDictionary(); + td.Add(new FacingInit(92)); + td.Add(new TurretFacingInit(92)); + td.Add(new HideBibPreviewInit()); + td.Add(new OwnerInit(selectedOwner.Name)); + td.Add(new RaceInit(selectedOwner.Race)); + + try + { + var item = ScrollItemWidget.Setup(itemTemplate, + () => { var brush = editor.CurrentBrush as EditorActorBrush; return brush != null && brush.Actor == actor; }, + () => editor.SetBrush(new EditorActorBrush(editor, actor, selectedOwner, worldRenderer))); + + var preview = item.Get("ACTOR_PREVIEW"); + preview.SetPreview(actor, td); + + // Scale templates to fit within the panel + var scale = 1f; + if (scale * preview.IdealPreviewSize.X > itemTemplate.Bounds.Width) + scale = (float)(itemTemplate.Bounds.Width - panel.ItemSpacing) / (float)preview.IdealPreviewSize.X; + + preview.GetScale = () => scale; + preview.Bounds.Width = (int)(scale * preview.IdealPreviewSize.X); + preview.Bounds.Height = (int)(scale * preview.IdealPreviewSize.Y); + + item.Bounds.Width = preview.Bounds.Width + 2 * preview.Bounds.X; + item.Bounds.Height = preview.Bounds.Height + 2 * preview.Bounds.Y; + item.IsVisible = () => true; + item.GetTooltipText = () => actor.Name; + panel.AddChild(item); + } + catch + { + Log.Write("debug", "Map editor ignoring actor {0}, because of missing sprites for tileset {1}.", + actor.Name, world.TileSet.Id); + continue; + } + } + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/LayerSelectorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/LayerSelectorLogic.cs new file mode 100644 index 0000000000..2412f4c8d9 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/LayerSelectorLogic.cs @@ -0,0 +1,84 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class LayerSelectorLogic + { + readonly EditorViewportControllerWidget editor; + readonly Ruleset modRules; + readonly World world; + readonly WorldRenderer worldRenderer; + + readonly ScrollPanelWidget layerTemplateList; + readonly ScrollItemWidget layerPreviewTemplate; + + [ObjectCreator.UseCtor] + public LayerSelectorLogic(Widget widget, WorldRenderer worldRenderer, Ruleset modRules) + { + this.modRules = modRules; + this.worldRenderer = worldRenderer; + this.world = worldRenderer.World; + + editor = widget.Parent.Get("MAP_EDITOR"); + + layerTemplateList = widget.Get("LAYERTEMPLATE_LIST"); + layerTemplateList.Layout = new GridLayout(layerTemplateList); + layerPreviewTemplate = layerTemplateList.Get("LAYERPREVIEW_TEMPLATE"); + + IntializeLayerPreview(widget); + } + + void IntializeLayerPreview(Widget widget) + { + layerTemplateList.RemoveChildren(); + + var resources = modRules.Actors["world"].Traits.WithInterface(); + foreach (var resource in resources) + { + var newResourcePreviewTemplate = ScrollItemWidget.Setup(layerPreviewTemplate, + () => { var brush = editor.CurrentBrush as EditorResourceBrush; return brush != null && brush.ResourceType == resource; }, + () => editor.SetBrush(new EditorResourceBrush(editor, resource, worldRenderer))); + + newResourcePreviewTemplate.Bounds.X = 0; + newResourcePreviewTemplate.Bounds.Y = 0; + + var layerPreview = newResourcePreviewTemplate.Get("LAYER_PREVIEW"); + layerPreview.IsVisible = () => true; + layerPreview.GetPalette = () => resource.Palette; + + var variant = resource.Variants.FirstOrDefault(); + var sequenceProvier = modRules.Sequences[world.TileSet.Id]; + var sequence = sequenceProvier.GetSequence("resources", variant); + var frame = sequence.Frames != null ? sequence.Frames.Last() : resource.MaxDensity - 1; + layerPreview.GetSprite = () => sequence.GetSprite(frame); + + var tileWidth = Game.ModData.Manifest.TileSize.Width; + var tileHeight = Game.ModData.Manifest.TileSize.Height; + layerPreview.Bounds.Width = tileWidth; + layerPreview.Bounds.Height = tileHeight; + newResourcePreviewTemplate.Bounds.Width = tileWidth + (layerPreview.Bounds.X * 2); + newResourcePreviewTemplate.Bounds.Height = tileHeight + (layerPreview.Bounds.Y * 2); + + newResourcePreviewTemplate.IsVisible = () => true; + newResourcePreviewTemplate.GetTooltipText = () => resource.Name; + + layerTemplateList.AddChild(newResourcePreviewTemplate); + } + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorLogic.cs new file mode 100644 index 0000000000..2a1ee4a80b --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorLogic.cs @@ -0,0 +1,70 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Drawing; +using System.Linq; +using OpenRA.Graphics; +using OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; +using OpenRA.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class MapEditorLogic + { + [ObjectCreator.UseCtor] + public MapEditorLogic(Widget widget, World world, WorldRenderer worldRenderer) + { + var gridButton = widget.GetOrNull("GRID_BUTTON"); + var terrainGeometryTrait = world.WorldActor.Trait(); + + if (gridButton != null && terrainGeometryTrait != null) + { + gridButton.OnClick = () => terrainGeometryTrait.Enabled ^= true; + gridButton.IsHighlighted = () => terrainGeometryTrait.Enabled; + } + + var zoomDropdown = widget.Get("ZOOM_BUTTON"); + if (zoomDropdown != null) + { + var selectedZoom = Game.Settings.Graphics.PixelDouble ? 2f : 1f; + var selectedLabel = selectedZoom.ToString(); + Func setupItem = (zoom, itemTemplate) => + { + var item = ScrollItemWidget.Setup(itemTemplate, + () => selectedZoom == zoom, + () => { worldRenderer.Viewport.Zoom = selectedZoom = zoom; selectedLabel = zoom.ToString(); }); + + var label = zoom.ToString(); + item.Get("LABEL").GetText = () => label; + + return item; + }; + + var options = new[] { 2f, 1f, 0.5f, 0.25f }; + zoomDropdown.OnMouseDown = _ => zoomDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 150, options, setupItem); + zoomDropdown.GetText = () => selectedLabel; + zoomDropdown.GetKey = _ => Game.Settings.Keys.TogglePixelDoubleKey; + zoomDropdown.OnKeyPress = e => + { + var key = Hotkey.FromKeyInput(e); + if (key != Game.Settings.Keys.TogglePixelDoubleKey) + return; + + var selected = (options.IndexOf(selectedZoom) + 1) % options.Length; + worldRenderer.Viewport.Zoom = selectedZoom = options[selected]; + selectedLabel = selectedZoom.ToString(); + }; + } + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorTabsLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorTabsLogic.cs new file mode 100644 index 0000000000..5edae36850 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorTabsLogic.cs @@ -0,0 +1,53 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Collections.Generic; +using System.Linq; +using OpenRA.GameRules; +using OpenRA.Graphics; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class MapEditorTabsLogic + { + protected enum MenuType { Tiles, Layers, Actors } + protected MenuType menuType = MenuType.Tiles; + + [ObjectCreator.UseCtor] + public MapEditorTabsLogic(Widget widget, WorldRenderer worldRenderer) + { + var tabContainer = widget.Get("MAP_EDITOR_TAB_CONTAINER"); + + var tilesTab = tabContainer.Get("TILES_TAB"); + tilesTab.IsHighlighted = () => menuType == MenuType.Tiles; + tilesTab.OnClick = () => { menuType = MenuType.Tiles; }; + + var overlaysTab = tabContainer.Get("OVERLAYS_TAB"); + overlaysTab.IsHighlighted = () => menuType == MenuType.Layers; + overlaysTab.OnClick = () => { menuType = MenuType.Layers; }; + + var actorsTab = tabContainer.Get("ACTORS_TAB"); + actorsTab.IsHighlighted = () => menuType == MenuType.Actors; + actorsTab.OnClick = () => { menuType = MenuType.Actors; }; + + var tileContainer = widget.Parent.Get("TILE_WIDGETS"); + tileContainer.IsVisible = () => menuType == MenuType.Tiles; + + var layerContainer = widget.Parent.Get("LAYER_WIDGETS"); + layerContainer.IsVisible = () => menuType == MenuType.Layers; + + var actorContainer = widget.Parent.Get("ACTOR_WIDGETS"); + actorContainer.IsVisible = () => menuType == MenuType.Actors; + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs new file mode 100644 index 0000000000..c9d07117b4 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs @@ -0,0 +1,94 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using OpenRA; +using OpenRA.FileFormats; +using OpenRA.Mods.Common.Widgets; +using OpenRA.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class NewMapLogic + { + Widget panel; + + [ObjectCreator.UseCtor] + public NewMapLogic(Action onExit, Action onSelect, Ruleset modRules, Widget widget, World world) + { + panel = widget; + + panel.Get("CANCEL_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); }; + + var tilesetDropDown = panel.Get("TILESET"); + var tilesets = modRules.TileSets.Select(t => t.Key).ToList(); + Func setupItem = (option, template) => + { + var item = ScrollItemWidget.Setup(template, + () => tilesetDropDown.Text == option, + () => { tilesetDropDown.Text = option; }); + item.Get("LABEL").GetText = () => option; + return item; + }; + tilesetDropDown.Text = tilesets.First(); + tilesetDropDown.OnClick = () => + tilesetDropDown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, tilesets, setupItem); + + var widthTextField = panel.Get("WIDTH"); + var heightTextField = panel.Get("HEIGHT"); + + panel.Get("CREATE_BUTTON").OnClick = () => + { + var tileset = modRules.TileSets[tilesetDropDown.Text]; + var map = Map.FromTileset(tileset); + + int width, height; + int.TryParse(widthTextField.Text, out width); + int.TryParse(heightTextField.Text, out height); + + // Require at least a 2x2 playable area so that the + // ground is visible through the edge shroud + width = Math.Max(2, width); + height = Math.Max(2, height); + + map.Resize(width + 2, height + tileset.MaxGroundHeight + 2); + map.ResizeCordon(1, 1, width + 1, height + tileset.MaxGroundHeight + 1); + map.PlayerDefinitions = new MapPlayers(map.Rules, map.SpawnPoints.Value.Length).ToMiniYaml(); + map.FixOpenAreas(modRules); + + var userMapFolder = Game.ModData.Manifest.MapFolders.First(f => f.Value == "User").Key; + + // Ignore optional flag + if (userMapFolder.StartsWith("~")) + userMapFolder = userMapFolder.Substring(1); + + var mapDir = Platform.ResolvePath(userMapFolder); + Directory.CreateDirectory(mapDir); + var tempLocation = Path.Combine(mapDir, "temp") + ".oramap"; + map.Save(tempLocation); // TODO: load it right away and save later properly + + var newMap = new Map(tempLocation); + Game.ModData.MapCache[newMap.Uid].UpdateFromMap(newMap, MapClassification.User); + + ConnectionLogic.Connect(System.Net.IPAddress.Loopback.ToString(), + Game.CreateLocalServer(newMap.Uid), + "", + () => { Game.LoadEditor(newMap.Uid); }, + () => { Game.CloseServer(); onExit(); }); + onSelect(newMap.Uid); + }; + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/SaveMapLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/SaveMapLogic.cs new file mode 100644 index 0000000000..73144f0c94 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/SaveMapLogic.cs @@ -0,0 +1,122 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Collections.Generic; +using System.IO; +using System.Linq; +using OpenRA.Mods.Common.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class SaveMapLogic + { + [ObjectCreator.UseCtor] + public SaveMapLogic(Widget widget, Action onExit, World world) + { + var newMap = world.Map; + + var title = widget.GetOrNull("TITLE"); + if (title != null) + title.Text = newMap.Title; + + var description = widget.GetOrNull("DESCRIPTION"); + if (description != null) + description.Text = newMap.Description; + + var author = widget.GetOrNull("AUTHOR"); + if (author != null) + author.Text = newMap.Author; + + var visibilityDropdown = widget.GetOrNull("CLASS_DROPDOWN"); + if (visibilityDropdown != null) + { + var mapVisibility = new List(Enum.GetNames(typeof(MapVisibility))); + Func setupItem = (option, template) => + { + var item = ScrollItemWidget.Setup(template, + () => visibilityDropdown.Text == option, + () => { visibilityDropdown.Text = option; }); + item.Get("LABEL").GetText = () => option; + return item; + }; + visibilityDropdown.Text = Enum.GetName(typeof(MapVisibility), newMap.Visibility); + visibilityDropdown.OnClick = () => + visibilityDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, mapVisibility, setupItem); + } + + var pathDropdown = widget.GetOrNull("PATH_DROPDOWN"); + if (pathDropdown != null) + { + var mapFolders = new List(); + foreach (var mapFolder in Game.ModData.Manifest.MapFolders.Keys) + { + var folder = mapFolder; + if (mapFolder.StartsWith("~")) + folder = mapFolder.Substring(1); + + mapFolders.Add(Platform.ResolvePath(folder)); + } + + Func setupItem = (option, template) => + { + var item = ScrollItemWidget.Setup(template, + () => pathDropdown.Text == Platform.UnresolvePath(option), + () => { pathDropdown.Text = Platform.UnresolvePath(option); }); + item.Get("LABEL").GetText = () => option; + return item; + }; + + var userMapFolder = Game.ModData.Manifest.MapFolders.First(f => f.Value == "User").Key; + if (userMapFolder.StartsWith("~")) + userMapFolder = userMapFolder.Substring(1); + pathDropdown.Text = Platform.UnresolvePath(userMapFolder); + pathDropdown.OnClick = () => + pathDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, mapFolders, setupItem); + } + + var filename = widget.GetOrNull("FILENAME"); + if (filename != null) + filename.Text = Path.GetFileName(world.Map.Path); + + var close = widget.GetOrNull("CLOSE"); + if (close != null) + close.OnClick = () => { Ui.CloseWindow(); onExit(); }; + + var save = widget.GetOrNull("SAVE"); + if (save != null && !string.IsNullOrEmpty(filename.Text)) + { + var editorLayer = world.WorldActor.Trait(); + save.OnClick = () => + { + newMap.Title = title.Text; + newMap.Description = description.Text; + newMap.Author = author.Text; + newMap.Visibility = (MapVisibility)Enum.Parse(typeof(MapVisibility), visibilityDropdown.Text); + newMap.ActorDefinitions = editorLayer.Save(); + newMap.PlayerDefinitions = editorLayer.Players.ToMiniYaml(); + newMap.RequiresMod = Game.ModData.Manifest.Mod.Id; + + var combinedPath = Path.Combine(pathDropdown.Text, filename.Text); + var resolvedPath = Platform.ResolvePath(combinedPath); + newMap.Save(resolvedPath); + + // Update the map cache so it can be loaded without restarting the game + Game.ModData.MapCache[newMap.Uid].UpdateFromMap(newMap, MapClassification.User); + + Console.WriteLine("Saved current map at {0}", resolvedPath); + Ui.CloseWindow(); + onExit(); + }; + } + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs new file mode 100644 index 0000000000..6693e6e3a3 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs @@ -0,0 +1,92 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class TileSelectorLogic + { + readonly EditorViewportControllerWidget editor; + readonly ScrollPanelWidget panel; + readonly ScrollItemWidget itemTemplate; + + [ObjectCreator.UseCtor] + public TileSelectorLogic(Widget widget, WorldRenderer worldRenderer, Ruleset modRules) + { + var tileset = modRules.TileSets[worldRenderer.World.Map.Tileset]; + + editor = widget.Parent.Get("MAP_EDITOR"); + panel = widget.Get("TILETEMPLATE_LIST"); + itemTemplate = panel.Get("TILEPREVIEW_TEMPLATE"); + panel.Layout = new GridLayout(panel); + + var tileCategorySelector = widget.Get("TILE_CATEGORY"); + var categories = tileset.EditorTemplateOrder; + Func setupItem = (option, template) => + { + var item = ScrollItemWidget.Setup(template, + () => tileCategorySelector.Text == option, + () => { tileCategorySelector.Text = option; IntializeTilePreview(widget, worldRenderer, tileset, option); }); + + item.Get("LABEL").GetText = () => option; + return item; + }; + + tileCategorySelector.OnClick = () => + tileCategorySelector.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 270, categories, setupItem); + + tileCategorySelector.Text = categories.First(); + IntializeTilePreview(widget, worldRenderer, tileset, categories.First()); + } + + void IntializeTilePreview(Widget widget, WorldRenderer worldRenderer, TileSet tileset, string category) + { + panel.RemoveChildren(); + + var tileIds = tileset.Templates + .Where(t => t.Value.Category == category) + .Select(t => t.Value.Id); + + foreach (var t in tileIds) + { + var tileId = t; + var item = ScrollItemWidget.Setup(itemTemplate, + () => { var brush = editor.CurrentBrush as EditorTileBrush; return brush != null && brush.Template == tileId; }, + () => editor.SetBrush(new EditorTileBrush(editor, tileId, worldRenderer))); + + var preview = item.Get("TILE_PREVIEW"); + var template = tileset.Templates[tileId]; + var bounds = worldRenderer.Theater.TemplateBounds(template, Game.ModData.Manifest.TileSize, worldRenderer.World.Map.TileShape); + + // Scale templates to fit within the panel + var scale = 1f; + while (scale * bounds.Width > itemTemplate.Bounds.Width) + scale /= 2; + + preview.Template = template; + preview.GetScale = () => scale; + preview.Bounds.Width = (int)(scale * bounds.Width); + preview.Bounds.Height = (int)(scale * bounds.Height); + + item.Bounds.Width = preview.Bounds.Width + 2 * preview.Bounds.X; + item.Bounds.Height = preview.Bounds.Height + 2 * preview.Bounds.Y; + item.IsVisible = () => true; + item.GetTooltipText = () => tileId.ToString(); + + panel.AddChild(item); + } + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/LoadMapEditorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/LoadMapEditorLogic.cs new file mode 100644 index 0000000000..74b6320ca3 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/LoadMapEditorLogic.cs @@ -0,0 +1,24 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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 OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class LoadMapEditorLogic + { + [ObjectCreator.UseCtor] + public LoadMapEditorLogic(Widget widget, World world) + { + var editorRoot = widget.Get("WORLD_ROOT"); + Game.LoadWidget(world, "EDITOR_WORLD_ROOT", editorRoot, new WidgetArgs()); + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/SpriteWidget.cs b/OpenRA.Mods.Common/Widgets/SpriteWidget.cs index cde5e4a9e8..a9858140cc 100644 --- a/OpenRA.Mods.Common/Widgets/SpriteWidget.cs +++ b/OpenRA.Mods.Common/Widgets/SpriteWidget.cs @@ -16,6 +16,7 @@ namespace OpenRA.Mods.Common.Widgets { public class SpriteWidget : Widget { + public Func GetScale = () => 1f; public string Palette = "chrome"; public Func GetPalette; public Func GetSprite; @@ -44,6 +45,7 @@ namespace OpenRA.Mods.Common.Widgets Sprite cachedSprite = null; string cachedPalette = null; + float cachedScale; PaletteReference pr; float2 offset = float2.Zero; @@ -51,6 +53,7 @@ namespace OpenRA.Mods.Common.Widgets { var sprite = GetSprite(); var palette = GetPalette(); + var scale = GetScale(); if (sprite == null || palette == null) return; @@ -67,7 +70,14 @@ namespace OpenRA.Mods.Common.Widgets cachedPalette = palette; } - Game.Renderer.SpriteRenderer.DrawSprite(sprite, RenderOrigin + offset, pr); + if (scale != cachedScale) + { + offset *= scale; + cachedScale = scale; + } + + var size = new float2(sprite.Size.X * scale, sprite.Size.Y * scale); + Game.Renderer.SpriteRenderer.DrawSprite(sprite, RenderOrigin + offset, pr, size); } } } diff --git a/OpenRA.Mods.D2k/Traits/World/D2kEditorResourceLayer.cs b/OpenRA.Mods.D2k/Traits/World/D2kEditorResourceLayer.cs index c44441ed6a..f46354380f 100644 --- a/OpenRA.Mods.D2k/Traits/World/D2kEditorResourceLayer.cs +++ b/OpenRA.Mods.D2k/Traits/World/D2kEditorResourceLayer.cs @@ -42,6 +42,8 @@ namespace OpenRA.Mods.D2k.Traits return t; } + t.Density = ResourceDensityAt(c); + int index; var clear = FindClearSides(t.Type, c); if (clear == ClearSides.None) diff --git a/mods/cnc/chrome/editor.yaml b/mods/cnc/chrome/editor.yaml new file mode 100644 index 0000000000..8d3df16217 --- /dev/null +++ b/mods/cnc/chrome/editor.yaml @@ -0,0 +1,378 @@ +Container@NEW_MAP_BG: + Logic: NewMapLogic + X: (WINDOW_RIGHT - WIDTH)/2 + Y: (WINDOW_BOTTOM - HEIGHT)/2 + Width: 300 + Height: 150 + Children: + Label@TITLE: + Text:New Map + Width: PARENT_RIGHT + Y: 0-25 + Font: BigBold + Contrast: true + Align: Center + Background@bg: + Width: PARENT_RIGHT + Height: 115 + Background: panel-black + Children: + Label@TILESET_LABEL: + X: 5 + Y: 19 + Width: 95 + Height: 25 + Align: Right + Text: Tileset: + DropDownButton@TILESET: + X: 105 + Y: 22 + Width: 150 + Height: 25 + Label@WIDTH_LABEL: + X: 5 + Y: 59 + Width: 95 + Height: 25 + Align: Right + Text: Width: + TextField@WIDTH: + X: 105 + Y: 60 + Width: 50 + MaxLength: 3 + Height: 25 + Text: 128 + Label@HEIGHT_LABEL: + X: 115 + Y: 59 + Width: 95 + Height: 25 + Align: Right + Text: Height: + TextField@HEIGHT: + X: 215 + Y: 60 + Width: 50 + MaxLength: 3 + Height: 25 + Text: 128 + Button@CREATE_BUTTON: + X: 0 + Y: 114 + Width: 140 + Height: 35 + Text: Create + Font: Bold + Key: return + Button@CANCEL_BUTTON: + X: PARENT_RIGHT - WIDTH + Y: 114 + Width: 140 + Height: 35 + Text: Cancel + Font: Bold + Key: escape + +Container@SAVE_MAP_PANEL: + X: (WINDOW_RIGHT - WIDTH)/2 + Y: (WINDOW_BOTTOM - HEIGHT)/2 + Width: 350 + Height: 330 + Children: + Label@TITLE: + Text: Save Map + Width: PARENT_RIGHT + Y: 0-25 + Font: BigBold + Contrast: true + Align: Center + Background@SAVE_MAP_BACKGROUND: + Logic: SaveMapLogic + Width: PARENT_RIGHT + Height: 295 + Background: panel-black + Children: + Label@TITLE_LABEL: + X: 10 + Y: 39 + Width: 95 + Height: 25 + Align: Right + Text: Title: + TextField@TITLE: + X: 110 + Y: 40 + Width: 210 + MaxLength: 50 + Height: 25 + Label@DESCRIPTION_LABEL: + X: 10 + Y: 79 + Width: 95 + Height: 25 + Align: Right + Text: Description: + TextField@DESCRIPTION: + X: 110 + Y: 80 + Width: 210 + MaxLength: 50 + Height: 25 + Label@AUTHOR_LABEL: + X: 10 + Y: 119 + Width: 95 + Height: 25 + Align: Right + Text: Author: + TextField@AUTHOR: + X: 110 + Y: 120 + Width: 210 + MaxLength: 50 + Height: 25 + Label@CLASS_LABEL: + X: 10 + Y: 160 + Width: 95 + Height: 25 + Align: Right + Text: Class: + DropDownButton@CLASS_DROPDOWN: + X: 110 + Y: 160 + Width: 210 + Height: 25 + Label@PATH_LABEL: + X: 10 + Y: 199 + Width: 40 + Height: 25 + Align: Right + Text: Path: + DropDownButton@PATH_DROPDOWN: + X: 60 + Y: 200 + Width: 270 + Height: 25 + Label@FILENAME_LABEL: + X: 10 + Y: 239 + Width: 40 + Height: 25 + Align: Right + Text: File: + TextField@FILENAME: + X: 60 + Y: 240 + Width: 270 + Height: 25 + Button@CLOSE: + X: 0 + Y: 294 + Width: 140 + Height: 35 + Text: Close + Font: Bold + Key: escape + Button@SAVE: + X: PARENT_RIGHT - WIDTH + Y: 294 + Width: 140 + Height: 35 + Text: Save + Font: Bold + +Container@EDITOR_ROOT: + Logic: LoadMapEditorLogic + Children: + Container@WORLD_ROOT: + Container@MENU_ROOT: + TooltipContainer@TOOLTIP_CONTAINER: + +Container@EDITOR_WORLD_ROOT: + Logic: LoadIngamePerfLogic, MapEditorLogic + Children: + Container@PERF_ROOT: + ViewportController: + Width: WINDOW_RIGHT + Height: WINDOW_BOTTOM + EditorViewportController@MAP_EDITOR: + Width: WINDOW_RIGHT + Height: WINDOW_BOTTOM + TooltipContainer: TOOLTIP_CONTAINER + TooltipTemplate: SIMPLE_TOOLTIP + Children: + TerrainTemplatePreview@DRAG_TILE_PREVIEW: + Visible: false + Sprite@DRAG_LAYER_PREVIEW: + Visible: false + ActorPreview@DRAG_ACTOR_PREVIEW: + Visible: false + Background@RADAR_BG: + X: WINDOW_RIGHT-255 + Y: 5 + Width: 250 + Height: 250 + Background: panel-gray + Children: + Radar@INGAME_RADAR: + X: 1 + Y: 1 + Width: PARENT_RIGHT-2 + Height: PARENT_BOTTOM-2 + MenuButton@OPTIONS_BUTTON: + Logic: MenuButtonsChromeLogic + Key: escape + X: WINDOW_RIGHT-254-WIDTH + Y: 5 + Width: 30 + Height: 25 + TooltipText: Menu + TooltipContainer: TOOLTIP_CONTAINER + Children: + Image: + X: 7 + Y: 5 + ImageCollection: order-icons + ImageName: options + Container@TILE_WIDGETS: + Logic: TileSelectorLogic + X: WINDOW_RIGHT-255 + Y: 278 + Width: 250 + Height: 324 + ClickThrough: false + Children: + Container@TILES_BG: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + Children: + DropDownButton@TILE_CATEGORY: + Width: PARENT_RIGHT + Height: 25 + Font: Bold + ScrollPanel@TILETEMPLATE_LIST: + Y: 24 + Width: PARENT_RIGHT + Height: PARENT_BOTTOM - 24 + ItemSpacing: 4 + Children: + ScrollItem@TILEPREVIEW_TEMPLATE: + Visible: false + Width: PARENT_RIGHT - 35 + TooltipContainer: TOOLTIP_CONTAINER + Children: + TerrainTemplatePreview@TILE_PREVIEW: + X: 4 + Y: 4 + Container@LAYER_WIDGETS: + Logic: LayerSelectorLogic + X: WINDOW_RIGHT-255 + Y: 278 + Width: 250 + Height: 324 + ClickThrough: false + Children: + Container@LAYERS_BG: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + Children: + ScrollPanel@LAYERTEMPLATE_LIST: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + ItemSpacing: 4 + Children: + ScrollItem@LAYERPREVIEW_TEMPLATE: + Visible: false + IgnoreChildMouseOver: true + TooltipContainer: TOOLTIP_CONTAINER + Children: + Sprite@LAYER_PREVIEW: + X: 4 + Y: 4 + Visible: false + Container@ACTOR_WIDGETS: + Logic: ActorSelectorLogic + X: WINDOW_RIGHT-255 + Y: 278 + Width: 250 + Height: 324 + ClickThrough: false + Children: + Container@ACTORS_BG: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + Children: + DropDownButton@OWNERS_DROPDOWN: + Width: PARENT_RIGHT + Height: 25 + Font: Bold + ScrollPanel@ACTORTEMPLATE_LIST: + Y: 24 + Width: PARENT_RIGHT + Height: PARENT_BOTTOM - 24 + ItemSpacing: 4 + Children: + ScrollItem@ACTORPREVIEW_TEMPLATE: + Visible: false + Width: PARENT_RIGHT - 35 + TooltipContainer: TOOLTIP_CONTAINER + IgnoreChildMouseOver: true + Children: + ActorPreview@ACTOR_PREVIEW: + X: 4 + Y: 4 + Visible: true + Container@MAP_EDITOR_TAB_CONTAINER: + Logic: MapEditorTabsLogic + X: WINDOW_RIGHT-255 + Y: 254 + Width: 250 + Height: 25 + ClickThrough: false + Children: + Button@TILES_TAB: + Width: 81 + Height: 25 + Text: Tiles + Font: Bold + Button@OVERLAYS_TAB: + X: 80 + Width: 90 + Height: 25 + Text: Overlays + Font: Bold + Button@ACTORS_TAB: + X: 169 + Width: 81 + Height: 25 + Text: Actors + Font: Bold + Button@GRID_BUTTON: + X: WINDOW_RIGHT - 420 + Y: 5 + Width: 100 + Height: 25 + Text: Grid + Font: Bold + Key: f1 + TooltipTemplate: BUTTON_TOOLTIP + TooltipText: Toggle the terrain grid + TooltipContainer: TOOLTIP_CONTAINER + Label@ZOOM_LABEL: + X: WINDOW_RIGHT - 500 - 55 + Y: 5 + Width: 50 + Height: 25 + Text: Zoom: + Align: Right + Font: Bold + Contrast: true + DropDownButton@ZOOM_BUTTON: + X: WINDOW_RIGHT - 500 + Y: 5 + Width: 70 + Height: 25 + Font: Bold \ No newline at end of file diff --git a/mods/cnc/chrome/ingame-menu.yaml b/mods/cnc/chrome/ingame-menu.yaml index 0cf8a5a81d..6fe83efae8 100644 --- a/mods/cnc/chrome/ingame-menu.yaml +++ b/mods/cnc/chrome/ingame-menu.yaml @@ -33,12 +33,24 @@ Container@INGAME_MENU: Width: 140 Height: 35 Text: Abort Mission + Button@EXIT_EDITOR: + X: 0 + Y: 0 + Width: 140 + Height: 35 + Text: Exit Map Editor Button@SURRENDER: X: 150 Y: 0 Width: 140 Height: 35 Text: Surrender + Button@SAVE_MAP: + X: 150 + Y: 0 + Width: 140 + Height: 35 + Text: Save Map Button@MUSIC: X: 300 Y: 0 diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index cbfe2428e5..832b1ad871 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -120,6 +120,7 @@ ChromeLayout: ./mods/cnc/chrome/tooltips.yaml ./mods/cnc/chrome/assetbrowser.yaml ./mods/cnc/chrome/missionbrowser.yaml + ./mods/cnc/chrome/editor.yaml Voices: ./mods/cnc/audio/voices.yaml diff --git a/mods/d2k/chrome/ingame-menu.yaml b/mods/d2k/chrome/ingame-menu.yaml index 61d6286484..24837fbe63 100644 --- a/mods/d2k/chrome/ingame-menu.yaml +++ b/mods/d2k/chrome/ingame-menu.yaml @@ -53,6 +53,13 @@ Container@INGAME_MENU: Height: 30 Text: Surrender Font: Bold + Button@SAVE_MAP: + X: (PARENT_RIGHT - WIDTH)/2 + Y: 180 + Width: 140 + Height: 30 + Text: Save Map + Font: Bold Button@ABORT_MISSION: X: (PARENT_RIGHT - WIDTH)/2 Y: 220 @@ -60,3 +67,10 @@ Container@INGAME_MENU: Height: 30 Text: Abort Mission Font: Bold + Button@EXIT_EDITOR: + X: (PARENT_RIGHT - WIDTH)/2 + Y: 220 + Width: 140 + Height: 30 + Text: Exit Map Editor + Font: Bold diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index a419c758b5..76f981f112 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -100,6 +100,7 @@ ChromeLayout: ./mods/ra/chrome/assetbrowser.yaml ./mods/ra/chrome/missionbrowser.yaml ./mods/ra/chrome/confirmation-dialogs.yaml + ./mods/ra/chrome/editor.yaml Weapons: ./mods/d2k/weapons.yaml diff --git a/mods/d2k/rules/arrakis.yaml b/mods/d2k/rules/arrakis.yaml index fa648ea655..0b63cb91f1 100644 --- a/mods/d2k/rules/arrakis.yaml +++ b/mods/d2k/rules/arrakis.yaml @@ -76,4 +76,5 @@ sietch: Power: Amount: 0 ProvidesPrerequisite@buildingname: + -WithCrumbleOverlay: diff --git a/mods/ra/chrome/editor.yaml b/mods/ra/chrome/editor.yaml new file mode 100644 index 0000000000..c4896664d5 --- /dev/null +++ b/mods/ra/chrome/editor.yaml @@ -0,0 +1,367 @@ +Background@NEW_MAP_BG: + Logic: NewMapLogic + X: (WINDOW_RIGHT - WIDTH)/2 + Y: (WINDOW_BOTTOM - HEIGHT)/2 + Width: 300 + Height: 200 + Children: + Label@LABEL_TITLE: + X: 0 + Y: 20 + Width: 300 + Height: 25 + Text: New Map + Align: Center + Font: Bold + Label@TILESET_LABEL: + X: 5 + Y: 59 + Width: 95 + Height: 25 + Align: Right + Text: Tileset: + DropDownButton@TILESET: + X: 105 + Y: 60 + Width: 150 + Height: 25 + Label@WIDTH_LABEL: + X: 5 + Y: 99 + Width: 95 + Height: 25 + Align: Right + Text: Width: + TextField@WIDTH: + X: 105 + Y: 100 + Width: 50 + MaxLength: 3 + Height: 25 + Text: 128 + Label@HEIGHT_LABEL: + X: 115 + Y: 99 + Width: 95 + Height: 25 + Align: Right + Text: Height: + TextField@HEIGHT: + X: 215 + Y: 100 + Width: 50 + MaxLength: 3 + Height: 25 + Text: 128 + Button@CREATE_BUTTON: + X: 15 + Y: PARENT_BOTTOM - 45 + Width: 120 + Height: 25 + Text: Create + Font: Bold + Key: return + Button@CANCEL_BUTTON: + X: PARENT_RIGHT - 15 - WIDTH + Y: PARENT_BOTTOM - 45 + Width: 120 + Height: 25 + Text: Cancel + Font: Bold + Key: escape + +Background@SAVE_MAP_PANEL: + Logic: SaveMapLogic + X: (WINDOW_RIGHT - WIDTH)/2 + Y: (WINDOW_BOTTOM - HEIGHT)/2 + Width: 350 + Height: 350 + Children: + Label@LABEL_TITLE: + X: (PARENT_RIGHT - WIDTH)/2 + Y: 20 + Width: 250 + Height: 25 + Text: Save Map + Align: Center + Font: Bold + Label@TITLE_LABEL: + X: 10 + Y: 59 + Width: 95 + Height: 25 + Align: Right + Text: Title: + TextField@TITLE: + X: 110 + Y: 60 + Width: 210 + MaxLength: 50 + Height: 25 + Label@DESCRIPTION_LABEL: + X: 10 + Y: 99 + Width: 95 + Height: 25 + Align: Right + Text: Description: + TextField@DESCRIPTION: + X: 110 + Y: 100 + Width: 210 + MaxLength: 50 + Height: 25 + Label@AUTHOR_LABEL: + X: 10 + Y: 139 + Width: 95 + Height: 25 + Align: Right + Text: Author: + TextField@AUTHOR: + X: 110 + Y: 140 + Width: 210 + MaxLength: 50 + Height: 25 + Label@CLASS_LABEL: + X: 10 + Y: 180 + Width: 95 + Height: 25 + Align: Right + Text: Class: + DropDownButton@CLASS_DROPDOWN: + X: 110 + Y: 180 + Width: 210 + Height: 25 + Label@PATH_LABEL: + X: 10 + Y: 219 + Width: 40 + Height: 25 + Align: Right + Text: Path: + DropDownButton@PATH_DROPDOWN: + X: 60 + Y: 220 + Width: 270 + Height: 25 + Label@FILENAME_LABEL: + X: 10 + Y: 259 + Width: 40 + Height: 25 + Align: Right + Text: File: + TextField@FILENAME: + X: 60 + Y: 260 + Width: 270 + Height: 25 + Button@CLOSE: + X: 30 + Y: 300 + Width: 100 + Height: 25 + Text: Close + Font: Bold + Key: escape + Button@SAVE: + X: 210 + Y: 300 + Width: 100 + Height: 25 + Text: Save + Font: Bold + +Container@EDITOR_ROOT: + Logic: LoadMapEditorLogic + Children: + Container@WORLD_ROOT: + Container@MENU_ROOT: + TooltipContainer@TOOLTIP_CONTAINER: + +Container@EDITOR_WORLD_ROOT: + Logic: LoadIngamePerfLogic, MapEditorLogic + Children: + Container@PERF_ROOT: + ViewportController: + X: 0 + Y: 0 + Width: WINDOW_RIGHT + Height: WINDOW_BOTTOM + TooltipContainer: TOOLTIP_CONTAINER + EditorViewportController@MAP_EDITOR: + Width: WINDOW_RIGHT + Height: WINDOW_BOTTOM + TooltipContainer: TOOLTIP_CONTAINER + TooltipTemplate: SIMPLE_TOOLTIP + Children: + ActorPreview@DRAG_ACTOR_PREVIEW: + Visible: false + TerrainTemplatePreview@DRAG_TILE_PREVIEW: + Visible: false + Sprite@DRAG_LAYER_PREVIEW: + Visible: false + Background@RADAR_BG: + X: WINDOW_RIGHT-255 + Y: 5 + Width: 250 + Height: 250 + Children: + Radar@INGAME_RADAR: + X: 10 + Y: 10 + Width: PARENT_RIGHT-19 + Height: PARENT_BOTTOM-19 + Container@TILE_WIDGETS: + Logic: TileSelectorLogic + Children: + Background@TILES_BG: + X: WINDOW_RIGHT-250 + Y: 290 + Width: 240 + Height: 360 + Children: + DropDownButton@TILE_CATEGORY: + X: 10 + Y: 10 + Width: 220 + Height: 25 + Font: Bold + ScrollPanel@TILETEMPLATE_LIST: + X: 10 + Y: 35 + Width: PARENT_RIGHT-20 + Height: PARENT_BOTTOM-45 + ItemSpacing: 4 + Children: + ScrollItem@TILEPREVIEW_TEMPLATE: + Visible: false + Width: PARENT_RIGHT - 35 + TooltipContainer: TOOLTIP_CONTAINER + Children: + TerrainTemplatePreview@TILE_PREVIEW: + X: 4 + Y: 4 + Container@LAYER_WIDGETS: + Visible: false + Logic: LayerSelectorLogic + Children: + Background@LAYERS_BG: + X: WINDOW_RIGHT-250 + Y: 290 + Width: 240 + Height: 360 + Children: + ScrollPanel@LAYERTEMPLATE_LIST: + X: 10 + Y: 10 + Width: PARENT_RIGHT-20 + Height: PARENT_BOTTOM-20 + ItemSpacing: 4 + Children: + ScrollItem@LAYERPREVIEW_TEMPLATE: + Visible: false + IgnoreChildMouseOver: true + TooltipContainer: TOOLTIP_CONTAINER + Children: + Sprite@LAYER_PREVIEW: + X: 4 + Y: 4 + Visible: false + Container@ACTOR_WIDGETS: + Visible: false + Logic: ActorSelectorLogic + Children: + Background@ACTORS_BG: + X: WINDOW_RIGHT-250 + Y: 290 + Width: 240 + Height: 360 + Children: + DropDownButton@OWNERS_DROPDOWN: + X: 10 + Y: 10 + Width: 220 + Height: 25 + Font: Bold + ScrollPanel@ACTORTEMPLATE_LIST: + X: 10 + Y: 35 + Width: PARENT_RIGHT-20 + Height: PARENT_BOTTOM-45 + ItemSpacing: 4 + Children: + ScrollItem@ACTORPREVIEW_TEMPLATE: + Visible: false + Width: PARENT_RIGHT - 35 + TooltipContainer: TOOLTIP_CONTAINER + IgnoreChildMouseOver: true + Children: + ActorPreview@ACTOR_PREVIEW: + X: 4 + Y: 4 + Visible: true + Container@MAP_EDITOR_TAB_CONTAINER: + Logic: MapEditorTabsLogic + X: WINDOW_RIGHT-245 + Y: 260 + Width: 240 + Height: 25 + Children: + Button@TILES_TAB: + X: 0 + Width: 70 + Height: 25 + Text: Tiles + Font: Bold + Button@OVERLAYS_TAB: + X: 70 + Width: 90 + Height: 25 + Text: Overlays + Font: Bold + Button@ACTORS_TAB: + X: 160 + Width: 70 + Height: 25 + Text: Actors + Font: Bold + MenuButton@OPTIONS_BUTTON: + Logic: MenuButtonsChromeLogic + MenuContainer: INGAME_MENU + HideIngameUI: true + Pause: true + Width: 160 + Height: 25 + Text: Menu + TooltipText: Menu + TooltipContainer: TOOLTIP_CONTAINER + Font: Bold + Key: escape + Button@GRID_BUTTON: + X: 180 + Width: 160 + Height: 25 + Text: Grid + TooltipTemplate: BUTTON_TOOLTIP + TooltipText: Toggle the terrain grid + TooltipContainer: TOOLTIP_CONTAINER + Font: Bold + Key: f1 + Label@ZOOM_LABEL: + X: 345 + Width: 50 + Height: 25 + Text: Zoom: + Align: Right + Font: Bold + Contrast: true + DropDownButton@ZOOM_BUTTON: + X: 400 + Width: 70 + Height: 25 + Font: Bold \ No newline at end of file diff --git a/mods/ra/chrome/ingame-menu.yaml b/mods/ra/chrome/ingame-menu.yaml index 19783bc110..f42993f72f 100644 --- a/mods/ra/chrome/ingame-menu.yaml +++ b/mods/ra/chrome/ingame-menu.yaml @@ -66,6 +66,13 @@ Container@INGAME_MENU: Height: 30 Text: Surrender Font: Bold + Button@SAVE_MAP: + X: (PARENT_RIGHT - WIDTH)/2 + Y: 180 + Width: 140 + Height: 30 + Text: Save Map + Font: Bold Button@ABORT_MISSION: X: (PARENT_RIGHT - WIDTH)/2 Y: 220 @@ -73,3 +80,10 @@ Container@INGAME_MENU: Height: 30 Text: Abort Mission Font: Bold + Button@EXIT_EDITOR: + X: (PARENT_RIGHT - WIDTH)/2 + Y: 220 + Width: 140 + Height: 30 + Text: Exit Map Editor + Font: Bold diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index c0b1c1e584..1322ac5bcb 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -114,6 +114,7 @@ ChromeLayout: ./mods/ra/chrome/assetbrowser.yaml ./mods/ra/chrome/missionbrowser.yaml ./mods/ra/chrome/confirmation-dialogs.yaml + ./mods/ra/chrome/editor.yaml Weapons: ./mods/ra/weapons/explosions.yaml diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index 331a76b933..eca1a34a79 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -168,6 +168,7 @@ ChromeLayout: ./mods/ra/chrome/assetbrowser.yaml ./mods/ra/chrome/missionbrowser.yaml ./mods/ra/chrome/confirmation-dialogs.yaml + ./mods/ra/chrome/editor.yaml Voices: ./mods/ts/audio/voices.yaml