Merge pull request #8065 from pchote/ingame-map-editor

Add the new map editor.
This commit is contained in:
Pavel Penev
2015-05-18 19:16:16 +03:00
61 changed files with 3315 additions and 140 deletions

View File

@@ -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<EditorActorLayer>();
Actor = actor;
this.owner = owner;
preview = editorWidget.Get<ActorPreviewWidget>("DRAG_ACTOR_PREVIEW");
preview.GetScale = () => worldRenderer.Viewport.Zoom;
preview.IsVisible = () => editorWidget.CurrentBrush == this;
var buildingInfo = actor.Traits.GetOrDefault<BuildingInfo>();
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<IOccupySpaceInfo>();
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<IFacingInfo>())
initDict.Add(new FacingInit(facing));
if (Actor.Traits.Contains<TurretedInfo>())
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);
}
}
}

View File

@@ -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<int, ResourceType> resources;
public EditorDefaultBrush(EditorViewportControllerWidget editorWidget, WorldRenderer wr)
{
this.editorWidget = editorWidget;
worldRenderer = wr;
world = wr.World;
editorLayer = world.WorldActor.Trait<EditorActorLayer>();
resources = world.WorldActor.TraitsImplementing<ResourceType>()
.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<FacingInit>();
if (facing != null)
underCursor.ReplaceInit(new FacingInit((facing.Value(world) + 32) % 256));
else if (underCursor.Info.Traits.WithInterface<UsesInit<FacingInit>>().Any())
underCursor.ReplaceInit(new FacingInit(32));
var turret = underCursor.Init<TurretFacingInit>();
if (turret != null)
underCursor.ReplaceInit(new TurretFacingInit((turret.Value(world) + 32) % 256));
else if (underCursor.Info.Traits.WithInterface<UsesInit<TurretFacingInit>>().Any())
underCursor.ReplaceInit(new TurretFacingInit(32));
}
}
return true;
}
public void Tick() { }
}
}

View File

@@ -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<SpriteWidget>("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;
}
}
}

View File

@@ -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<TerrainTemplatePreviewWidget>("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);
}
}
}

View File

@@ -539,6 +539,7 @@
<Compile Include="Widgets\Logic\Ingame\IngameRadarDisplayLogic.cs" />
<Compile Include="Widgets\Logic\Ingame\LeaveMapLogic.cs" />
<Compile Include="Widgets\Logic\Ingame\LoadIngamePlayerOrObserverUILogic.cs" />
<Compile Include="Widgets\Logic\Ingame\LoadMapEditorLogic.cs" />
<Compile Include="Widgets\Logic\Ingame\ObserverShroudSelectorLogic.cs" />
<Compile Include="Widgets\Logic\Ingame\ObserverStatsLogic.cs" />
<Compile Include="Widgets\Logic\Ingame\OrderButtonsChromeLogic.cs" />
@@ -559,6 +560,13 @@
<Compile Include="Widgets\Logic\Lobby\LobbyUtils.cs" />
<Compile Include="Widgets\Logic\Lobby\SpawnSelectorTooltipLogic.cs" />
<Compile Include="Widgets\Logic\MainMenuLogic.cs" />
<Compile Include="Widgets\Logic\Editor\ActorSelectorLogic.cs" />
<Compile Include="Widgets\Logic\Editor\LayerSelectorLogic.cs" />
<Compile Include="Widgets\Logic\Editor\MapEditorLogic.cs" />
<Compile Include="Widgets\Logic\Editor\MapEditorTabsLogic.cs" />
<Compile Include="Widgets\Logic\Editor\NewMapLogic.cs" />
<Compile Include="Widgets\Logic\Editor\SaveMapLogic.cs" />
<Compile Include="Widgets\Logic\Editor\TileSelectorLogic.cs" />
<Compile Include="Widgets\Logic\MapChooserLogic.cs" />
<Compile Include="Widgets\Logic\MissionBrowserLogic.cs" />
<Compile Include="Widgets\Logic\ModBrowserLogic.cs" />
@@ -627,6 +635,14 @@
<Compile Include="Traits\Plug.cs" />
<Compile Include="Widgets\Logic\Ingame\MenuButtonsChromeLogic.cs" />
<Compile Include="Widgets\Logic\Ingame\LoadIngamePerfLogic.cs" />
<Compile Include="Traits\World\EditorActorLayer.cs" />
<Compile Include="EditorBrushes\EditorActorBrush.cs" />
<Compile Include="EditorBrushes\EditorTileBrush.cs" />
<Compile Include="EditorBrushes\EditorResourceBrush.cs" />
<Compile Include="EditorBrushes\EditorDefaultBrush.cs" />
<Compile Include="Traits\World\EditorActorPreview.cs" />
<Compile Include="Traits\World\EditorResourceLayer.cs" />
<Compile Include="Widgets\EditorViewportControllerWidget.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
@@ -641,4 +657,7 @@ cd "$(SolutionDir)"</PostBuildEvent>
<Target Name="AfterBuild">
</Target>
-->
<ItemGroup>
<Folder Include="EditorBrushes\" />
</ItemGroup>
</Project>

View File

@@ -43,6 +43,9 @@ namespace OpenRA.Mods.Common.Traits
public virtual object Create(ActorInitializer init) { return new Aircraft(init, this); }
public int GetInitialFacing() { return InitialFacing; }
public WRange GetCruiseAltitude() { return CruiseAltitude; }
public IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any) { return new ReadOnlyDictionary<CPos, SubCell>(); }
bool IOccupySpaceInfo.SharesCell { get { return false; } }
}
public class Aircraft : IFacing, IPositionable, ISync, INotifyKilled, IIssueOrder, IOrderVoice, INotifyAddedToWorld, INotifyRemovedFromWorld

