Merge pull request #11572 from pchote/building-depth
Add building depth imposters for TS base structures.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -43,16 +44,47 @@ namespace OpenRA.Graphics
|
||||
public class SpriteCache
|
||||
{
|
||||
public readonly SheetBuilder SheetBuilder;
|
||||
readonly Cache<string, Sprite[]> sprites;
|
||||
readonly ISpriteLoader[] loaders;
|
||||
readonly IReadOnlyFileSystem fileSystem;
|
||||
|
||||
readonly Dictionary<string, List<Sprite[]>> sprites = new Dictionary<string, List<Sprite[]>>();
|
||||
|
||||
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders, SheetBuilder sheetBuilder)
|
||||
{
|
||||
SheetBuilder = sheetBuilder;
|
||||
|
||||
sprites = new Cache<string, Sprite[]>(filename => SpriteLoader.GetSprites(fileSystem, filename, loaders, sheetBuilder));
|
||||
this.fileSystem = fileSystem;
|
||||
this.loaders = loaders;
|
||||
}
|
||||
|
||||
public Sprite[] this[string filename] { get { return sprites[filename]; } }
|
||||
Sprite[] LoadSprite(string filename, List<Sprite[]> cache)
|
||||
{
|
||||
var sprite = SpriteLoader.GetSprites(fileSystem, filename, loaders, SheetBuilder);
|
||||
cache.Add(sprite);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
/// <summary>Returns the first set of sprites with the given filename.</summary>
|
||||
public Sprite[] this[string filename]
|
||||
{
|
||||
get
|
||||
{
|
||||
var allSprites = sprites.GetOrAdd(filename);
|
||||
var sprite = allSprites.FirstOrDefault();
|
||||
return sprite ?? LoadSprite(filename, allSprites);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns all instances of sets of sprites with the given filename</summary>
|
||||
public IEnumerable<Sprite[]> AllCached(string filename)
|
||||
{
|
||||
return sprites.GetOrAdd(filename);
|
||||
}
|
||||
|
||||
/// <summary>Loads and caches a new instance of sprites with the given filename</summary>
|
||||
public Sprite[] Reload(string filename)
|
||||
{
|
||||
return LoadSprite(filename, sprites.GetOrAdd(filename));
|
||||
}
|
||||
}
|
||||
|
||||
public class FrameCache
|
||||
|
||||
@@ -52,7 +52,9 @@ namespace OpenRA.Graphics
|
||||
float3 ScreenPosition(WorldRenderer wr)
|
||||
{
|
||||
var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset) - (0.5f * scale * sprite.Size.XY).ToInt2();
|
||||
return new float3(xy, wr.ScreenZPosition(pos, 0) - 0.5f * scale * sprite.Size.Z);
|
||||
|
||||
// HACK: The z offset needs to be applied somewhere, but this probably is the wrong place.
|
||||
return new float3(xy, sprite.Offset.Z + wr.ScreenZPosition(pos, 0) - 0.5f * scale * sprite.Size.Z);
|
||||
}
|
||||
|
||||
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace OpenRA.Graphics
|
||||
st = ss.SecondaryTop;
|
||||
sr = ss.SecondaryRight;
|
||||
sb = ss.SecondaryBottom;
|
||||
attribC = -(attribC + ChannelSelect[(int)ss.Channel] / 10);
|
||||
attribC = -(attribC + ChannelSelect[(int)ss.SecondaryChannel] / 10);
|
||||
}
|
||||
|
||||
vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, attribC);
|
||||
|
||||
@@ -275,7 +275,9 @@ namespace OpenRA
|
||||
resolved.Add(kv.Key, new MiniYaml(kv.Value.Value, children));
|
||||
}
|
||||
|
||||
return resolved.Select(kv => new MiniYamlNode(kv.Key, kv.Value)).ToList();
|
||||
// Resolve any top-level removals (e.g. removing whole actor blocks)
|
||||
var nodes = new MiniYaml("", resolved.Select(kv => new MiniYamlNode(kv.Key, kv.Value)).ToList());
|
||||
return ResolveInherits("", nodes, tree, new Dictionary<string, MiniYamlNode.SourceLocation>());
|
||||
}
|
||||
|
||||
static void MergeIntoResolved(MiniYamlNode overrideNode, List<MiniYamlNode> existingNodes,
|
||||
|
||||
@@ -34,15 +34,22 @@ namespace OpenRA.Mods.Common.Graphics
|
||||
var nodes = node.Value.ToDictionary();
|
||||
|
||||
MiniYaml defaults;
|
||||
if (nodes.TryGetValue("Defaults", out defaults))
|
||||
try
|
||||
{
|
||||
nodes.Remove("Defaults");
|
||||
foreach (var n in nodes)
|
||||
if (nodes.TryGetValue("Defaults", out defaults))
|
||||
{
|
||||
n.Value.Nodes = MiniYaml.Merge(new[] { defaults.Nodes, n.Value.Nodes });
|
||||
n.Value.Value = n.Value.Value ?? defaults.Value;
|
||||
nodes.Remove("Defaults");
|
||||
foreach (var n in nodes)
|
||||
{
|
||||
n.Value.Nodes = MiniYaml.Merge(new[] { defaults.Nodes, n.Value.Nodes });
|
||||
n.Value.Value = n.Value.Value ?? defaults.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("Error occurred while parsing {0}".F(node.Key), e);
|
||||
}
|
||||
|
||||
foreach (var kvp in nodes)
|
||||
{
|
||||
@@ -154,7 +161,7 @@ namespace OpenRA.Mods.Common.Graphics
|
||||
|
||||
// Allow per-sprite offset, flipping, start, and length
|
||||
var subStart = LoadField(sd, "Start", 0);
|
||||
var subOffset = LoadField(sd, "Offset", float2.Zero);
|
||||
var subOffset = LoadField(sd, "Offset", float3.Zero);
|
||||
var subFlipX = LoadField(sd, "FlipX", false);
|
||||
var subFlipY = LoadField(sd, "FlipY", false);
|
||||
|
||||
@@ -162,7 +169,7 @@ namespace OpenRA.Mods.Common.Graphics
|
||||
var subSprites = cache[subSrc].Select(
|
||||
s => new Sprite(s.Sheet,
|
||||
FlipRectangle(s.Bounds, subFlipX, subFlipY), ZRamp,
|
||||
new float2(subFlipX ? -s.Offset.X : s.Offset.X, subFlipY ? -s.Offset.Y : s.Offset.Y) + subOffset + offset,
|
||||
new float3(subFlipX ? -s.Offset.X : s.Offset.X, subFlipY ? -s.Offset.Y : s.Offset.Y, s.Offset.Z) + subOffset + offset,
|
||||
s.Channel, blendMode));
|
||||
|
||||
var subLength = 0;
|
||||
@@ -185,10 +192,47 @@ namespace OpenRA.Mods.Common.Graphics
|
||||
sprites = cache[src].Select(
|
||||
s => new Sprite(s.Sheet,
|
||||
FlipRectangle(s.Bounds, flipX, flipY), ZRamp,
|
||||
new float2(flipX ? -s.Offset.X : s.Offset.X, flipY ? -s.Offset.Y : s.Offset.Y) + offset,
|
||||
new float3(flipX ? -s.Offset.X : s.Offset.X, flipY ? -s.Offset.Y : s.Offset.Y, s.Offset.Z) + offset,
|
||||
s.Channel, blendMode)).ToArray();
|
||||
}
|
||||
|
||||
var depthSprite = LoadField<string>(d, "DepthSprite", null);
|
||||
if (!string.IsNullOrEmpty(depthSprite))
|
||||
{
|
||||
var depthSpriteFrame = LoadField(d, "DepthSpriteFrame", 0);
|
||||
var depthOffset = LoadField(d, "DepthSpriteOffset", float2.Zero);
|
||||
var depthSprites = cache.AllCached(depthSprite)
|
||||
.Select(s => s[depthSpriteFrame]);
|
||||
|
||||
sprites = sprites.Select(s =>
|
||||
{
|
||||
// The depth sprite must live on the same sheet as the main sprite
|
||||
var ds = depthSprites.FirstOrDefault(dss => dss.Sheet == s.Sheet);
|
||||
if (ds == null)
|
||||
{
|
||||
// The sequence has probably overflowed onto a new sheet.
|
||||
// Allocating a new depth sprite on this sheet will almost certainly work
|
||||
ds = cache.Reload(depthSprite)[depthSpriteFrame];
|
||||
depthSprites = cache.AllCached(depthSprite)
|
||||
.Select(ss => ss[depthSpriteFrame]);
|
||||
|
||||
// If that doesn't work then we may be referencing a cached sprite from an earlier sheet
|
||||
// TODO: We could try and reallocate the main sprite, but that requires more complicated code and a perf hit
|
||||
// We'll only cross that bridge if this becomes a problem in reality
|
||||
if (ds.Sheet != s.Sheet)
|
||||
throw new SheetOverflowException("Cross-sheet depth sprite reference: {0}.{1}: {2}");
|
||||
}
|
||||
|
||||
var cw = (ds.Bounds.Left + ds.Bounds.Right) / 2 + (int)(s.Offset.X + depthOffset.X);
|
||||
var ch = (ds.Bounds.Top + ds.Bounds.Bottom) / 2 + (int)(s.Offset.Y + depthOffset.Y);
|
||||
var w = s.Bounds.Width / 2;
|
||||
var h = s.Bounds.Height / 2;
|
||||
|
||||
var r = Rectangle.FromLTRB(cw - w, ch - h, cw + w, ch + h);
|
||||
return new SpriteWithSecondaryData(s, r, ds.Channel);
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
MiniYaml length;
|
||||
if (d.TryGetValue("Length", out length) && length.Value == "*")
|
||||
Length = sprites.Length - Start;
|
||||
|
||||
@@ -26,6 +26,9 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
[Desc("Wall type for connections")]
|
||||
public readonly string Type = "wall";
|
||||
|
||||
[Desc("Override sequence to use when fully open.")]
|
||||
public readonly string OpenSequence = null;
|
||||
|
||||
public override object Create(ActorInitializer init) { return new WithGateSpriteBody(init, this); }
|
||||
|
||||
public override IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
|
||||
@@ -42,10 +45,11 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
}
|
||||
}
|
||||
|
||||
class WithGateSpriteBody : WithSpriteBody, INotifyRemovedFromWorld, INotifyBuildComplete, IWallConnector
|
||||
class WithGateSpriteBody : WithSpriteBody, INotifyRemovedFromWorld, INotifyBuildComplete, IWallConnector, ITick
|
||||
{
|
||||
readonly WithGateSpriteBodyInfo gateInfo;
|
||||
readonly Gate gate;
|
||||
bool renderOpen;
|
||||
|
||||
public WithGateSpriteBody(ActorInitializer init, WithGateSpriteBodyInfo info)
|
||||
: base(init, info, () => 0)
|
||||
@@ -54,6 +58,26 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
gate = init.Self.Trait<Gate>();
|
||||
}
|
||||
|
||||
void UpdateState(Actor self)
|
||||
{
|
||||
if (renderOpen)
|
||||
DefaultAnimation.PlayRepeating(NormalizeSequence(self, gateInfo.OpenSequence));
|
||||
else
|
||||
DefaultAnimation.PlayFetchIndex(NormalizeSequence(self, Info.Sequence), GetGateFrame);
|
||||
}
|
||||
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
if (gateInfo.OpenSequence == null)
|
||||
return;
|
||||
|
||||
if (gate.Position == gate.OpenPosition ^ renderOpen)
|
||||
{
|
||||
renderOpen = gate.Position == gate.OpenPosition;
|
||||
UpdateState(self);
|
||||
}
|
||||
}
|
||||
|
||||
int GetGateFrame()
|
||||
{
|
||||
return int2.Lerp(0, DefaultAnimation.CurrentSequence.Length - 1, gate.Position, gate.OpenPosition);
|
||||
@@ -61,12 +85,12 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
|
||||
public override void DamageStateChanged(Actor self, AttackInfo e)
|
||||
{
|
||||
DefaultAnimation.PlayFetchIndex(NormalizeSequence(self, Info.Sequence), GetGateFrame);
|
||||
UpdateState(self);
|
||||
}
|
||||
|
||||
public override void BuildingComplete(Actor self)
|
||||
{
|
||||
DefaultAnimation.PlayFetchIndex(NormalizeSequence(self, Info.Sequence), GetGateFrame);
|
||||
UpdateState(self);
|
||||
UpdateNeighbours(self);
|
||||
}
|
||||
|
||||
|
||||
BIN
mods/ts/bits/isodepth.shp
Normal file
BIN
mods/ts/bits/isodepth.shp
Normal file
Binary file not shown.
@@ -839,6 +839,7 @@
|
||||
-MustBeDestroyed:
|
||||
-WithSpriteBody:
|
||||
WithGateSpriteBody:
|
||||
OpenSequence: open
|
||||
Tooltip:
|
||||
Description: Automated barrier that opens for allied units.
|
||||
Gate:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -188,6 +188,8 @@ gghunt:
|
||||
BlendMode: Additive
|
||||
|
||||
smech:
|
||||
Defaults:
|
||||
Offset: 0,0,8
|
||||
stand:
|
||||
Start: 96
|
||||
Facings: -8
|
||||
|
||||
Reference in New Issue
Block a user