mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
1635 lines
55 KiB
C#
1635 lines
55 KiB
C#
using Robust.Client.Graphics;
|
|
using Robust.Client.Graphics.ClientEye;
|
|
using Robust.Client.Graphics.Drawing;
|
|
using Robust.Client.Graphics.Shaders;
|
|
using Robust.Client.Interfaces.GameObjects;
|
|
using Robust.Client.Interfaces.GameObjects.Components;
|
|
using Robust.Client.Interfaces.ResourceManagement;
|
|
using Robust.Client.ResourceManagement;
|
|
using Robust.Client.Utility;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.GameObjects.Components.Renderable;
|
|
using Robust.Shared.Interfaces.GameObjects;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Log;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Serialization;
|
|
using Robust.Shared.Utility;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
using Robust.Shared.Animations;
|
|
using Robust.Shared.Interfaces.Reflection;
|
|
using Robust.Shared.ViewVariables;
|
|
|
|
namespace Robust.Client.GameObjects
|
|
{
|
|
public sealed class SpriteComponent : SharedSpriteComponent, ISpriteComponent, IClickTargetComponent,
|
|
IComponentDebug
|
|
{
|
|
private bool _visible = true;
|
|
|
|
[ViewVariables(VVAccess.ReadWrite)]
|
|
public bool Visible
|
|
{
|
|
get => _visible;
|
|
set => _visible = value;
|
|
}
|
|
|
|
private DrawDepth drawDepth = DrawDepth.Objects;
|
|
|
|
/// <summary>
|
|
/// Z-index for drawing.
|
|
/// </summary>
|
|
[ViewVariables(VVAccess.ReadWrite)]
|
|
public DrawDepth DrawDepth
|
|
{
|
|
get => drawDepth;
|
|
set => drawDepth = value;
|
|
}
|
|
|
|
private Vector2 scale = Vector2.One;
|
|
|
|
/// <summary>
|
|
/// A scale applied to all layers.
|
|
/// </summary>
|
|
[Animatable]
|
|
[ViewVariables(VVAccess.ReadWrite)]
|
|
public Vector2 Scale
|
|
{
|
|
get => scale;
|
|
set => scale = value;
|
|
}
|
|
|
|
private Angle rotation;
|
|
|
|
[Animatable]
|
|
[ViewVariables(VVAccess.ReadWrite)]
|
|
public Angle Rotation
|
|
{
|
|
get => rotation;
|
|
set => rotation = value;
|
|
}
|
|
|
|
private Vector2 offset = Vector2.Zero;
|
|
|
|
/// <summary>
|
|
/// Offset applied to all layers.
|
|
/// </summary>
|
|
[Animatable]
|
|
[ViewVariables(VVAccess.ReadWrite)]
|
|
public Vector2 Offset
|
|
{
|
|
get => offset;
|
|
set => offset = value;
|
|
}
|
|
|
|
private Color color = Color.White;
|
|
|
|
[Animatable]
|
|
[ViewVariables]
|
|
public Color Color
|
|
{
|
|
get => color;
|
|
set => color = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Controls whether we use RSI directions to rotate, or just get angular rotation applied.
|
|
/// If true, all rotation to this sprite component is negated (that is rotation from say the owner being rotated).
|
|
/// Rotation transformations on individual layers still apply.
|
|
/// If false, all layers get locked to south and rotation is a transformation.
|
|
/// </summary>
|
|
[ViewVariables]
|
|
public bool Directional
|
|
{
|
|
get => _directional;
|
|
set => _directional = value;
|
|
}
|
|
|
|
private bool _directional = true;
|
|
|
|
private RSI _baseRsi;
|
|
|
|
[ViewVariables]
|
|
public RSI BaseRSI
|
|
{
|
|
get => _baseRsi;
|
|
set
|
|
{
|
|
_baseRsi = value;
|
|
if (Layers == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < Layers.Count; i++)
|
|
{
|
|
var layer = Layers[i];
|
|
if (!layer.State.IsValid || layer.RSI != null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (value.TryGetState(layer.State, out var state))
|
|
{
|
|
layer.AnimationTimeLeft = state.GetDelay(0);
|
|
}
|
|
else
|
|
{
|
|
Logger.ErrorS(LogCategory,
|
|
"Layer '{0}'no longer has state '{1}' due to base RSI change. Trace:\n{2}",
|
|
i, layer.State, Environment.StackTrace);
|
|
layer.Texture = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public ShaderInstance PostShader { get; set; }
|
|
|
|
[ViewVariables] private Dictionary<object, int> LayerMap = new Dictionary<object, int>();
|
|
[ViewVariables] private bool _layerMapShared;
|
|
[ViewVariables] private List<Layer> Layers;
|
|
|
|
#pragma warning disable 649
|
|
[Dependency] private readonly IResourceCache resourceCache;
|
|
[Dependency] private readonly IPrototypeManager prototypes;
|
|
[Dependency] private readonly IReflectionManager reflectionManager;
|
|
#pragma warning restore 649
|
|
|
|
[ViewVariables(VVAccess.ReadWrite)]
|
|
public uint RenderOrder { get; set; }
|
|
|
|
private static ShaderInstance _defaultShader;
|
|
|
|
[ViewVariables]
|
|
private ShaderInstance DefaultShader => _defaultShader ??
|
|
(_defaultShader = prototypes
|
|
.Index<ShaderPrototype>("shaded")
|
|
.Instance());
|
|
|
|
public const string LogCategory = "go.comp.sprite";
|
|
const string LayerSerializationCache = "spritelayer";
|
|
const string LayerMapSerializationCache = "spritelayermap";
|
|
|
|
[ViewVariables(VVAccess.ReadWrite)]
|
|
public bool IsInert { get; private set; }
|
|
|
|
/// <inheritdoc />
|
|
public void LayerMapSet(object key, int layer)
|
|
{
|
|
if (layer < 0 || layer >= Layers.Count)
|
|
{
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
_layerMapEnsurePrivate();
|
|
LayerMap.Add(key, layer);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void LayerMapRemove(object key)
|
|
{
|
|
_layerMapEnsurePrivate();
|
|
LayerMap.Remove(key);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public int LayerMapGet(object key)
|
|
{
|
|
return LayerMap[key];
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool LayerMapTryGet(object key, out int layer)
|
|
{
|
|
return LayerMap.TryGetValue(key, out layer);
|
|
}
|
|
|
|
private void _layerMapEnsurePrivate()
|
|
{
|
|
if (!_layerMapShared)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LayerMap = LayerMap.ShallowClone();
|
|
_layerMapShared = false;
|
|
}
|
|
|
|
public void LayerMapReserveBlank(object key)
|
|
{
|
|
if (LayerMapTryGet(key, out var _))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LayerMapSet(key, AddBlankLayer());
|
|
}
|
|
|
|
public int AddBlankLayer(int? newIndex = null)
|
|
{
|
|
var layer = new Layer {Visible = false};
|
|
return AddLayer(layer, newIndex);
|
|
}
|
|
|
|
public int AddLayer(string texturePath, int? newIndex = null)
|
|
{
|
|
return AddLayer(new ResourcePath(texturePath), newIndex);
|
|
}
|
|
|
|
public int AddLayer(ResourcePath texturePath, int? newIndex = null)
|
|
{
|
|
if (!resourceCache.TryGetResource<TextureResource>(TextureRoot / texturePath, out var texture))
|
|
{
|
|
if (texturePath.Extension == "rsi")
|
|
{
|
|
Logger.ErrorS(LogCategory, "Expected texture but got rsi '{0}', did you mean 'sprite:' instead of 'texture:'?", texturePath);
|
|
}
|
|
Logger.ErrorS(LogCategory, "Unable to load texture '{0}'. Trace:\n{1}", texturePath,
|
|
Environment.StackTrace);
|
|
}
|
|
|
|
return AddLayer(texture, newIndex);
|
|
}
|
|
|
|
public int AddLayer(Texture texture, int? newIndex = null)
|
|
{
|
|
var layer = new Layer {Texture = texture};
|
|
return AddLayer(layer, newIndex);
|
|
}
|
|
|
|
public int AddLayer(RSI.StateId stateId, int? newIndex = null)
|
|
{
|
|
var layer = new Layer {State = stateId};
|
|
if (BaseRSI != null && BaseRSI.TryGetState(stateId, out var state))
|
|
{
|
|
layer.AnimationTimeLeft = state.GetDelay(0);
|
|
}
|
|
else
|
|
{
|
|
Logger.ErrorS(LogCategory, "State does not exist in RSI: '{0}'. Trace:\n{1}", stateId,
|
|
Environment.StackTrace);
|
|
}
|
|
|
|
return AddLayer(layer, newIndex);
|
|
}
|
|
|
|
public int AddLayerState(string stateId, int? newIndex = null)
|
|
{
|
|
return AddLayer(new RSI.StateId(stateId), newIndex);
|
|
}
|
|
|
|
public int AddLayer(RSI.StateId stateId, string rsiPath, int? newIndex = null)
|
|
{
|
|
return AddLayer(stateId, new ResourcePath(rsiPath), newIndex);
|
|
}
|
|
|
|
public int AddLayerState(string stateId, string rsiPath, int? newIndex = null)
|
|
{
|
|
return AddLayer(new RSI.StateId(stateId), rsiPath, newIndex);
|
|
}
|
|
|
|
public int AddLayer(RSI.StateId stateId, ResourcePath rsiPath, int? newIndex = null)
|
|
{
|
|
if (!resourceCache.TryGetResource<RSIResource>(TextureRoot / rsiPath, out var res))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Unable to load RSI '{0}'. Trace:\n{1}", rsiPath, Environment.StackTrace);
|
|
}
|
|
|
|
return AddLayer(stateId, res?.RSI);
|
|
}
|
|
|
|
public int AddLayerState(string stateId, ResourcePath rsiPath, int? newIndex = null)
|
|
{
|
|
return AddLayer(new RSI.StateId(stateId), rsiPath, newIndex);
|
|
}
|
|
|
|
public int AddLayer(RSI.StateId stateId, RSI rsi, int? newIndex = null)
|
|
{
|
|
var layer = new Layer {State = stateId, RSI = rsi};
|
|
if (rsi.TryGetState(stateId, out var state))
|
|
{
|
|
layer.AnimationTimeLeft = state.GetDelay(0);
|
|
}
|
|
else
|
|
{
|
|
Logger.ErrorS(LogCategory, "State does not exist in RSI: '{0}'. Trace:\n{1}", stateId,
|
|
Environment.StackTrace);
|
|
}
|
|
|
|
return AddLayer(layer, newIndex);
|
|
}
|
|
|
|
public int AddLayerState(string stateId, RSI rsi, int? newIndex = null)
|
|
{
|
|
return AddLayer(new RSI.StateId(stateId), rsi, newIndex);
|
|
}
|
|
|
|
public int AddLayer(SpriteSpecifier specifier, int? newIndex = null)
|
|
{
|
|
switch (specifier)
|
|
{
|
|
case SpriteSpecifier.Texture tex:
|
|
return AddLayer(tex.TexturePath, newIndex);
|
|
|
|
case SpriteSpecifier.Rsi rsi:
|
|
return AddLayerState(rsi.RsiState, rsi.RsiPath, newIndex);
|
|
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
|
|
private int AddLayer(Layer layer, int? newIndex)
|
|
{
|
|
int index;
|
|
if (newIndex.HasValue)
|
|
{
|
|
Layers.Insert(newIndex.Value, layer);
|
|
foreach (var kv in LayerMap)
|
|
{
|
|
if (kv.Value >= newIndex.Value)
|
|
{
|
|
LayerMap[kv.Key] = kv.Value + 1;
|
|
}
|
|
}
|
|
|
|
index = newIndex.Value;
|
|
}
|
|
else
|
|
{
|
|
Layers.Add(layer);
|
|
index = Layers.Count - 1;
|
|
}
|
|
|
|
UpdateIsInert();
|
|
return index;
|
|
}
|
|
|
|
public void RemoveLayer(int layer)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot remove! Trace:\n{1}", layer,
|
|
Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
Layers.RemoveAt(layer);
|
|
foreach (var kv in LayerMap)
|
|
{
|
|
if (kv.Value == layer)
|
|
{
|
|
LayerMap.Remove(kv.Key);
|
|
}
|
|
|
|
else if (kv.Value > layer)
|
|
{
|
|
LayerMap[kv.Key] = kv.Value - 1;
|
|
}
|
|
}
|
|
|
|
UpdateIsInert();
|
|
}
|
|
|
|
public void RemoveLayer(object layerKey)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot remove! Trace:\n{1}", layerKey,
|
|
Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
RemoveLayer(layer);
|
|
}
|
|
|
|
public void LayerSetShader(int layer, ShaderInstance shader)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set shader! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
theLayer.Shader = shader;
|
|
}
|
|
|
|
public void LayerSetShader(object layerKey, ShaderInstance shader)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set shader! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetShader(layer, shader);
|
|
}
|
|
|
|
public void LayerSetShader(int layer, string shaderName)
|
|
{
|
|
if (!prototypes.TryIndex<ShaderPrototype>(shaderName, out var prototype))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Shader prototype '{0}' does not exist. Trace:\n{1}", shaderName,
|
|
Environment.StackTrace);
|
|
}
|
|
|
|
// This will set the shader to null if it does not exist.
|
|
LayerSetShader(layer, prototype?.Instance());
|
|
}
|
|
|
|
public void LayerSetShader(object layerKey, string shaderName)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set shader! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetShader(layer, shaderName);
|
|
}
|
|
|
|
public void LayerSetSprite(int layer, SpriteSpecifier specifier)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set sprite! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
switch (specifier)
|
|
{
|
|
case SpriteSpecifier.Texture tex:
|
|
LayerSetTexture(layer, tex.TexturePath);
|
|
break;
|
|
case SpriteSpecifier.Rsi rsi:
|
|
LayerSetState(layer, rsi.RsiState, rsi.RsiPath);
|
|
break;
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
|
|
public void LayerSetSprite(object layerKey, SpriteSpecifier specifier)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set sprite! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetSprite(layer, specifier);
|
|
}
|
|
|
|
public void LayerSetTexture(int layer, Texture texture)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set texture! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
theLayer.State = null;
|
|
theLayer.Texture = texture;
|
|
|
|
UpdateIsInert();
|
|
}
|
|
|
|
public void LayerSetTexture(object layerKey, Texture texture)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set texture! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetTexture(layer, texture);
|
|
}
|
|
|
|
public void LayerSetTexture(int layer, string texturePath)
|
|
{
|
|
LayerSetTexture(layer, new ResourcePath(texturePath));
|
|
}
|
|
|
|
public void LayerSetTexture(object layerKey, string texturePath)
|
|
{
|
|
LayerSetTexture(layerKey, new ResourcePath(texturePath));
|
|
}
|
|
|
|
public void LayerSetTexture(int layer, ResourcePath texturePath)
|
|
{
|
|
if (!resourceCache.TryGetResource<TextureResource>(TextureRoot / texturePath, out var texture))
|
|
{
|
|
if (texturePath.Extension == "rsi")
|
|
{
|
|
Logger.ErrorS(LogCategory, "Expected texture but got rsi '{0}', did you mean 'sprite:' instead of 'texture:'?", texturePath);
|
|
}
|
|
Logger.ErrorS(LogCategory, "Unable to load texture '{0}'. Trace:\n{1}", texturePath,
|
|
Environment.StackTrace);
|
|
}
|
|
|
|
LayerSetTexture(layer, texture);
|
|
}
|
|
|
|
public void LayerSetTexture(object layerKey, ResourcePath texturePath)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set texture! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetTexture(layer, texturePath);
|
|
}
|
|
|
|
public void LayerSetState(int layer, RSI.StateId stateId)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set state! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
if (theLayer.State == stateId)
|
|
{
|
|
return;
|
|
}
|
|
|
|
theLayer.State = stateId;
|
|
RSI.State state;
|
|
var rsi = theLayer.RSI ?? BaseRSI;
|
|
if (rsi == null)
|
|
{
|
|
state = GetFallbackState();
|
|
Logger.ErrorS(LogCategory, "No RSI to pull new state from! Trace:\n{0}", Environment.StackTrace);
|
|
}
|
|
else
|
|
{
|
|
if (!rsi.TryGetState(stateId, out state))
|
|
{
|
|
state = GetFallbackState();
|
|
Logger.ErrorS(LogCategory, "State '{0}' does not exist in RSI. Trace:\n{1}", stateId,
|
|
Environment.StackTrace);
|
|
}
|
|
}
|
|
|
|
theLayer.AnimationFrame = 0;
|
|
theLayer.AnimationTime = 0;
|
|
theLayer.AnimationTimeLeft = state.GetDelay(0);
|
|
|
|
UpdateIsInert();
|
|
}
|
|
|
|
public void LayerSetState(object layerKey, RSI.StateId stateId)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set state! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetState(layer, stateId);
|
|
}
|
|
|
|
public void LayerSetState(int layer, RSI.StateId stateId, RSI rsi)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set state! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
theLayer.State = stateId;
|
|
theLayer.RSI = rsi;
|
|
var actualRsi = theLayer.RSI ?? BaseRSI;
|
|
if (actualRsi == null)
|
|
{
|
|
Logger.ErrorS(LogCategory, "No RSI to pull new state from! Trace:\n{0}", Environment.StackTrace);
|
|
theLayer.Texture = null;
|
|
}
|
|
else
|
|
{
|
|
if (actualRsi.TryGetState(stateId, out var state))
|
|
{
|
|
theLayer.AnimationFrame = 0;
|
|
theLayer.AnimationTime = 0;
|
|
theLayer.AnimationTimeLeft = state.GetDelay(0);
|
|
}
|
|
else
|
|
{
|
|
Logger.ErrorS(LogCategory, "State '{0}' does not exist in RSI. Trace:\n{1}", stateId,
|
|
Environment.StackTrace);
|
|
theLayer.Texture = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void LayerSetState(object layerKey, RSI.StateId stateId, RSI rsi)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set state! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetState(layer, stateId, rsi);
|
|
}
|
|
|
|
public void LayerSetState(int layer, RSI.StateId stateId, string rsiPath)
|
|
{
|
|
LayerSetState(layer, stateId, new ResourcePath(rsiPath));
|
|
}
|
|
|
|
public void LayerSetState(object layerKey, RSI.StateId stateId, string rsiPath)
|
|
{
|
|
LayerSetState(layerKey, stateId, new ResourcePath(rsiPath));
|
|
}
|
|
|
|
public void LayerSetState(int layer, RSI.StateId stateId, ResourcePath rsiPath)
|
|
{
|
|
if (!resourceCache.TryGetResource<RSIResource>(TextureRoot / rsiPath, out var res))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Unable to load RSI '{0}'. Trace:\n{1}", rsiPath, Environment.StackTrace);
|
|
}
|
|
|
|
LayerSetState(layer, stateId, res?.RSI);
|
|
}
|
|
|
|
public void LayerSetState(object layerKey, RSI.StateId stateId, ResourcePath rsiPath)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set state! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetState(layer, stateId, rsiPath);
|
|
}
|
|
|
|
public void LayerSetRSI(int layer, RSI rsi)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set RSI! Trace:\n{1}", layer,
|
|
Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
theLayer.RSI = rsi;
|
|
if (!theLayer.State.IsValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Gotta do this because somebody might use null as argument (totally valid).
|
|
var actualRsi = theLayer.RSI ?? BaseRSI;
|
|
if (actualRsi == null)
|
|
{
|
|
Logger.ErrorS(LogCategory, "No RSI to pull new state from! Trace:\n{0}", Environment.StackTrace);
|
|
theLayer.Texture = null;
|
|
}
|
|
else
|
|
{
|
|
if (rsi.TryGetState(theLayer.State, out var state))
|
|
{
|
|
theLayer.AnimationTimeLeft = state.GetDelay(0);
|
|
}
|
|
else
|
|
{
|
|
Logger.ErrorS(LogCategory, "State '{0}' does not exist in set RSI. Trace:\n{1}", theLayer.State,
|
|
Environment.StackTrace);
|
|
theLayer.Texture = null;
|
|
}
|
|
}
|
|
|
|
UpdateIsInert();
|
|
}
|
|
|
|
public void LayerSetRSI(object layerKey, RSI rsi)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set RSI! Trace:\n{1}", layerKey,
|
|
Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetRSI(layer, rsi);
|
|
}
|
|
|
|
public void LayerSetRSI(int layer, string rsiPath)
|
|
{
|
|
LayerSetRSI(layer, new ResourcePath(rsiPath));
|
|
}
|
|
|
|
public void LayerSetRSI(object layerKey, string rsiPath)
|
|
{
|
|
LayerSetRSI(layerKey, new ResourcePath(rsiPath));
|
|
}
|
|
|
|
public void LayerSetRSI(int layer, ResourcePath rsiPath)
|
|
{
|
|
if (!resourceCache.TryGetResource<RSIResource>(TextureRoot / rsiPath, out var res))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Unable to load RSI '{0}'. Trace:\n{1}", rsiPath, Environment.StackTrace);
|
|
}
|
|
|
|
LayerSetRSI(layer, res?.RSI);
|
|
}
|
|
|
|
public void LayerSetRSI(object layerKey, ResourcePath rsiPath)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set RSI! Trace:\n{1}", layerKey,
|
|
Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetRSI(layer, rsiPath);
|
|
}
|
|
|
|
public void LayerSetScale(int layer, Vector2 scale)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set scale! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
theLayer.Scale = scale;
|
|
}
|
|
|
|
public void LayerSetScale(object layerKey, Vector2 scale)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set scale! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetScale(layer, scale);
|
|
}
|
|
|
|
|
|
public void LayerSetRotation(int layer, Angle rotation)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set rotation! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
theLayer.Rotation = rotation;
|
|
}
|
|
|
|
public void LayerSetRotation(object layerKey, Angle rotation)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set rotation! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetRotation(layer, rotation);
|
|
}
|
|
|
|
public void LayerSetVisible(int layer, bool visible)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set visibility! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
theLayer.Visible = visible;
|
|
|
|
UpdateIsInert();
|
|
}
|
|
|
|
public void LayerSetVisible(object layerKey, bool visible)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set visibility! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetVisible(layer, visible);
|
|
}
|
|
|
|
public void LayerSetColor(int layer, Color color)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set color! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
theLayer.Color = color;
|
|
}
|
|
|
|
public void LayerSetColor(object layerKey, Color color)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set color! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetColor(layer, color);
|
|
}
|
|
|
|
public void LayerSetDirOffset(int layer, DirectionOffset offset)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set dir offset! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
theLayer.DirOffset = offset;
|
|
}
|
|
|
|
public void LayerSetDirOffset(object layerKey, DirectionOffset offset)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set dir offset! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetDirOffset(layer, offset);
|
|
}
|
|
|
|
public void LayerSetAnimationTime(int layer, float animationTime)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set animation time! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
if (!theLayer.State.IsValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// TODO: This could throw exceptions which shouldn't happen I think.
|
|
var state = (theLayer.RSI ?? BaseRSI)[theLayer.State];
|
|
if (animationTime > theLayer.AnimationTime)
|
|
{
|
|
// Handle advancing differently from going backwards.
|
|
theLayer.AnimationTimeLeft -= (animationTime - theLayer.AnimationTime);
|
|
}
|
|
else
|
|
{
|
|
// Going backwards we re-calculate from zero.
|
|
// Definitely possible to optimize this for going backwards but I'm too lazy to figure that out.
|
|
theLayer.AnimationTimeLeft = -animationTime + state.GetDelay(0);
|
|
theLayer.AnimationFrame = 0;
|
|
}
|
|
|
|
theLayer.AnimationTime = animationTime;
|
|
// After setting timing data correctly, run advance to get to the correct frame.
|
|
_advanceFrameAnimation(theLayer, state);
|
|
}
|
|
|
|
public void LayerSetAnimationTime(object layerKey, float animationTime)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set animation time! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetAnimationTime(layer, animationTime);
|
|
}
|
|
|
|
public void LayerSetAutoAnimated(int layer, bool autoAnimated)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set auto animated! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
theLayer.AutoAnimated = autoAnimated;
|
|
|
|
UpdateIsInert();
|
|
}
|
|
|
|
public void LayerSetAutoAnimated(object layerKey, bool autoAnimated)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set auto animated! Trace:\n{1}",
|
|
layerKey, Environment.StackTrace);
|
|
return;
|
|
}
|
|
|
|
LayerSetAutoAnimated(layer, autoAnimated);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public RSI.StateId LayerGetState(int layer)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot get state! Trace:\n{1}",
|
|
layer, Environment.StackTrace);
|
|
return null;
|
|
}
|
|
|
|
var thelayer = Layers[layer];
|
|
return thelayer.State;
|
|
}
|
|
|
|
public RSI LayerGetActualRSI(int layer)
|
|
{
|
|
if (Layers.Count <= layer)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(layer), $"Layer '{layer}' does not exist.");
|
|
}
|
|
|
|
var theLayer = Layers[layer];
|
|
return BaseRSI ?? theLayer.RSI;
|
|
}
|
|
|
|
public RSI LayerGetActualRSI(object layerKey)
|
|
{
|
|
if (!LayerMapTryGet(layerKey, out var layer))
|
|
{
|
|
throw new KeyNotFoundException($"Layer '{layerKey}' does not exist.");
|
|
}
|
|
|
|
return LayerGetActualRSI(layer);
|
|
}
|
|
|
|
internal void Render(DrawingHandleWorld drawingHandle, in Matrix3 worldTransform, Angle worldRotation, Direction? overrideDirection=null)
|
|
{
|
|
var angle = Rotation;
|
|
if (Directional)
|
|
{
|
|
angle -= worldRotation;
|
|
}
|
|
else
|
|
{
|
|
angle -= new Angle(MathHelper.PiOver2);
|
|
}
|
|
|
|
var mOffset = Matrix3.CreateTranslation(Offset);
|
|
var mRotation = Matrix3.CreateRotation(angle);
|
|
Matrix3.Multiply(ref mRotation, ref mOffset, out var transform);
|
|
|
|
transform.Multiply(worldTransform);
|
|
|
|
RenderInternal(drawingHandle, worldRotation, overrideDirection, transform);
|
|
}
|
|
|
|
internal void Render(DrawingHandleWorld drawingHandle, Angle worldRotation, Direction? overrideDirection = null)
|
|
{
|
|
RenderInternal(drawingHandle, worldRotation, overrideDirection, Matrix3.Identity);
|
|
}
|
|
|
|
private void RenderInternal(DrawingHandleWorld drawingHandle, Angle worldRotation, Direction? overrideDirection,
|
|
in Matrix3 transform)
|
|
{
|
|
drawingHandle.SetTransform(transform);
|
|
|
|
foreach (var layer in Layers)
|
|
{
|
|
if (!layer.Visible)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// TODO: Implement layer-specific rotation and scale.
|
|
|
|
var texture = layer.Texture;
|
|
|
|
if (layer.State.IsValid)
|
|
{
|
|
// Pull texture from RSI state instead.
|
|
var rsi = layer.RSI ?? BaseRSI;
|
|
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
|
|
{
|
|
state = GetFallbackState();
|
|
}
|
|
|
|
RSI.State.Direction layerSpecificDir;
|
|
if (state.Directions == RSI.State.DirectionType.Dir1)
|
|
{
|
|
layerSpecificDir = RSI.State.Direction.South;
|
|
}
|
|
else
|
|
{
|
|
RSI.State.Direction dir;
|
|
if (overrideDirection != null)
|
|
{
|
|
dir = overrideDirection.Value.Convert(state.Directions);
|
|
}
|
|
else
|
|
{
|
|
dir = GetDir(state.Directions, worldRotation);
|
|
}
|
|
|
|
layerSpecificDir = OffsetRsiDir(dir, layer.DirOffset);
|
|
}
|
|
|
|
texture = state.GetFrame(layerSpecificDir, layer.AnimationFrame);
|
|
}
|
|
|
|
texture ??= resourceCache.GetFallback<TextureResource>();
|
|
|
|
if (layer.Shader != null)
|
|
{
|
|
drawingHandle.UseShader(layer.Shader);
|
|
}
|
|
|
|
drawingHandle.DrawTexture(texture, -(Vector2) texture.Size / (2f * EyeManager.PixelsPerMeter),
|
|
color * layer.Color);
|
|
|
|
if (layer.Shader != null)
|
|
{
|
|
drawingHandle.UseShader(null);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void ExposeData(ObjectSerializer serializer)
|
|
{
|
|
base.ExposeData(serializer);
|
|
|
|
serializer.DataFieldCached(ref scale, "scale", Vector2.One);
|
|
serializer.DataFieldCached(ref rotation, "rotation", Angle.Zero);
|
|
serializer.DataFieldCached(ref offset, "offset", Vector2.Zero);
|
|
serializer.DataFieldCached(ref drawDepth, "drawdepth", DrawDepth.Objects);
|
|
serializer.DataFieldCached(ref color, "color", Color.White);
|
|
serializer.DataFieldCached(ref _directional, "directional", true);
|
|
serializer.DataFieldCached(ref _visible, "visible", true);
|
|
|
|
// TODO: Writing?
|
|
if (!serializer.Reading)
|
|
{
|
|
return;
|
|
}
|
|
|
|
{
|
|
var rsi = serializer.ReadDataField<string>("sprite", null);
|
|
if (!string.IsNullOrWhiteSpace(rsi))
|
|
{
|
|
var rsiPath = TextureRoot / rsi;
|
|
try
|
|
{
|
|
BaseRSI = resourceCache.GetResource<RSIResource>(rsiPath).RSI;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.ErrorS(LogCategory, "Unable to load RSI '{0}'. Trace:\n{1}", rsiPath, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
static List<Layer> CloneLayers(List<Layer> source)
|
|
{
|
|
var clone = new List<Layer>(source.Count);
|
|
foreach (var layer in source)
|
|
{
|
|
clone.Add(new Layer(layer));
|
|
}
|
|
|
|
return clone;
|
|
}
|
|
|
|
if (serializer.TryGetCacheData<List<Layer>>(LayerSerializationCache, out var layers))
|
|
{
|
|
LayerMap = serializer.GetCacheData<Dictionary<object, int>>(LayerMapSerializationCache);
|
|
_layerMapShared = true;
|
|
Layers = CloneLayers(layers);
|
|
UpdateIsInert();
|
|
return;
|
|
}
|
|
|
|
layers = new List<Layer>();
|
|
|
|
var layerMap = new Dictionary<object, int>();
|
|
|
|
var layerData =
|
|
serializer.ReadDataField("layers", new List<PrototypeLayerData>());
|
|
|
|
{
|
|
var baseState = serializer.ReadDataField<string>("state", null);
|
|
var texturePath = serializer.ReadDataField<string>("texture", null);
|
|
|
|
if (baseState != null || texturePath != null)
|
|
{
|
|
layerData.Insert(0, new PrototypeLayerData
|
|
{
|
|
TexturePath = string.IsNullOrWhiteSpace(texturePath) ? null : texturePath,
|
|
State = string.IsNullOrWhiteSpace(baseState) ? null : baseState,
|
|
Color = Color.White,
|
|
Scale = Vector2.One,
|
|
Visible = true,
|
|
});
|
|
}
|
|
}
|
|
|
|
foreach (var layerDatum in layerData)
|
|
{
|
|
var anyTextureAttempted = false;
|
|
var layer = new Layer();
|
|
if (!string.IsNullOrWhiteSpace(layerDatum.RsiPath))
|
|
{
|
|
var path = TextureRoot / layerDatum.RsiPath;
|
|
try
|
|
{
|
|
layer.RSI = resourceCache.GetResource<RSIResource>(path).RSI;
|
|
}
|
|
catch
|
|
{
|
|
Logger.ErrorS(LogCategory, "Unable to load layer RSI '{0}'.", path);
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(layerDatum.State))
|
|
{
|
|
anyTextureAttempted = true;
|
|
var theRsi = layer.RSI ?? BaseRSI;
|
|
if (theRsi == null)
|
|
{
|
|
Logger.ErrorS(LogCategory,
|
|
"Layer has no RSI to load states from."
|
|
+ "cannot use 'state' property. Prototype: '{0}'", Owner.Prototype.ID);
|
|
}
|
|
else
|
|
{
|
|
var stateid = new RSI.StateId(layerDatum.State);
|
|
layer.State = stateid;
|
|
if (theRsi.TryGetState(stateid, out var state))
|
|
{
|
|
// Always use south because this layer will be cached in the serializer.
|
|
layer.AnimationTimeLeft = state.GetDelay(0);
|
|
}
|
|
else
|
|
{
|
|
Logger.ErrorS(LogCategory,
|
|
"State not found in layer RSI: '{0}'.",
|
|
stateid);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(layerDatum.TexturePath))
|
|
{
|
|
anyTextureAttempted = true;
|
|
if (layer.State.IsValid)
|
|
{
|
|
Logger.ErrorS(LogCategory,
|
|
"Cannot specify 'texture' on a layer if it has an RSI state specified."
|
|
);
|
|
}
|
|
else
|
|
{
|
|
layer.Texture =
|
|
resourceCache.GetResource<TextureResource>(TextureRoot / layerDatum.TexturePath);
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(layerDatum.Shader))
|
|
{
|
|
if (prototypes.TryIndex<ShaderPrototype>(layerDatum.Shader, out var prototype))
|
|
{
|
|
layer.Shader = prototype.Instance();
|
|
}
|
|
else
|
|
{
|
|
Logger.ErrorS(LogCategory,
|
|
"Shader prototype '{0}' does not exist. Prototype: '{1}'",
|
|
layerDatum.Shader, Owner.Prototype.ID);
|
|
}
|
|
}
|
|
|
|
layer.Color = layerDatum.Color;
|
|
layer.Rotation = layerDatum.Rotation;
|
|
// If neither state: nor texture: were provided we assume that they want a blank invisible layer.
|
|
layer.Visible = anyTextureAttempted && layerDatum.Visible;
|
|
layer.Scale = layerDatum.Scale;
|
|
|
|
layers.Add(layer);
|
|
|
|
if (layerDatum.MapKeys != null)
|
|
{
|
|
var index = layers.Count - 1;
|
|
foreach (var keyString in layerDatum.MapKeys)
|
|
{
|
|
object key;
|
|
if (reflectionManager.TryParseEnumReference(keyString, out var @enum))
|
|
{
|
|
key = @enum;
|
|
}
|
|
else
|
|
{
|
|
key = keyString;
|
|
}
|
|
|
|
if (layerMap.ContainsKey(key))
|
|
{
|
|
Logger.ErrorS(LogCategory, "Duplicate layer map key definition: {0}", key);
|
|
continue;
|
|
}
|
|
|
|
layerMap.Add(key, index);
|
|
}
|
|
}
|
|
}
|
|
|
|
Layers = layers;
|
|
LayerMap = layerMap;
|
|
_layerMapShared = true;
|
|
serializer.SetCacheData(LayerSerializationCache, CloneLayers(Layers));
|
|
serializer.SetCacheData(LayerMapSerializationCache, layerMap);
|
|
UpdateIsInert();
|
|
}
|
|
|
|
public void FrameUpdate(float delta)
|
|
{
|
|
foreach (var t in Layers)
|
|
{
|
|
var layer = t;
|
|
// Since StateId is a struct, we can't null-check it directly.
|
|
if (!layer.State.IsValid || !layer.Visible || !layer.AutoAnimated)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var rsi = layer.RSI ?? BaseRSI;
|
|
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
|
|
{
|
|
state = GetFallbackState();
|
|
}
|
|
|
|
if (!state.IsAnimated)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
layer.AnimationTime += delta;
|
|
layer.AnimationTimeLeft -= delta;
|
|
_advanceFrameAnimation(layer, state);
|
|
}
|
|
}
|
|
|
|
private static void _advanceFrameAnimation(Layer layer, RSI.State state)
|
|
{
|
|
var delayCount = state.DelayCount;
|
|
while (layer.AnimationTimeLeft < 0)
|
|
{
|
|
layer.AnimationFrame += 1;
|
|
|
|
if (layer.AnimationFrame >= delayCount)
|
|
{
|
|
layer.AnimationFrame = 0;
|
|
layer.AnimationTime = -layer.AnimationTimeLeft;
|
|
}
|
|
|
|
layer.AnimationTimeLeft += state.GetDelay(layer.AnimationFrame);
|
|
}
|
|
}
|
|
|
|
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
|
{
|
|
if (curState == null)
|
|
return;
|
|
|
|
var thestate = (SpriteComponentState) curState;
|
|
|
|
Visible = thestate.Visible;
|
|
DrawDepth = thestate.DrawDepth;
|
|
Scale = thestate.Scale;
|
|
Rotation = thestate.Rotation;
|
|
Offset = thestate.Offset;
|
|
Color = thestate.Color;
|
|
Directional = thestate.Directional;
|
|
RenderOrder = thestate.RenderOrder;
|
|
|
|
if (thestate.BaseRsiPath != null && BaseRSI != null)
|
|
{
|
|
if (resourceCache.TryGetResource<RSIResource>(TextureRoot / thestate.BaseRsiPath, out var res))
|
|
{
|
|
if (BaseRSI != res.RSI)
|
|
{
|
|
BaseRSI = res.RSI;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Logger.ErrorS(LogCategory, "Hey server, RSI '{0}' doesn't exist.", thestate.BaseRsiPath);
|
|
}
|
|
}
|
|
|
|
// Maybe optimize this to NOT full clear.
|
|
Layers.Clear();
|
|
for (var i = 0; i < thestate.Layers.Count; i++)
|
|
{
|
|
var netlayer = thestate.Layers[i];
|
|
var layer = new Layer
|
|
{
|
|
// These are easy so do them here.
|
|
Scale = netlayer.Scale,
|
|
Rotation = netlayer.Rotation,
|
|
Visible = netlayer.Visible,
|
|
Color = netlayer.Color
|
|
};
|
|
Layers.Add(layer);
|
|
|
|
// Using the public API to handle errors.
|
|
// Probably slow as crap.
|
|
// Who am I kidding, DEFINITELY.
|
|
if (netlayer.Shader != null)
|
|
{
|
|
LayerSetShader(i, netlayer.Shader);
|
|
}
|
|
|
|
if (netlayer.RsiPath != null)
|
|
{
|
|
LayerSetRSI(i, netlayer.RsiPath);
|
|
}
|
|
|
|
if (netlayer.TexturePath != null)
|
|
{
|
|
LayerSetTexture(i, netlayer.TexturePath);
|
|
}
|
|
else if (netlayer.State != null)
|
|
{
|
|
LayerSetState(i, netlayer.State);
|
|
}
|
|
}
|
|
}
|
|
|
|
private RSI.State.Direction GetDir(RSI.State.DirectionType type, Angle worldRotation)
|
|
{
|
|
if (!Directional)
|
|
|
|
{
|
|
return RSI.State.Direction.South;
|
|
}
|
|
|
|
var angle = new Angle(worldRotation);
|
|
return angle.GetDir().Convert(type);
|
|
}
|
|
|
|
private void UpdateIsInert()
|
|
{
|
|
IsInert = true;
|
|
|
|
foreach (var layer in Layers)
|
|
{
|
|
// Since StateId is a struct, we can't null-check it directly.
|
|
if (!layer.State.IsValid || !layer.Visible || !layer.AutoAnimated)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var rsi = layer.RSI ?? BaseRSI;
|
|
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
|
|
{
|
|
state = GetFallbackState();
|
|
}
|
|
|
|
if (state.IsAnimated)
|
|
{
|
|
IsInert = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private RSI.State GetFallbackState()
|
|
{
|
|
var rsi = resourceCache.GetResource<RSIResource>("/Textures/error.rsi").RSI;
|
|
return rsi["error"];
|
|
}
|
|
|
|
private static RSI.State.Direction OffsetRsiDir(RSI.State.Direction dir, DirectionOffset offset)
|
|
{
|
|
// There is probably a better way to do this.
|
|
// Eh.
|
|
switch (offset)
|
|
{
|
|
case DirectionOffset.None:
|
|
return dir;
|
|
case DirectionOffset.Clockwise:
|
|
switch (dir)
|
|
{
|
|
case RSI.State.Direction.North:
|
|
return RSI.State.Direction.East;
|
|
case RSI.State.Direction.East:
|
|
return RSI.State.Direction.South;
|
|
case RSI.State.Direction.South:
|
|
return RSI.State.Direction.West;
|
|
case RSI.State.Direction.West:
|
|
return RSI.State.Direction.North;
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
case DirectionOffset.CounterClockwise:
|
|
switch (dir)
|
|
{
|
|
case RSI.State.Direction.North:
|
|
return RSI.State.Direction.West;
|
|
case RSI.State.Direction.East:
|
|
return RSI.State.Direction.North;
|
|
case RSI.State.Direction.South:
|
|
return RSI.State.Direction.East;
|
|
case RSI.State.Direction.West:
|
|
return RSI.State.Direction.South;
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
case DirectionOffset.Flip:
|
|
switch (dir)
|
|
{
|
|
case RSI.State.Direction.North:
|
|
return RSI.State.Direction.South;
|
|
case RSI.State.Direction.East:
|
|
return RSI.State.Direction.West;
|
|
case RSI.State.Direction.South:
|
|
return RSI.State.Direction.North;
|
|
case RSI.State.Direction.West:
|
|
return RSI.State.Direction.East;
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
|
|
public string GetDebugString()
|
|
{
|
|
var builder = new StringBuilder();
|
|
builder.AppendFormat(
|
|
"vis/depth/scl/rot/ofs/col/diral/dir: {0}/{1}/{2}/{3}/{4}/{5}/{6}/{7}\n",
|
|
Visible, DrawDepth, Scale, Rotation, Offset,
|
|
Color, Directional, GetDir(RSI.State.DirectionType.Dir8, Owner.Transform.WorldRotation)
|
|
);
|
|
|
|
foreach (var layer in Layers)
|
|
{
|
|
builder.AppendFormat(
|
|
"shad/tex/rsi/state/ant/anf/scl/rot/vis/col/dofs: {0}/{1}/{2}/{3}/{4}/{5}/{6}/{7}/{8}/{9}/{10}\n",
|
|
// These are references and don't include useful data for knowing where they came from, sadly.
|
|
// "is one set" is better than nothing at least.
|
|
layer.Shader != null, layer.Texture != null, layer.RSI != null,
|
|
layer.State,
|
|
layer.AnimationTimeLeft, layer.AnimationFrame, layer.Scale, layer.Rotation, layer.Visible,
|
|
layer.Color, layer.DirOffset
|
|
);
|
|
}
|
|
|
|
return builder.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enum to "offset" a cardinal direction.
|
|
/// </summary>
|
|
public enum DirectionOffset
|
|
{
|
|
/// <summary>
|
|
/// No offset.
|
|
/// </summary>
|
|
None = 0,
|
|
|
|
/// <summary>
|
|
/// Rotate direction clockwise. (North -> East, etc...)
|
|
/// </summary>
|
|
Clockwise = 1,
|
|
|
|
/// <summary>
|
|
/// Rotate direction counter-clockwise. (North -> West, etc...)
|
|
/// </summary>
|
|
CounterClockwise = 2,
|
|
|
|
/// <summary>
|
|
/// Rotate direction 180 degrees, so flip. (North -> South, etc...)
|
|
/// </summary>
|
|
Flip = 3,
|
|
}
|
|
|
|
private class Layer
|
|
{
|
|
public ShaderInstance Shader;
|
|
public Texture Texture;
|
|
|
|
public RSI RSI;
|
|
public RSI.StateId State;
|
|
public float AnimationTimeLeft;
|
|
public float AnimationTime;
|
|
public int AnimationFrame;
|
|
public Vector2 Scale = Vector2.One;
|
|
public Angle Rotation;
|
|
public bool Visible = true;
|
|
public Color Color = Color.White;
|
|
public DirectionOffset DirOffset;
|
|
public bool AutoAnimated = true;
|
|
|
|
public Layer()
|
|
{
|
|
Visible = true;
|
|
Scale = Vector2.One;
|
|
Color = Color.White;
|
|
AutoAnimated = true;
|
|
}
|
|
|
|
public Layer(Layer toClone)
|
|
{
|
|
Shader = toClone.Shader;
|
|
Texture = toClone.Texture;
|
|
RSI = toClone.RSI;
|
|
State = toClone.State;
|
|
AnimationTimeLeft = toClone.AnimationTimeLeft;
|
|
AnimationTime = toClone.AnimationTime;
|
|
AnimationFrame = toClone.AnimationFrame;
|
|
Scale = toClone.Scale;
|
|
Rotation = toClone.Rotation;
|
|
Visible = toClone.Visible;
|
|
Color = toClone.Color;
|
|
DirOffset = toClone.DirOffset;
|
|
AutoAnimated = toClone.AutoAnimated;
|
|
}
|
|
}
|
|
|
|
void IAnimationProperties.SetAnimatableProperty(string name, object value)
|
|
{
|
|
if (!name.StartsWith("layer/"))
|
|
{
|
|
AnimationHelper.SetAnimatableProperty(this, name, value);
|
|
return;
|
|
}
|
|
|
|
var delimiter = name.IndexOf("/", 6, StringComparison.Ordinal);
|
|
var indexString = name.Substring(6, delimiter - 6);
|
|
var index = int.Parse(indexString, CultureInfo.InvariantCulture);
|
|
var layerProp = name.Substring(delimiter+1);
|
|
|
|
switch (layerProp)
|
|
{
|
|
case "texture":
|
|
LayerSetTexture(index, (string) value);
|
|
return;
|
|
case "state":
|
|
LayerSetState(index, (string) value);
|
|
return;
|
|
case "color":
|
|
LayerSetColor(index, (Color) value);
|
|
return;
|
|
default:
|
|
throw new ArgumentException($"Unknown layer property '{layerProp}'");
|
|
}
|
|
}
|
|
}
|
|
}
|