View File

@@ -104,6 +104,16 @@ namespace OpenRA.Mods.Common.Traits
.Any(b => Math.Abs(a.X - b.X) <= Adjacent
&& Math.Abs(a.Y - b.Y) <= Adjacent));
}
public IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos topLeft, SubCell subCell = SubCell.Any)
{
var occupied = FootprintUtils.UnpathableTiles(info.Name, this, topLeft)
.ToDictionary(c => c, c => SubCell.FullCell);
return new ReadOnlyDictionary<CPos, SubCell>(occupied);
}
bool IOccupySpaceInfo.SharesCell { get { return false; } }
}
public class Building : IOccupySpace, INotifySold, INotifyTransform, ISync, INotifyCreated, INotifyAddedToWorld, INotifyRemovedFromWorld

View File

@@ -28,6 +28,14 @@ namespace OpenRA.Mods.Common.Traits
public readonly string CrushClass = "crate";
public object Create(ActorInitializer init) { return new Crate(init, this); }
public IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any)
{
var occupied = new Dictionary<CPos, SubCell>() { { location, SubCell.FullCell } };
return new ReadOnlyDictionary<CPos, SubCell>(occupied);
}
bool IOccupySpaceInfo.SharesCell { get { return false; } }
}
class Crate : ITick, IPositionable, ICrushable, ISync, INotifyParachuteLanded, INotifyAddedToWorld, INotifyRemovedFromWorld

View File

@@ -24,6 +24,14 @@ namespace OpenRA.Mods.Common.Traits
public object Create(ActorInitializer init) { return new Husk(init, this); }
public int GetInitialFacing() { return 128; }
public IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any)
{
var occupied = new Dictionary<CPos, SubCell>() { { location, SubCell.FullCell } };
return new ReadOnlyDictionary<CPos, SubCell>(occupied);
}
bool IOccupySpaceInfo.SharesCell { get { return false; } }
}
public class Husk : IPositionable, IFacing, ISync, INotifyCreated, INotifyAddedToWorld, INotifyRemovedFromWorld, IDisable

View File

@@ -18,6 +18,16 @@ namespace OpenRA.Mods.Common.Traits
{
public readonly bool OccupiesSpace = true;
public object Create(ActorInitializer init) { return new Immobile(init, this); }
public IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any)
{
var occupied = OccupiesSpace ? new Dictionary<CPos, SubCell>() { { location, SubCell.FullCell } } :
new Dictionary<CPos, SubCell>();
return new ReadOnlyDictionary<CPos, SubCell>(occupied);
}
bool IOccupySpaceInfo.SharesCell { get { return false; } }
}
class Immobile : IOccupySpace, ISync, INotifyAddedToWorld, INotifyRemovedFromWorld

View File

@@ -295,6 +295,13 @@ namespace OpenRA.Mods.Common.Traits
}
public int GetInitialFacing() { return InitialFacing; }
public IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any)
{
return new ReadOnlyDictionary<CPos, SubCell>(new Dictionary<CPos, SubCell>() { { location, subCell } });
}
bool IOccupySpaceInfo.SharesCell { get { return SharesCell; } }
}
public class Mobile : IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, IFacing, ISync, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove

View File

@@ -64,9 +64,15 @@ namespace OpenRA.Mods.Common.Traits
var facings = 0;
var body = init.Actor.Traits.GetOrDefault<BodyOrientationInfo>();
if (body != null)
facings = body.QuantizedFacings == -1 ?
init.Actor.Traits.Get<IQuantizeBodyOrientationInfo>().QuantizedBodyFacings(init.Actor, sequenceProvider, race) :
body.QuantizedFacings;
{
facings = body.QuantizedFacings;
if (facings == -1)
{
var qbo = init.Actor.Traits.GetOrDefault<IQuantizeBodyOrientationInfo>();
facings = qbo != null ? qbo.QuantizedBodyFacings(init.Actor, sequenceProvider, race) : 1;
}
}
foreach (var spi in init.Actor.Traits.WithInterface<IRenderActorPreviewSpritesInfo>())
foreach (var preview in spi.RenderPreviewSprites(init, this, image, facings, palette))

View File

@@ -0,0 +1,268 @@
#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.IO;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Required for the map editor to work. Attach this to the world actor.")]
public class EditorActorLayerInfo : ITraitInfo
{
[Desc("Size of partition bins (world pixels)")]
public readonly int BinSize = 250;
public object Create(ActorInitializer init) { return new EditorActorLayer(init.Self, this); }
}
public class EditorActorLayer : IWorldLoaded, ITickRender, IRender, IRadarSignature, ICreatePlayers
{
readonly EditorActorLayerInfo info;
readonly Dictionary<string, EditorActorPreview> previews = new Dictionary<string, EditorActorPreview>();
readonly Dictionary<CPos, List<EditorActorPreview>> cellMap = new Dictionary<CPos, List<EditorActorPreview>>();
SpatiallyPartitioned<EditorActorPreview> screenMap;
WorldRenderer worldRenderer;
public MapPlayers Players { get; private set; }
public EditorActorLayer(Actor self, EditorActorLayerInfo info)
{
this.info = info;
}
public void CreatePlayers(World w)
{
if (w.Type != WorldType.Editor)
return;
Players = new MapPlayers(w.Map.PlayerDefinitions);
}
public void WorldLoaded(World world, WorldRenderer wr)
{
if (world.Type != WorldType.Editor)
return;
worldRenderer = wr;
foreach (var pr in Players.Players.Values)
wr.UpdatePalettesForPlayer(pr.Name, pr.Color, false);
var ts = Game.ModData.Manifest.TileSize;
var width = world.Map.MapSize.X * ts.Width;
var height = world.Map.MapSize.Y * ts.Height;
screenMap = new SpatiallyPartitioned<EditorActorPreview>(width, height, info.BinSize);
foreach (var kv in world.Map.ActorDefinitions)
Add(kv.Key, new ActorReference(kv.Value.Value, kv.Value.ToDictionary()), true);
// Update neighbours in one pass
foreach (var p in previews.Values)
UpdateNeighbours(p.Footprint);
}
public void TickRender(WorldRenderer wr, Actor self)
{
if (wr.World.Type != WorldType.Editor)
return;
foreach (var kv in previews.Values)
kv.Tick();
}
static readonly IEnumerable<IRenderable> NoRenderables = Enumerable.Empty<IRenderable>();
public virtual IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
{
if (wr.World.Type != WorldType.Editor)
return NoRenderables;
return PreviewsInBox(wr.Viewport.TopLeft, wr.Viewport.BottomRight)
.SelectMany(p => p.Render());
}
public EditorActorPreview Add(ActorReference reference) { return Add(NextActorName(), reference); }
EditorActorPreview Add(string id, ActorReference reference, bool initialSetup = false)
{
var owner = Players.Players[reference.InitDict.Get<OwnerInit>().PlayerName];
var preview = new EditorActorPreview(worldRenderer, id, reference, owner);
previews.Add(id, preview);
screenMap.Add(preview, preview.Bounds);
foreach (var kv in preview.Footprint)
{
List<EditorActorPreview> list;
if (!cellMap.TryGetValue(kv.Key, out list))
{
list = new List<EditorActorPreview>();
cellMap.Add(kv.Key, list);
}
list.Add(preview);
}
if (!initialSetup)
{
UpdateNeighbours(preview.Footprint);
if (reference.Type == "mpspawn")
SyncMultiplayerCount();
}
return preview;
}
public void Remove(EditorActorPreview preview)
{
previews.Remove(preview.ID);
screenMap.Remove(preview);
foreach (var kv in preview.Footprint)
{
List<EditorActorPreview> list;
if (!cellMap.TryGetValue(kv.Key, out list))
continue;
list.Remove(preview);
if (!list.Any())
cellMap.Remove(kv.Key);
}
UpdateNeighbours(preview.Footprint);
if (preview.Info.Name == "mpspawn")
SyncMultiplayerCount();
}
void SyncMultiplayerCount()
{
var newCount = previews.Count(p => p.Value.Info.Name == "mpspawn");
var mp = Players.Players.Where(p => p.Key.StartsWith("Multi")).ToList();
foreach (var kv in mp)
{
var name = kv.Key;
var index = int.Parse(name.Substring(5));
if (index >= newCount)
{
Players.Players.Remove(name);
worldRenderer.World.Players.RemoveAll(pp => pp.InternalName == name);
}
}
for (var index = 0; index < newCount; index++)
{
if (Players.Players.ContainsKey("Multi{0}".F(index)))
continue;
var pr = new PlayerReference
{
Name = "Multi{0}".F(index),
Race = "Random",
Playable = true,
Enemies = new[] { "Creeps" }
};
Players.Players.Add(pr.Name, pr);
worldRenderer.UpdatePalettesForPlayer(pr.Name, pr.Color, true);
}
}
void UpdateNeighbours(IReadOnlyDictionary<CPos, SubCell> footprint)
{
// Include actors inside the footprint too
var cells = OpenRA.Traits.Util.ExpandFootprint(footprint.Keys, true);
foreach (var p in cells.SelectMany(c => PreviewsAt(c)))
p.ReplaceInit(new RuntimeNeighbourInit(NeighbouringPreviews(p.Footprint)));
}
Dictionary<CPos, string[]> NeighbouringPreviews(IReadOnlyDictionary<CPos, SubCell> footprint)
{
var cells = OpenRA.Traits.Util.ExpandFootprint(footprint.Keys, true).Except(footprint.Keys);
return cells.ToDictionary(c => c, c => PreviewsAt(c).Select(p => p.Info.Name).ToArray());
}
public IEnumerable<EditorActorPreview> PreviewsInBox(int2 a, int2 b)
{
return screenMap.InBox(Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)));
}
public IEnumerable<EditorActorPreview> PreviewsInBox(Rectangle r)
{
return screenMap.InBox(r);
}
public IEnumerable<EditorActorPreview> PreviewsAt(CPos cell)
{
List<EditorActorPreview> list;
if (cellMap.TryGetValue(cell, out list))
return list;
return Enumerable.Empty<EditorActorPreview>();
}
public SubCell FreeSubCellAt(CPos cell)
{
var map = worldRenderer.World.Map;
var previews = PreviewsAt(cell).ToList();
if (!previews.Any())
return map.DefaultSubCell;
for (var i = (int)SubCell.First; i < map.SubCellOffsets.Length; i++)
if (!previews.Any(p => p.Footprint[cell] == (SubCell)i))
return (SubCell)i;
return SubCell.Invalid;
}
public IEnumerable<EditorActorPreview> PreviewsAt(int2 worldPx)
{
return screenMap.At(worldPx);
}
string NextActorName()
{
var id = previews.Count();
var possibleName = "Actor" + id.ToString();
while (previews.ContainsKey(possibleName))
{
id++;
possibleName = "Actor" + id.ToString();
}
return possibleName;
}
public List<MiniYamlNode> Save()
{
var nodes = new List<MiniYamlNode>();
foreach (var a in previews)
nodes.Add(new MiniYamlNode(a.Key, a.Value.Save()));
return nodes;
}
public IEnumerable<Pair<CPos, Color>> RadarSignatureCells(Actor self)
{
return cellMap.SelectMany(c => c.Value.Select(p => Pair.New(c.Key, p.Owner.Color.RGB)));
}
}
}

View File

@@ -0,0 +1,164 @@
#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.IO;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
public class EditorActorPreview
{
public readonly string Tooltip;
public readonly string ID;
public readonly ActorInfo Info;
public readonly PlayerReference Owner;
public readonly WPos CenterPosition;
public readonly IReadOnlyDictionary<CPos, SubCell> Footprint;
public readonly Rectangle Bounds;
public SubCell SubCell { get; private set; }
readonly ActorReference actor;
readonly WorldRenderer worldRenderer;
IActorPreview[] previews;
public EditorActorPreview(WorldRenderer worldRenderer, string id, ActorReference actor, PlayerReference owner)
{
ID = id;
this.actor = actor;
this.Owner = owner;
this.worldRenderer = worldRenderer;
if (!actor.InitDict.Contains<RaceInit>())
actor.InitDict.Add(new RaceInit(owner.Race));
if (!actor.InitDict.Contains<OwnerInit>())
actor.InitDict.Add(new OwnerInit(owner.Name));
var world = worldRenderer.World;
if (!world.Map.Rules.Actors.TryGetValue(actor.Type.ToLowerInvariant(), out Info))
throw new InvalidDataException("Actor {0} of unknown type {1}".F(id, actor.Type.ToLowerInvariant()));
CenterPosition = PreviewPosition(world, actor.InitDict);
var location = actor.InitDict.Get<LocationInit>().Value(worldRenderer.World);
var ios = Info.Traits.GetOrDefault<IOccupySpaceInfo>();
var subCellInit = actor.InitDict.GetOrDefault<SubCellInit>();
var subCell = subCellInit != null ? subCellInit.Value(worldRenderer.World) : SubCell.Any;
if (ios != null)
Footprint = ios.OccupiedCells(Info, location, subCell);
else
{
var footprint = new Dictionary<CPos, SubCell>() { { location, SubCell.FullCell } };
Footprint = new ReadOnlyDictionary<CPos, SubCell>(footprint);
}
var tooltipInfo = Info.Traits.GetOrDefault<ITooltipInfo>();
Tooltip = "{0} ({1})".F(tooltipInfo != null ? tooltipInfo.TooltipForPlayerStance(Stance.None) : Info.Name, ID);
GeneratePreviews();
// Bounds are fixed from the initial render.
// If this is a problem, then we may need to fetch the area from somewhere else
var r = previews
.SelectMany(p => p.Render(worldRenderer, CenterPosition))
.Select(rr => rr.PrepareRender(worldRenderer));
if (r.Any())
{
Bounds = r.First().ScreenBounds(worldRenderer);
foreach (var rr in r.Skip(1))
Bounds = Rectangle.Union(Bounds, rr.ScreenBounds(worldRenderer));
}
}
public void Tick()
{
foreach (var p in previews)
p.Tick();
}
public IEnumerable<IRenderable> Render()
{
return previews.SelectMany(p => p.Render(worldRenderer, CenterPosition));
}
public void ReplaceInit<T>(T init)
{
var original = actor.InitDict.GetOrDefault<T>();
if (original != null)
actor.InitDict.Remove(original);
actor.InitDict.Add(init);
GeneratePreviews();
}
public T Init<T>()
{
return actor.InitDict.GetOrDefault<T>();
}
public MiniYaml Save()
{
Func<object, bool> saveInit = init =>
{
var race = init as RaceInit;
if (race != null && race.Race == Owner.Race)
return false;
// TODO: Other default values will need to be filtered
// here after we have built a properties panel
return true;
};
return actor.Save(saveInit);
}
WPos PreviewPosition(World world, TypeDictionary init)
{
if (init.Contains<CenterPositionInit>())
return init.Get<CenterPositionInit>().Value(world);
if (init.Contains<LocationInit>())
{
var cell = init.Get<LocationInit>().Value(world);
var offset = WVec.Zero;
var subCellInit = actor.InitDict.GetOrDefault<SubCellInit>();
var subCell = subCellInit != null ? subCellInit.Value(worldRenderer.World) : SubCell.Any;
var buildingInfo = Info.Traits.GetOrDefault<BuildingInfo>();
if (buildingInfo != null)
offset = FootprintUtils.CenterOffset(world, buildingInfo);
return world.Map.CenterOfSubCell(cell, subCell) + offset;
}
else
throw new InvalidDataException("Actor {0} must define Location or CenterPosition".F(ID));
}
void GeneratePreviews()
{
var init = new ActorPreviewInitializer(Info, worldRenderer, actor.InitDict);
previews = Info.Traits.WithInterface<IRenderActorPreviewInfo>()
.SelectMany(rpi => rpi.RenderPreview(init))
.ToArray();
}
}
}

View File

@@ -0,0 +1,155 @@
#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.IO;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
using CellContents = ResourceLayer.CellContents;
[Desc("Required for the map editor to work. Attach this to the world actor.")]
public class EditorResourceLayerInfo : ITraitInfo, Requires<ResourceTypeInfo>
{
public virtual object Create(ActorInitializer init) { return new EditorResourceLayer(init.Self); }
}
public class EditorResourceLayer : IWorldLoaded, IRenderOverlay
{
protected readonly Map Map;
protected readonly TileSet Tileset;
protected readonly Dictionary<int, ResourceType> Resources;
protected readonly CellLayer<CellContents> Tiles;
protected readonly HashSet<CPos> Dirty = new HashSet<CPos>();
public EditorResourceLayer(Actor self)
{
if (self.World.Type != WorldType.Editor)
return;
Map = self.World.Map;
Tileset = self.World.TileSet;
Tiles = new CellLayer<CellContents>(Map);
Resources = self.TraitsImplementing<ResourceType>()
.ToDictionary(r => r.Info.ResourceType, r => r);
Map.MapResources.Value.CellEntryChanged += UpdateCell;
}
public void WorldLoaded(World w, WorldRenderer wr)
{
if (w.Type != WorldType.Editor)
return;
foreach (var cell in Map.Cells)
UpdateCell(cell);
}
public void UpdateCell(CPos cell)
{
var uv = cell.ToMPos(Map);
var tile = Map.MapResources.Value[uv];
ResourceType type;
if (Resources.TryGetValue(tile.Type, out type))
{
Tiles[uv] = new CellContents
{
Type = type,
Variant = ChooseRandomVariant(type),
};
Map.CustomTerrain[uv] = Tileset.GetTerrainIndex(type.Info.TerrainType);
}
else
{
Tiles[uv] = CellContents.Empty;
Map.CustomTerrain[uv] = byte.MaxValue;
}
// Ingame resource rendering is a giant hack (#6395),
// so we must also touch all the neighbouring tiles
Dirty.Add(cell);
foreach (var d in CVec.Directions)
{
var c = cell + d;
if (Map.Contains(c))
Dirty.Add(c);
}
}
protected virtual string ChooseRandomVariant(ResourceType t)
{
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 (type == null)
{
t.Sprite = null;
return t;
}
// Set density based on the number of neighboring resources
t.Density = ResourceDensityAt(c);
var sprites = type.Variants[t.Variant];
var frame = int2.Lerp(0, sprites.Length - 1, t.Density - 1, type.Info.MaxDensity);
t.Sprite = sprites[frame];
return t;
}
public void Render(WorldRenderer wr)
{
if (wr.World.Type != WorldType.Editor)
return;
foreach (var c in Dirty)
Tiles[c] = UpdateDirtyTile(c);
Dirty.Clear();
foreach (var uv in wr.Viewport.VisibleCells.MapCoords)
{
var t = Tiles[uv];
if (t.Sprite != null)
new SpriteRenderable(t.Sprite, wr.World.Map.CenterOfCell(uv.ToCPos(Map)),
WVec.Zero, -511, t.Type.Palette, 1f, true).Render(wr);
}
}
}
}

View File

@@ -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());
}
}

View File

@@ -248,6 +248,7 @@ namespace OpenRA.Mods.Common.Traits
public struct CellContents
{
public static readonly CellContents Empty = new CellContents();
public ResourceType Type;
public int Density;
public string Variant;

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Renders a debug overlay showing the terrain cells. Attach this to the world actor.")]
public class TerrainGeometryOverlayInfo : TraitInfo<TerrainGeometryOverlay> { }
public class TerrainGeometryOverlay : IRenderOverlay, IWorldLoaded, IChatCommand
public class TerrainGeometryOverlay : IPostRender, IWorldLoaded, IChatCommand
{
const string CommandName = "terrainoverlay";
const string CommandDesc = "Toggles the terrain geometry overlay";
@@ -46,7 +46,7 @@ namespace OpenRA.Mods.Common.Traits
Enabled ^= true;
}
public void Render(WorldRenderer wr)
public void RenderAfterWorld(WorldRenderer wr, Actor self)
{
if (!Enabled)
return;

View File

@@ -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<TooltipContainerWidget> 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<TooltipContainerWidget>(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<string> 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();
}
}
}

View File

@@ -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<EditorViewportControllerWidget>("MAP_EDITOR");
ownersDropDown = widget.Get<DropDownButtonWidget>("OWNERS_DROPDOWN");
panel = widget.Get<ScrollPanelWidget>("ACTORTEMPLATE_LIST");
itemTemplate = panel.Get<ScrollItemWidget>("ACTORPREVIEW_TEMPLATE");
panel.Layout = new GridLayout(panel);
var editorLayer = world.WorldActor.Trait<EditorActorLayer>();
selectedOwner = editorLayer.Players.Players.Values.First();
Func<PlayerReference, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
{
var item = ScrollItemWidget.Setup(template, () => selectedOwner == option, () =>
{
selectedOwner = option;
ownersDropDown.Text = selectedOwner.Name;
ownersDropDown.TextColor = selectedOwner.Color.RGB;
IntializeActorPreviews();
});
item.Get<LabelWidget>("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<BridgeInfo>()) // bridge layer takes care about that automatically
continue;
if (!actor.Traits.Contains<IRenderActorPreviewInfo>())
continue;
var filter = actor.Traits.GetOrDefault<EditorTilesetFilterInfo>();
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<ActorPreviewWidget>("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;
}
}
}
}
}

View File

@@ -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<EditorViewportControllerWidget>("MAP_EDITOR");
layerTemplateList = widget.Get<ScrollPanelWidget>("LAYERTEMPLATE_LIST");
layerTemplateList.Layout = new GridLayout(layerTemplateList);
layerPreviewTemplate = layerTemplateList.Get<ScrollItemWidget>("LAYERPREVIEW_TEMPLATE");
IntializeLayerPreview(widget);
}
void IntializeLayerPreview(Widget widget)
{
layerTemplateList.RemoveChildren();
var resources = modRules.Actors["world"].Traits.WithInterface<ResourceTypeInfo>();
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<SpriteWidget>("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);
}
}
}
}

View File

@@ -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<ButtonWidget>("GRID_BUTTON");
var terrainGeometryTrait = world.WorldActor.Trait<TerrainGeometryOverlay>();
if (gridButton != null && terrainGeometryTrait != null)
{
gridButton.OnClick = () => terrainGeometryTrait.Enabled ^= true;
gridButton.IsHighlighted = () => terrainGeometryTrait.Enabled;
}
var zoomDropdown = widget.Get<DropDownButtonWidget>("ZOOM_BUTTON");
if (zoomDropdown != null)
{
var selectedZoom = Game.Settings.Graphics.PixelDouble ? 2f : 1f;
var selectedLabel = selectedZoom.ToString();
Func<float, ScrollItemWidget, ScrollItemWidget> setupItem = (zoom, itemTemplate) =>
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => selectedZoom == zoom,
() => { worldRenderer.Viewport.Zoom = selectedZoom = zoom; selectedLabel = zoom.ToString(); });
var label = zoom.ToString();
item.Get<LabelWidget>("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();
};
}
}
}
}

View File

@@ -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<ButtonWidget>("TILES_TAB");
tilesTab.IsHighlighted = () => menuType == MenuType.Tiles;
tilesTab.OnClick = () => { menuType = MenuType.Tiles; };
var overlaysTab = tabContainer.Get<ButtonWidget>("OVERLAYS_TAB");
overlaysTab.IsHighlighted = () => menuType == MenuType.Layers;
overlaysTab.OnClick = () => { menuType = MenuType.Layers; };
var actorsTab = tabContainer.Get<ButtonWidget>("ACTORS_TAB");
actorsTab.IsHighlighted = () => menuType == MenuType.Actors;
actorsTab.OnClick = () => { menuType = MenuType.Actors; };
var tileContainer = widget.Parent.Get<ContainerWidget>("TILE_WIDGETS");
tileContainer.IsVisible = () => menuType == MenuType.Tiles;
var layerContainer = widget.Parent.Get<ContainerWidget>("LAYER_WIDGETS");
layerContainer.IsVisible = () => menuType == MenuType.Layers;
var actorContainer = widget.Parent.Get<ContainerWidget>("ACTOR_WIDGETS");
actorContainer.IsVisible = () => menuType == MenuType.Actors;
}
}
}

View File

@@ -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<string> onSelect, Ruleset modRules, Widget widget, World world)
{
panel = widget;
panel.Get<ButtonWidget>("CANCEL_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); };
var tilesetDropDown = panel.Get<DropDownButtonWidget>("TILESET");
var tilesets = modRules.TileSets.Select(t => t.Key).ToList();
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
{
var item = ScrollItemWidget.Setup(template,
() => tilesetDropDown.Text == option,
() => { tilesetDropDown.Text = option; });
item.Get<LabelWidget>("LABEL").GetText = () => option;
return item;
};
tilesetDropDown.Text = tilesets.First();
tilesetDropDown.OnClick = () =>
tilesetDropDown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, tilesets, setupItem);
var widthTextField = panel.Get<TextFieldWidget>("WIDTH");
var heightTextField = panel.Get<TextFieldWidget>("HEIGHT");
panel.Get<ButtonWidget>("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);
};
}
}
}

View File

@@ -0,0 +1,141 @@
#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, Map map, EditorActorLayer editorActorLayer)
{
var title = widget.Get<TextFieldWidget>("TITLE");
title.Text = map.Title;
var author = widget.Get<TextFieldWidget>("AUTHOR");
author.Text = map.Author;
// TODO: This should use a multi-line textfield once they exist
var description = widget.Get<TextFieldWidget>("DESCRIPTION");
description.Text = map.Description;
// TODO: This should use a multi-selection dropdown once they exist
var visibilityDropdown = widget.Get<DropDownButtonWidget>("VISIBILITY_DROPDOWN");
{
var mapVisibility = new List<string>(Enum.GetNames(typeof(MapVisibility)));
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
{
var item = ScrollItemWidget.Setup(template,
() => visibilityDropdown.Text == option,
() => { visibilityDropdown.Text = option; });
item.Get<LabelWidget>("LABEL").GetText = () => option;
return item;
};
visibilityDropdown.Text = Enum.GetName(typeof(MapVisibility), map.Visibility);
visibilityDropdown.OnClick = () =>
visibilityDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, mapVisibility, setupItem);
}
var directoryDropdown = widget.Get<DropDownButtonWidget>("DIRECTORY_DROPDOWN");
{
var mapDirectories = Game.ModData.Manifest.MapFolders.Keys.Select(ff =>
{
var f = Platform.UnresolvePath(ff);
if (f.StartsWith("~"))
f = f.Substring(1);
return f;
}).ToList();
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
{
var item = ScrollItemWidget.Setup(template,
() => directoryDropdown.Text == option,
() => directoryDropdown.Text = option);
item.Get<LabelWidget>("LABEL").GetText = () => option;
return item;
};
var mapDirectory = Platform.UnresolvePath(Path.GetDirectoryName(map.Path));
var initialDirectory = mapDirectories.FirstOrDefault(f => f == mapDirectory);
if (initialDirectory == null)
initialDirectory = mapDirectories.First();
directoryDropdown.Text = initialDirectory;
directoryDropdown.OnClick = () =>
directoryDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, mapDirectories, setupItem);
}
var filename = widget.Get<TextFieldWidget>("FILENAME");
filename.Text = Path.GetFileNameWithoutExtension(map.Path);
var fileTypes = new Dictionary<string, string>()
{
{ ".oramap", ".oramap" },
{ "(unpacked)", "" }
};
var typeDropdown = widget.Get<DropDownButtonWidget>("TYPE_DROPDOWN");
{
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
{
var item = ScrollItemWidget.Setup(template,
() => typeDropdown.Text == option,
() => typeDropdown.Text = option);
item.Get<LabelWidget>("LABEL").GetText = () => option;
return item;
};
typeDropdown.Text = Path.GetExtension(map.Path);
if (string.IsNullOrEmpty(typeDropdown.Text))
typeDropdown.Text = fileTypes.First(t => t.Value == "").Key;
typeDropdown.OnClick = () =>
typeDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, fileTypes.Keys, setupItem);
}
var close = widget.Get<ButtonWidget>("BACK_BUTTON");
close.OnClick = () => { Ui.CloseWindow(); onExit(); };
var save = widget.Get<ButtonWidget>("SAVE_BUTTON");
save.OnClick = () =>
{
if (string.IsNullOrEmpty(filename.Text))
return;
map.Title = title.Text;
map.Description = description.Text;
map.Author = author.Text;
map.Visibility = (MapVisibility)Enum.Parse(typeof(MapVisibility), visibilityDropdown.Text);
map.ActorDefinitions = editorActorLayer.Save();
map.PlayerDefinitions = editorActorLayer.Players.ToMiniYaml();
map.RequiresMod = Game.ModData.Manifest.Mod.Id;
var combinedPath = Platform.ResolvePath(Path.Combine(directoryDropdown.Text, filename.Text + fileTypes[typeDropdown.Text]));
map.Save(combinedPath);
// Update the map cache so it can be loaded without restarting the game
Game.ModData.MapCache[map.Uid].UpdateFromMap(map, MapClassification.User);
Console.WriteLine("Saved current map at {0}", combinedPath);
Ui.CloseWindow();
onExit();
};
}
}
}

View File

@@ -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<EditorViewportControllerWidget>("MAP_EDITOR");
panel = widget.Get<ScrollPanelWidget>("TILETEMPLATE_LIST");
itemTemplate = panel.Get<ScrollItemWidget>("TILEPREVIEW_TEMPLATE");
panel.Layout = new GridLayout(panel);
var tileCategorySelector = widget.Get<DropDownButtonWidget>("TILE_CATEGORY");
var categories = tileset.EditorTemplateOrder;
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
{
var item = ScrollItemWidget.Setup(template,
() => tileCategorySelector.Text == option,
() => { tileCategorySelector.Text = option; IntializeTilePreview(widget, worldRenderer, tileset, option); });
item.Get<LabelWidget>("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<TerrainTemplatePreviewWidget>("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);
}
}
}
}

View File

@@ -36,7 +36,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
// TODO: Create a mechanism to do things like this cleaner. Also needed for scripted missions
Action onQuit = () =>
{
Sound.PlayNotification(world.Map.Rules, null, "Speech", "Leave", world.LocalPlayer == null ? null : world.LocalPlayer.Country.Race);
if (world.Type == WorldType.Regular)
Sound.PlayNotification(world.Map.Rules, null, "Speech", "Leave", world.LocalPlayer == null ? null : world.LocalPlayer.Country.Race);
resumeDisabled = true;
var exitDelay = 1200;
@@ -64,18 +66,29 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Action showMenu = () => hideMenu = false;
menu.Get<ButtonWidget>("ABORT_MISSION").OnClick = () =>
var abortMissionButton = menu.Get<ButtonWidget>("ABORT_MISSION");
abortMissionButton.IsVisible = () => world.Type == WorldType.Regular;
abortMissionButton.OnClick = () =>
{
hideMenu = true;
ConfirmationDialogs.PromptConfirmAction("Abort Mission", "Leave this game and return to the menu?", onQuit, showMenu);
};
var exitEditorButton = menu.Get<ButtonWidget>("EXIT_EDITOR");
exitEditorButton.IsVisible = () => world.Type == WorldType.Editor;
exitEditorButton.OnClick = () =>
{
hideMenu = true;
ConfirmationDialogs.PromptConfirmAction("Exit Map Editor", "Exit and lose all unsaved changes?", onQuit, showMenu);
};
Action onSurrender = () =>
{
world.IssueOrder(new Order("Surrender", world.LocalPlayer.PlayerActor, false));
closeMenu();
};
var surrenderButton = menu.Get<ButtonWidget>("SURRENDER");
surrenderButton.IsVisible = () => world.Type == WorldType.Regular;
surrenderButton.IsDisabled = () => (world.LocalPlayer == null || world.LocalPlayer.WinState != WinState.Undefined);
surrenderButton.OnClick = () =>
{
@@ -84,6 +97,19 @@ namespace OpenRA.Mods.Common.Widgets.Logic
};
surrenderButton.IsDisabled = () => world.LocalPlayer == null || world.LocalPlayer.WinState != WinState.Undefined;
var saveMapButton = menu.Get<ButtonWidget>("SAVE_MAP");
saveMapButton.IsVisible = () => world.Type == WorldType.Editor;
saveMapButton.OnClick = () =>
{
hideMenu = true;
Ui.OpenWindow("SAVE_MAP_PANEL", new WidgetArgs()
{
{ "onExit", () => hideMenu = false },
{ "map", world.Map },
{ "editorActorLayer", world.WorldActor.Trait<EditorActorLayer>() }
});
};
menu.Get<ButtonWidget>("MUSIC").OnClick = () =>
{
hideMenu = true;
@@ -111,7 +137,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
resumeButton.OnClick = closeMenu;
var panelRoot = widget.GetOrNull("PANEL_ROOT");
if (panelRoot != null)
if (panelRoot != null && world.Type != WorldType.Editor)
{
var gameInfoPanel = Game.LoadWidget(world, "GAME_INFO_PANEL", panelRoot, new WidgetArgs()
{

View File

@@ -102,8 +102,9 @@ namespace OpenRA.Mods.Common.Widgets
{
leaveButton.Disabled = true;
Sound.PlayNotification(world.Map.Rules, null, "Speech", "Leave",
world.LocalPlayer == null ? null : world.LocalPlayer.Country.Race);
if (world.Type == WorldType.Regular)
Sound.PlayNotification(world.Map.Rules, null, "Speech", "Leave",
world.LocalPlayer == null ? null : world.LocalPlayer.Country.Race);
var exitDelay = 1200;
if (mpe != null)

View File

@@ -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());
}
}
}

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
public class MainMenuLogic
{
protected enum MenuType { Main, Singleplayer, Extras, None }
protected enum MenuType { Main, Singleplayer, Extras, MapEditor, None }
protected MenuType menuType = MenuType.Main;
readonly Widget rootMenu;
@@ -122,6 +122,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
});
};
extrasMenu.Get<ButtonWidget>("MAP_EDITOR_BUTTON").OnClick = () => menuType = MenuType.MapEditor;
var assetBrowserButton = extrasMenu.GetOrNull<ButtonWidget>("ASSETBROWSER_BUTTON");
if (assetBrowserButton != null)
assetBrowserButton.OnClick = () =>
@@ -144,6 +146,43 @@ namespace OpenRA.Mods.Common.Widgets.Logic
extrasMenu.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => menuType = MenuType.Main;
// Map editor menu
var mapEditorMenu = widget.Get("MAP_EDITOR_MENU");
mapEditorMenu.IsVisible = () => menuType == MenuType.MapEditor;
var onSelect = new Action<string>(uid =>
{
RemoveShellmapUI();
LoadMapIntoEditor(Game.ModData.MapCache[uid].Map);
});
var newMapButton = widget.Get<ButtonWidget>("NEW_MAP_BUTTON");
newMapButton.OnClick = () =>
{
menuType = MenuType.None;
Game.OpenWindow("NEW_MAP_BG", new WidgetArgs()
{
{ "onSelect", onSelect },
{ "onExit", () => menuType = MenuType.MapEditor }
});
};
var loadMapButton = widget.Get<ButtonWidget>("LOAD_MAP_BUTTON");
loadMapButton.OnClick = () =>
{
var initialMap = Game.ModData.MapCache.FirstOrDefault();
menuType = MenuType.None;
Game.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs()
{
{ "initialMap", initialMap != null ? initialMap.Uid : null },
{ "onExit", () => menuType = MenuType.MapEditor },
{ "onSelect", onSelect },
{ "filter", MapVisibility.Lobby | MapVisibility.Shellmap | MapVisibility.MissionSelector },
});
};
mapEditorMenu.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => menuType = MenuType.Extras;
var newsBG = widget.GetOrNull("NEWS_BG");
if (newsBG != null)
{
@@ -189,6 +228,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic
};
}
void LoadMapIntoEditor(Map map)
{
ConnectionLogic.Connect(System.Net.IPAddress.Loopback.ToString(),
Game.CreateLocalServer(map.Uid),
"",
() => { Game.LoadEditor(map.Uid); },
() => { Game.CloseServer(); menuType = MenuType.MapEditor; });
}
void SetNewsStatus(string message)
{
message = WidgetUtils.WrapText(message, newsStatus.Bounds.Width, Game.Renderer.Fonts[newsStatus.Font]);

View File

@@ -47,6 +47,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ "onExit", () => { } },
{ "onSelect", (Action<string>)(uid => preview = Game.ModData.MapCache[uid]) },
{ "filter", MapVisibility.Lobby },
{ "onStart", () => { } }
});
};

View File

@@ -16,6 +16,7 @@ namespace OpenRA.Mods.Common.Widgets
{
public class SpriteWidget : Widget
{
public Func<float> GetScale = () => 1f;
public string Palette = "chrome";
public Func<string> GetPalette;
public Func<Sprite> 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);
}
}
}

View File

@@ -89,7 +89,7 @@ namespace OpenRA.Mods.Common.Widgets
if (tileInfo == null)
continue;
var sprite = worldRenderer.Theater.TileSprite(tile);
var sprite = worldRenderer.Theater.TileSprite(tile, 0);
var size = new float2(sprite.Size.X * scale, sprite.Size.Y * scale);
var u = shape == TileShape.Rectangle ? x : (x - y) / 2f;