Merge pull request #10648 from pchote/rewrite-yaml-merger
Rewrite yaml merger
This commit is contained in:
@@ -31,21 +31,15 @@ namespace OpenRA
|
|||||||
readonly TypeDictionary traits = new TypeDictionary();
|
readonly TypeDictionary traits = new TypeDictionary();
|
||||||
List<ITraitInfo> constructOrderCache = null;
|
List<ITraitInfo> constructOrderCache = null;
|
||||||
|
|
||||||
public ActorInfo(ObjectCreator creator, string name, MiniYaml node, Dictionary<string, MiniYaml> allUnits)
|
public ActorInfo(ObjectCreator creator, string name, MiniYaml node)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
|
|
||||||
var allParents = new HashSet<string>();
|
|
||||||
var abstractActorType = name.StartsWith("^");
|
var abstractActorType = name.StartsWith("^");
|
||||||
|
foreach (var t in node.Nodes)
|
||||||
// Guard against circular inheritance
|
{
|
||||||
allParents.Add(name);
|
|
||||||
|
|
||||||
var partial = MergeWithParents(node, allUnits, allParents);
|
|
||||||
foreach (var t in MiniYaml.ApplyRemovals(partial.Nodes))
|
|
||||||
if (t.Key != "Inherits" && !t.Key.StartsWith("Inherits@"))
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
traits.Add(LoadTraitInfo(creator, t.Key.Split('@')[0], t.Value));
|
traits.Add(LoadTraitInfo(creator, t.Key.Split('@')[0], t.Value));
|
||||||
@@ -56,6 +50,7 @@ namespace OpenRA
|
|||||||
throw new YamlException(e.Message);
|
throw new YamlException(e.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (YamlException e)
|
catch (YamlException e)
|
||||||
{
|
{
|
||||||
throw new YamlException("Actor type {0}: {1}".F(name, e.Message));
|
throw new YamlException("Actor type {0}: {1}".F(name, e.Message));
|
||||||
@@ -69,36 +64,6 @@ namespace OpenRA
|
|||||||
traits.Add(t);
|
traits.Add(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Dictionary<string, MiniYaml> GetParents(MiniYaml node, Dictionary<string, MiniYaml> allUnits)
|
|
||||||
{
|
|
||||||
return node.Nodes.Where(n => n.Key == "Inherits" || n.Key.StartsWith("Inherits@"))
|
|
||||||
.ToDictionary(n => n.Value.Value, n =>
|
|
||||||
{
|
|
||||||
MiniYaml i;
|
|
||||||
if (!allUnits.TryGetValue(n.Value.Value, out i))
|
|
||||||
throw new YamlException(
|
|
||||||
"Bogus inheritance -- parent type {0} does not exist".F(n.Value.Value));
|
|
||||||
|
|
||||||
return i;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static MiniYaml MergeWithParents(MiniYaml node, Dictionary<string, MiniYaml> allUnits, HashSet<string> allParents)
|
|
||||||
{
|
|
||||||
var parents = GetParents(node, allUnits);
|
|
||||||
|
|
||||||
foreach (var kv in parents)
|
|
||||||
{
|
|
||||||
if (!allParents.Add(kv.Key))
|
|
||||||
throw new YamlException(
|
|
||||||
"Bogus inheritance -- duplicate inheritance of {0}.".F(kv.Key));
|
|
||||||
|
|
||||||
node = MiniYaml.MergePartial(node, MergeWithParents(kv.Value, allUnits, allParents));
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ITraitInfo LoadTraitInfo(ObjectCreator creator, string traitName, MiniYaml my)
|
static ITraitInfo LoadTraitInfo(ObjectCreator creator, string traitName, MiniYaml my)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(my.Value))
|
if (!string.IsNullOrEmpty(my.Value))
|
||||||
|
|||||||
@@ -61,27 +61,27 @@ namespace OpenRA
|
|||||||
using (new PerfTimer("Actors"))
|
using (new PerfTimer("Actors"))
|
||||||
actors = LoadYamlRules(actorCache, m.Rules,
|
actors = LoadYamlRules(actorCache, m.Rules,
|
||||||
map != null ? map.RuleDefinitions : NoMapRules,
|
map != null ? map.RuleDefinitions : NoMapRules,
|
||||||
(k, y) => new ActorInfo(Game.ModData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value, y));
|
k => new ActorInfo(Game.ModData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value));
|
||||||
|
|
||||||
using (new PerfTimer("Weapons"))
|
using (new PerfTimer("Weapons"))
|
||||||
weapons = LoadYamlRules(weaponCache, m.Weapons,
|
weapons = LoadYamlRules(weaponCache, m.Weapons,
|
||||||
map != null ? map.WeaponDefinitions : NoMapRules,
|
map != null ? map.WeaponDefinitions : NoMapRules,
|
||||||
(k, _) => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
|
k => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
|
||||||
|
|
||||||
using (new PerfTimer("Voices"))
|
using (new PerfTimer("Voices"))
|
||||||
voices = LoadYamlRules(voiceCache, m.Voices,
|
voices = LoadYamlRules(voiceCache, m.Voices,
|
||||||
map != null ? map.VoiceDefinitions : NoMapRules,
|
map != null ? map.VoiceDefinitions : NoMapRules,
|
||||||
(k, _) => new SoundInfo(k.Value));
|
k => new SoundInfo(k.Value));
|
||||||
|
|
||||||
using (new PerfTimer("Notifications"))
|
using (new PerfTimer("Notifications"))
|
||||||
notifications = LoadYamlRules(notificationCache, m.Notifications,
|
notifications = LoadYamlRules(notificationCache, m.Notifications,
|
||||||
map != null ? map.NotificationDefinitions : NoMapRules,
|
map != null ? map.NotificationDefinitions : NoMapRules,
|
||||||
(k, _) => new SoundInfo(k.Value));
|
k => new SoundInfo(k.Value));
|
||||||
|
|
||||||
using (new PerfTimer("Music"))
|
using (new PerfTimer("Music"))
|
||||||
music = LoadYamlRules(musicCache, m.Music,
|
music = LoadYamlRules(musicCache, m.Music,
|
||||||
map != null ? map.MusicDefinitions : NoMapRules,
|
map != null ? map.MusicDefinitions : NoMapRules,
|
||||||
(k, _) => new MusicInfo(k.Key, k.Value));
|
k => new MusicInfo(k.Key, k.Value));
|
||||||
|
|
||||||
using (new PerfTimer("TileSets"))
|
using (new PerfTimer("TileSets"))
|
||||||
tileSets = LoadTileSets(tileSetCache, sequenceCaches, m.TileSets);
|
tileSets = LoadTileSets(tileSetCache, sequenceCaches, m.TileSets);
|
||||||
@@ -93,33 +93,30 @@ namespace OpenRA
|
|||||||
Dictionary<string, T> LoadYamlRules<T>(
|
Dictionary<string, T> LoadYamlRules<T>(
|
||||||
Dictionary<string, T> itemCache,
|
Dictionary<string, T> itemCache,
|
||||||
string[] files, List<MiniYamlNode> nodes,
|
string[] files, List<MiniYamlNode> nodes,
|
||||||
Func<MiniYamlNode, Dictionary<string, MiniYaml>, T> f)
|
Func<MiniYamlNode, T> f)
|
||||||
{
|
{
|
||||||
RaiseProgress();
|
RaiseProgress();
|
||||||
|
|
||||||
var inputKey = string.Concat(string.Join("|", files), "|", nodes.WriteToString());
|
var inputKey = string.Concat(string.Join("|", files), "|", nodes.WriteToString());
|
||||||
|
Func<MiniYamlNode, T> wrap = wkv =>
|
||||||
var partial = files
|
|
||||||
.Select(s => MiniYaml.FromFile(s))
|
|
||||||
.Aggregate(nodes, MiniYaml.MergePartial);
|
|
||||||
|
|
||||||
Func<MiniYamlNode, Dictionary<string, MiniYaml>, T> wrap = (wkv, wyy) =>
|
|
||||||
{
|
{
|
||||||
var key = inputKey + wkv.Value.ToLines(wkv.Key).JoinWith("|");
|
var key = inputKey + wkv.Value.ToLines(wkv.Key).JoinWith("|");
|
||||||
T t;
|
T t;
|
||||||
if (itemCache.TryGetValue(key, out t))
|
if (itemCache.TryGetValue(key, out t))
|
||||||
return t;
|
return t;
|
||||||
|
|
||||||
t = f(wkv, wyy);
|
t = f(wkv);
|
||||||
itemCache.Add(key, t);
|
itemCache.Add(key, t);
|
||||||
|
|
||||||
RaiseProgress();
|
RaiseProgress();
|
||||||
return t;
|
return t;
|
||||||
};
|
};
|
||||||
|
|
||||||
var yy = partial.ToDictionary(x => x.Key, x => x.Value);
|
var tree = MiniYaml.Merge(files.Select(MiniYaml.FromFile).Append(nodes))
|
||||||
var itemSet = partial.ToDictionaryWithConflictLog(kv => kv.Key.ToLowerInvariant(), kv => wrap(kv, yy), "LoadYamlRules", null, null);
|
.ToDictionaryWithConflictLog(n => n.Key, n => n.Value, "LoadYamlRules", null, null);
|
||||||
|
RaiseProgress();
|
||||||
|
|
||||||
|
var itemSet = tree.ToDictionary(kv => kv.Key.ToLowerInvariant(), kv => wrap(new MiniYamlNode(kv.Key, kv.Value)));
|
||||||
RaiseProgress();
|
RaiseProgress();
|
||||||
return itemSet;
|
return itemSet;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,12 +33,7 @@ namespace OpenRA.Graphics
|
|||||||
cachedSheets = new Dictionary<string, Sheet>();
|
cachedSheets = new Dictionary<string, Sheet>();
|
||||||
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
|
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
|
||||||
|
|
||||||
var partial = chromeFiles
|
var chrome = MiniYaml.Merge(chromeFiles.Select(MiniYaml.FromFile));
|
||||||
.Select(s => MiniYaml.FromFile(s))
|
|
||||||
.Aggregate(MiniYaml.MergePartial);
|
|
||||||
|
|
||||||
var chrome = MiniYaml.ApplyRemovals(partial);
|
|
||||||
|
|
||||||
foreach (var c in chrome)
|
foreach (var c in chrome)
|
||||||
LoadCollection(c.Key, c.Value);
|
LoadCollection(c.Key, c.Value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,8 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
public CursorProvider(ModData modData)
|
public CursorProvider(ModData modData)
|
||||||
{
|
{
|
||||||
var sequenceFiles = modData.Manifest.Cursors;
|
var sequences = new MiniYaml(null, MiniYaml.Merge(modData.Manifest.Cursors.Select(MiniYaml.FromFile)));
|
||||||
var partial = sequenceFiles
|
|
||||||
.Select(s => MiniYaml.FromFile(s))
|
|
||||||
.Aggregate(MiniYaml.MergePartial);
|
|
||||||
|
|
||||||
var sequences = new MiniYaml(null, MiniYaml.ApplyRemovals(partial));
|
|
||||||
var shadowIndex = new int[] { };
|
var shadowIndex = new int[] { };
|
||||||
|
|
||||||
var nodesDict = sequences.ToDictionary();
|
var nodesDict = sequences.ToDictionary();
|
||||||
|
|||||||
@@ -123,13 +123,7 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
Sequences Load(List<MiniYamlNode> sequenceNodes)
|
Sequences Load(List<MiniYamlNode> sequenceNodes)
|
||||||
{
|
{
|
||||||
var sequenceFiles = modData.Manifest.Sequences;
|
var nodes = MiniYaml.Merge(modData.Manifest.Sequences.Select(MiniYaml.FromFile).Append(sequenceNodes));
|
||||||
|
|
||||||
var partial = sequenceFiles
|
|
||||||
.Select(s => MiniYaml.FromFile(s))
|
|
||||||
.Aggregate(sequenceNodes, MiniYaml.MergePartial);
|
|
||||||
|
|
||||||
var nodes = MiniYaml.ApplyRemovals(partial);
|
|
||||||
var items = new Dictionary<string, UnitSequences>();
|
var items = new Dictionary<string, UnitSequences>();
|
||||||
foreach (var n in nodes)
|
foreach (var n in nodes)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,11 +23,7 @@ namespace OpenRA.Graphics
|
|||||||
{
|
{
|
||||||
units = new Dictionary<string, Dictionary<string, Voxel>>();
|
units = new Dictionary<string, Dictionary<string, Voxel>>();
|
||||||
|
|
||||||
var partial = voxelFiles
|
var sequences = MiniYaml.Merge(voxelFiles.Select(MiniYaml.FromFile));
|
||||||
.Select(s => MiniYaml.FromFile(s))
|
|
||||||
.Aggregate(voxelNodes, MiniYaml.MergePartial);
|
|
||||||
|
|
||||||
var sequences = MiniYaml.ApplyRemovals(partial);
|
|
||||||
foreach (var s in sequences)
|
foreach (var s in sequences)
|
||||||
LoadVoxelsForUnit(s.Key, s.Value);
|
LoadVoxelsForUnit(s.Key, s.Value);
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,11 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
return "{{YamlNode: {0} @ {1}}}".F(Key, Location);
|
return "{{YamlNode: {0} @ {1}}}".F(Key, Location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MiniYamlNode Clone()
|
||||||
|
{
|
||||||
|
return new MiniYamlNode(Key, Value.Clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MiniYaml
|
public class MiniYaml
|
||||||
@@ -89,6 +94,11 @@ namespace OpenRA
|
|||||||
public string Value;
|
public string Value;
|
||||||
public List<MiniYamlNode> Nodes;
|
public List<MiniYamlNode> Nodes;
|
||||||
|
|
||||||
|
public MiniYaml Clone()
|
||||||
|
{
|
||||||
|
return new MiniYaml(Value, Nodes.Select(n => n.Clone()).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
public Dictionary<string, MiniYaml> ToDictionary()
|
public Dictionary<string, MiniYaml> ToDictionary()
|
||||||
{
|
{
|
||||||
return ToDictionary(MiniYamlIdentity);
|
return ToDictionary(MiniYamlIdentity);
|
||||||
@@ -245,89 +255,117 @@ namespace OpenRA
|
|||||||
return FromLines(text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries), fileName);
|
return FromLines(text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries), fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<MiniYamlNode> Merge(List<MiniYamlNode> a, List<MiniYamlNode> b)
|
public static List<MiniYamlNode> Merge(IEnumerable<List<MiniYamlNode>> sources)
|
||||||
{
|
{
|
||||||
return ApplyRemovals(MergePartial(a, b));
|
if (!sources.Any())
|
||||||
|
return new List<MiniYamlNode>();
|
||||||
|
|
||||||
|
var tree = sources.Where(s => s != null).Aggregate(MergePartial)
|
||||||
|
.ToDictionary(n => n.Key, n => n.Value);
|
||||||
|
|
||||||
|
var resolved = new Dictionary<string, MiniYaml>();
|
||||||
|
foreach (var kv in tree)
|
||||||
|
{
|
||||||
|
var inherited = new Dictionary<string, MiniYamlNode.SourceLocation>();
|
||||||
|
inherited.Add(kv.Key, new MiniYamlNode.SourceLocation());
|
||||||
|
|
||||||
|
var children = ResolveInherits(kv.Key, kv.Value, tree, inherited);
|
||||||
|
resolved.Add(kv.Key, new MiniYaml(kv.Value.Value, children));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<MiniYamlNode> MergePartial(List<MiniYamlNode> a, List<MiniYamlNode> b)
|
return resolved.Select(kv => new MiniYamlNode(kv.Key, kv.Value)).ToList();
|
||||||
{
|
}
|
||||||
if (a.Count == 0)
|
|
||||||
return b;
|
|
||||||
|
|
||||||
if (b.Count == 0)
|
static void MergeIntoResolved(MiniYamlNode overrideNode, List<MiniYamlNode> existingNodes,
|
||||||
return a;
|
Dictionary<string, MiniYaml> tree, Dictionary<string, MiniYamlNode.SourceLocation> inherited)
|
||||||
|
{
|
||||||
|
var existingNode = existingNodes.FirstOrDefault(n => n.Key == overrideNode.Key);
|
||||||
|
if (existingNode != null)
|
||||||
|
{
|
||||||
|
existingNode.Value = MiniYaml.MergePartial(existingNode.Value, overrideNode.Value);
|
||||||
|
existingNode.Value.Nodes = ResolveInherits(existingNode.Key, existingNode.Value, tree, inherited);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
existingNodes.Add(overrideNode.Clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<MiniYamlNode> ResolveInherits(string key, MiniYaml node, Dictionary<string, MiniYaml> tree, Dictionary<string, MiniYamlNode.SourceLocation> inherited)
|
||||||
|
{
|
||||||
|
var resolved = new List<MiniYamlNode>();
|
||||||
|
|
||||||
|
// Inheritance is tracked from parent->child, but not from child->parentsiblings.
|
||||||
|
inherited = new Dictionary<string, MiniYamlNode.SourceLocation>(inherited);
|
||||||
|
|
||||||
|
foreach (var n in node.Nodes)
|
||||||
|
{
|
||||||
|
if (n.Key == "Inherits" || n.Key.StartsWith("Inherits@"))
|
||||||
|
{
|
||||||
|
MiniYaml parent;
|
||||||
|
if (!tree.TryGetValue(n.Value.Value, out parent))
|
||||||
|
throw new YamlException(
|
||||||
|
"{0}: Parent type `{1}` not found".F(n.Location, n.Value.Value));
|
||||||
|
|
||||||
|
if (inherited.ContainsKey(n.Value.Value))
|
||||||
|
throw new YamlException("{0}: Parent type `{1}` was already inherited by this yaml tree at {2} (note: may be from a derived tree)"
|
||||||
|
.F(n.Location, n.Value.Value, inherited[n.Value.Value]));
|
||||||
|
|
||||||
|
inherited.Add(n.Value.Value, n.Location);
|
||||||
|
foreach (var r in ResolveInherits(n.Key, parent, tree, inherited))
|
||||||
|
MergeIntoResolved(r, resolved, tree, inherited);
|
||||||
|
}
|
||||||
|
else if (n.Key.StartsWith("-"))
|
||||||
|
{
|
||||||
|
var removed = n.Key.Substring(1);
|
||||||
|
if (resolved.RemoveAll(r => r.Key == removed) == 0)
|
||||||
|
throw new YamlException("{0}: There are no elements with key `{1}` to remove".F(n.Location, removed));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
MergeIntoResolved(n, resolved, tree, inherited);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MiniYaml MergePartial(MiniYaml existingNodes, MiniYaml overrideNodes)
|
||||||
|
{
|
||||||
|
if (existingNodes == null)
|
||||||
|
return overrideNodes;
|
||||||
|
|
||||||
|
if (overrideNodes == null)
|
||||||
|
return existingNodes;
|
||||||
|
|
||||||
|
return new MiniYaml(overrideNodes.Value ?? existingNodes.Value, MergePartial(existingNodes.Nodes, overrideNodes.Nodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<MiniYamlNode> MergePartial(List<MiniYamlNode> existingNodes, List<MiniYamlNode> overrideNodes)
|
||||||
|
{
|
||||||
|
if (existingNodes.Count == 0)
|
||||||
|
return overrideNodes;
|
||||||
|
|
||||||
|
if (overrideNodes.Count == 0)
|
||||||
|
return existingNodes;
|
||||||
|
|
||||||
var ret = new List<MiniYamlNode>();
|
var ret = new List<MiniYamlNode>();
|
||||||
|
|
||||||
var dictA = a.ToDictionaryWithConflictLog(x => x.Key, "MiniYaml.Merge", null, x => "{0} (at {1})".F(x.Key, x.Location));
|
var existingDict = existingNodes.ToDictionaryWithConflictLog(x => x.Key, "MiniYaml.Merge", null, x => "{0} (at {1})".F(x.Key, x.Location));
|
||||||
var dictB = b.ToDictionaryWithConflictLog(x => x.Key, "MiniYaml.Merge", null, x => "{0} (at {1})".F(x.Key, x.Location));
|
var overrideDict = overrideNodes.ToDictionaryWithConflictLog(x => x.Key, "MiniYaml.Merge", null, x => "{0} (at {1})".F(x.Key, x.Location));
|
||||||
var allKeys = dictA.Keys.Union(dictB.Keys);
|
var allKeys = existingDict.Keys.Union(overrideDict.Keys);
|
||||||
|
|
||||||
foreach (var key in allKeys)
|
foreach (var key in allKeys)
|
||||||
{
|
{
|
||||||
MiniYamlNode aa, bb;
|
MiniYamlNode existingNode, overrideNode;
|
||||||
dictA.TryGetValue(key, out aa);
|
existingDict.TryGetValue(key, out existingNode);
|
||||||
dictB.TryGetValue(key, out bb);
|
overrideDict.TryGetValue(key, out overrideNode);
|
||||||
|
|
||||||
var loc = aa == null ? default(MiniYamlNode.SourceLocation) : aa.Location;
|
var loc = overrideNode == null ? default(MiniYamlNode.SourceLocation) : overrideNode.Location;
|
||||||
var merged = (aa == null || bb == null) ? aa ?? bb : new MiniYamlNode(key, MergePartial(aa.Value, bb.Value), loc);
|
var merged = (existingNode == null || overrideNode == null) ? overrideNode ?? existingNode :
|
||||||
|
new MiniYamlNode(key, MergePartial(existingNode.Value, overrideNode.Value), loc);
|
||||||
ret.Add(merged);
|
ret.Add(merged);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<MiniYamlNode> ApplyRemovals(List<MiniYamlNode> a)
|
|
||||||
{
|
|
||||||
var removeKeys = a.Select(x => x.Key)
|
|
||||||
.Where(x => x.Length > 0 && x[0] == '-')
|
|
||||||
.Select(k => k.Substring(1))
|
|
||||||
.ToHashSet();
|
|
||||||
|
|
||||||
var ret = new List<MiniYamlNode>();
|
|
||||||
foreach (var x in a)
|
|
||||||
{
|
|
||||||
if (x.Key[0] == '-')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (removeKeys.Contains(x.Key))
|
|
||||||
removeKeys.Remove(x.Key);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
x.Value.Nodes = ApplyRemovals(x.Value.Nodes);
|
|
||||||
ret.Add(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (removeKeys.Any())
|
|
||||||
throw new YamlException("Bogus yaml removals: {0}".F(removeKeys.JoinWith(", ")));
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MiniYaml MergePartial(MiniYaml a, MiniYaml b)
|
|
||||||
{
|
|
||||||
if (a == null)
|
|
||||||
return b;
|
|
||||||
|
|
||||||
if (b == null)
|
|
||||||
return a;
|
|
||||||
|
|
||||||
return new MiniYaml(a.Value ?? b.Value, MergePartial(a.Nodes, b.Nodes));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MiniYaml Merge(MiniYaml a, MiniYaml b)
|
|
||||||
{
|
|
||||||
if (a == null)
|
|
||||||
return b;
|
|
||||||
|
|
||||||
if (b == null)
|
|
||||||
return a;
|
|
||||||
|
|
||||||
return new MiniYaml(a.Value ?? b.Value, Merge(a.Nodes, b.Nodes));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<string> ToLines(string name)
|
public IEnumerable<string> ToLines(string name)
|
||||||
{
|
{
|
||||||
yield return name + ": " + Value;
|
yield return name + ": " + Value;
|
||||||
|
|||||||
@@ -133,10 +133,9 @@ namespace OpenRA
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var partial = Manifest.Translations.Select(MiniYaml.FromFile).Aggregate(MiniYaml.MergePartial);
|
var yaml = MiniYaml.Merge(Manifest.Translations.Select(MiniYaml.FromFile).Append(map.TranslationDefinitions));
|
||||||
Languages = partial.Select(t => t.Key).ToArray();
|
Languages = yaml.Select(t => t.Key).ToArray();
|
||||||
|
|
||||||
var yaml = MiniYaml.Merge(map.TranslationDefinitions, partial);
|
|
||||||
foreach (var y in yaml)
|
foreach (var y in yaml)
|
||||||
{
|
{
|
||||||
if (y.Key == Game.Settings.Graphics.Language)
|
if (y.Key == Game.Settings.Graphics.Language)
|
||||||
|
|||||||
@@ -20,11 +20,8 @@ namespace OpenRA.Widgets
|
|||||||
public static void Initialize(IEnumerable<string> yaml)
|
public static void Initialize(IEnumerable<string> yaml)
|
||||||
{
|
{
|
||||||
data = new Dictionary<string, string>();
|
data = new Dictionary<string, string>();
|
||||||
var partial = yaml
|
|
||||||
.Select(y => MiniYaml.FromFile(y))
|
|
||||||
.Aggregate(MiniYaml.MergePartial);
|
|
||||||
|
|
||||||
var metrics = MiniYaml.ApplyRemovals(partial);
|
var metrics = MiniYaml.Merge(yaml.Select(MiniYaml.FromFile));
|
||||||
foreach (var m in metrics)
|
foreach (var m in metrics)
|
||||||
foreach (var n in m.Value.Nodes)
|
foreach (var n in m.Value.Nodes)
|
||||||
data[n.Key] = n.Value.Value;
|
data[n.Key] = n.Value.Value;
|
||||||
|
|||||||
@@ -36,20 +36,12 @@ namespace OpenRA.Mods.Common.Graphics
|
|||||||
if (nodes.TryGetValue("Defaults", out defaults))
|
if (nodes.TryGetValue("Defaults", out defaults))
|
||||||
{
|
{
|
||||||
nodes.Remove("Defaults");
|
nodes.Remove("Defaults");
|
||||||
nodes = nodes.ToDictionary(kv => kv.Key, kv => MiniYaml.Merge(kv.Value, defaults));
|
|
||||||
|
|
||||||
// Merge 'Defaults' animation image value. An example follows.
|
|
||||||
//
|
|
||||||
// - Before -
|
|
||||||
// stand:
|
|
||||||
// Facings: 8
|
|
||||||
//
|
|
||||||
// - After -
|
|
||||||
// stand: e1
|
|
||||||
// Facings: 8
|
|
||||||
foreach (var n in nodes)
|
foreach (var n in nodes)
|
||||||
|
{
|
||||||
|
n.Value.Nodes = MiniYaml.Merge(new[] { defaults.Nodes, n.Value.Nodes });
|
||||||
n.Value.Value = n.Value.Value ?? defaults.Value;
|
n.Value.Value = n.Value.Value ?? defaults.Value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var kvp in nodes)
|
foreach (var kvp in nodes)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -31,10 +31,7 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
this.emitError = emitError;
|
this.emitError = emitError;
|
||||||
|
|
||||||
var sequenceSource = map != null ? map.SequenceDefinitions : new List<MiniYamlNode>();
|
var sequenceSource = map != null ? map.SequenceDefinitions : new List<MiniYamlNode>();
|
||||||
var partial = Game.ModData.Manifest.Sequences
|
sequenceDefinitions = MiniYaml.Merge(Game.ModData.Manifest.Sequences.Select(MiniYaml.FromFile).Append(sequenceSource));
|
||||||
.Select(MiniYaml.FromFile)
|
|
||||||
.Aggregate(MiniYaml.MergePartial);
|
|
||||||
sequenceDefinitions = MiniYaml.Merge(sequenceSource, partial);
|
|
||||||
|
|
||||||
var rules = map == null ? Game.ModData.DefaultRules : map.Rules;
|
var rules = map == null ? Game.ModData.DefaultRules : map.Rules;
|
||||||
var factions = rules.Actors["world"].TraitInfos<FactionInfo>().Select(f => f.InternalName).ToArray();
|
var factions = rules.Actors["world"].TraitInfos<FactionInfo>().Select(f => f.InternalName).ToArray();
|
||||||
|
|||||||
@@ -36,13 +36,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
|||||||
var ts = new TileSet(Game.ModData, t);
|
var ts = new TileSet(Game.ModData, t);
|
||||||
Console.WriteLine("Tileset: " + ts.Name);
|
Console.WriteLine("Tileset: " + ts.Name);
|
||||||
var sc = new SpriteCache(modData.SpriteLoaders, new SheetBuilder(SheetType.Indexed));
|
var sc = new SpriteCache(modData.SpriteLoaders, new SheetBuilder(SheetType.Indexed));
|
||||||
var sequenceFiles = modData.Manifest.Sequences;
|
var nodes = MiniYaml.Merge(modData.Manifest.Sequences.Select(MiniYaml.FromFile));
|
||||||
|
|
||||||
var partial = sequenceFiles
|
|
||||||
.Select(s => MiniYaml.FromFile(s))
|
|
||||||
.Aggregate(MiniYaml.MergePartial);
|
|
||||||
|
|
||||||
var nodes = MiniYaml.ApplyRemovals(partial);
|
|
||||||
foreach (var n in nodes)
|
foreach (var n in nodes)
|
||||||
Game.ModData.SpriteSequenceLoader.ParseSequences(Game.ModData, ts, sc, n);
|
Game.ModData.SpriteSequenceLoader.ParseSequences(Game.ModData, ts, sc, n);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
[ObjectCreator.UseCtor]
|
[ObjectCreator.UseCtor]
|
||||||
public MissionBrowserLogic(Widget widget, World world, Action onStart, Action onExit)
|
public MissionBrowserLogic(Widget widget, World world, Action onStart, Action onExit)
|
||||||
{
|
{
|
||||||
|
var modData = Game.ModData;
|
||||||
this.onStart = onStart;
|
this.onStart = onStart;
|
||||||
|
|
||||||
missionList = widget.Get<ScrollPanelWidget>("MISSION_LIST");
|
missionList = widget.Get<ScrollPanelWidget>("MISSION_LIST");
|
||||||
@@ -92,18 +93,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
missionList.RemoveChildren();
|
missionList.RemoveChildren();
|
||||||
|
|
||||||
// Add a group for each campaign
|
// Add a group for each campaign
|
||||||
if (Game.ModData.Manifest.Missions.Any())
|
if (modData.Manifest.Missions.Any())
|
||||||
{
|
{
|
||||||
var partial = Game.ModData.Manifest.Missions
|
var yaml = MiniYaml.Merge(modData.Manifest.Missions.Select(MiniYaml.FromFile));
|
||||||
.Select(MiniYaml.FromFile)
|
|
||||||
.Aggregate(MiniYaml.MergePartial);
|
|
||||||
|
|
||||||
var yaml = MiniYaml.ApplyRemovals(partial);
|
|
||||||
foreach (var kv in yaml)
|
foreach (var kv in yaml)
|
||||||
{
|
{
|
||||||
var missionMapPaths = kv.Value.Nodes.Select(n => Path.GetFullPath(n.Key)).ToList();
|
var missionMapPaths = kv.Value.Nodes.Select(n => Path.GetFullPath(n.Key)).ToList();
|
||||||
|
|
||||||
var maps = Game.ModData.MapCache
|
var maps = modData.MapCache
|
||||||
.Where(p => p.Status == MapStatus.Available && missionMapPaths.Contains(Path.GetFullPath(p.Map.Path)))
|
.Where(p => p.Status == MapStatus.Available && missionMapPaths.Contains(Path.GetFullPath(p.Map.Path)))
|
||||||
.Select(p => p.Map)
|
.Select(p => p.Map)
|
||||||
.OrderBy(m => missionMapPaths.IndexOf(Path.GetFullPath(m.Path)));
|
.OrderBy(m => missionMapPaths.IndexOf(Path.GetFullPath(m.Path)));
|
||||||
@@ -114,7 +111,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add an additional group for loose missions
|
// Add an additional group for loose missions
|
||||||
var looseMissions = Game.ModData.MapCache
|
var looseMissions = modData.MapCache
|
||||||
.Where(p => p.Status == MapStatus.Available && p.Map.Visibility.HasFlag(MapVisibility.MissionSelector) && !allMaps.Contains(p.Map))
|
.Where(p => p.Status == MapStatus.Available && p.Map.Visibility.HasFlag(MapVisibility.MissionSelector) && !allMaps.Contains(p.Map))
|
||||||
.Select(p => p.Map);
|
.Select(p => p.Map);
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ namespace OpenRA.Test
|
|||||||
class MockB2Info : MockTraitInfo { }
|
class MockB2Info : MockTraitInfo { }
|
||||||
class MockC2Info : MockTraitInfo { }
|
class MockC2Info : MockTraitInfo { }
|
||||||
|
|
||||||
|
class MockStringInfo : MockTraitInfo { public string AString = null; }
|
||||||
|
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class ActorInfoTest
|
public class ActorInfoTest
|
||||||
{
|
{
|
||||||
@@ -95,64 +97,19 @@ namespace OpenRA.Test
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(TestName = "Trait inheritance and removal can be composed")]
|
|
||||||
public void TraitInheritanceAndRemovalCanBeComposed()
|
|
||||||
{
|
|
||||||
var baseYaml = @"
|
|
||||||
^BaseA:
|
|
||||||
MockA2:
|
|
||||||
^BaseB:
|
|
||||||
Inherits@a: ^BaseA
|
|
||||||
MockB2:
|
|
||||||
";
|
|
||||||
var extendedYaml = @"
|
|
||||||
Actor:
|
|
||||||
Inherits@b: ^BaseB
|
|
||||||
-MockA2:
|
|
||||||
";
|
|
||||||
var mapYaml = @"
|
|
||||||
^BaseC:
|
|
||||||
MockC2:
|
|
||||||
Actor:
|
|
||||||
Inherits@c: ^BaseC
|
|
||||||
";
|
|
||||||
|
|
||||||
var actorInfo = CreateActorInfoFromYaml("Actor", mapYaml, baseYaml, extendedYaml);
|
|
||||||
Assert.IsFalse(actorInfo.HasTraitInfo<MockA2Info>(), "Actor should not have the MockA2 trait, but does.");
|
|
||||||
Assert.IsTrue(actorInfo.HasTraitInfo<MockB2Info>(), "Actor should have the MockB2 trait, but does not.");
|
|
||||||
Assert.IsTrue(actorInfo.HasTraitInfo<MockC2Info>(), "Actor should have the MockC2 trait, but does not.");
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase(TestName = "Trait can be removed after multiple inheritance")]
|
|
||||||
public void TraitCanBeRemovedAfterMultipleInheritance()
|
|
||||||
{
|
|
||||||
var baseYaml = @"
|
|
||||||
^BaseA:
|
|
||||||
MockA2:
|
|
||||||
Actor:
|
|
||||||
Inherits: ^BaseA
|
|
||||||
MockA2:
|
|
||||||
";
|
|
||||||
var overrideYaml = @"
|
|
||||||
Actor:
|
|
||||||
-MockA2
|
|
||||||
";
|
|
||||||
|
|
||||||
var actorInfo = CreateActorInfoFromYaml("Actor", null, baseYaml, overrideYaml);
|
|
||||||
Assert.IsFalse(actorInfo.HasTraitInfo<MockA2Info>(), "Actor should not have the MockA2 trait, but does.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// This needs to match the logic used in RulesetCache.LoadYamlRules
|
// This needs to match the logic used in RulesetCache.LoadYamlRules
|
||||||
ActorInfo CreateActorInfoFromYaml(string name, string mapYaml, params string[] yamls)
|
ActorInfo CreateActorInfoFromYaml(string name, string mapYaml, params string[] yamls)
|
||||||
{
|
{
|
||||||
var initialNodes = mapYaml == null ? new List<MiniYamlNode>() : MiniYaml.FromString(mapYaml);
|
var nodes = mapYaml == null ? new List<MiniYamlNode>() : MiniYaml.FromString(mapYaml);
|
||||||
var yaml = yamls
|
var sources = yamls.ToList();
|
||||||
.Select(s => MiniYaml.FromString(s))
|
if (mapYaml != null)
|
||||||
.Aggregate(initialNodes, MiniYaml.MergePartial);
|
sources.Add(mapYaml);
|
||||||
|
|
||||||
|
var yaml = MiniYaml.Merge(sources.Select(s => MiniYaml.FromString(s)));
|
||||||
var allUnits = yaml.ToDictionary(node => node.Key, node => node.Value);
|
var allUnits = yaml.ToDictionary(node => node.Key, node => node.Value);
|
||||||
var unit = allUnits[name];
|
var unit = allUnits[name];
|
||||||
var creator = new ObjectCreator(new[] { typeof(ActorInfoTest).Assembly });
|
var creator = new ObjectCreator(new[] { typeof(ActorInfoTest).Assembly });
|
||||||
return new ActorInfo(creator, name, unit, allUnits);
|
return new ActorInfo(creator, name, unit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,24 +17,6 @@ namespace OpenRA.Test
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class MiniYamlTest
|
public class MiniYamlTest
|
||||||
{
|
{
|
||||||
readonly string mixedMergeA = @"
|
|
||||||
Merge:
|
|
||||||
FromA:
|
|
||||||
FromARemovedB:
|
|
||||||
FromARemovedA:
|
|
||||||
-FromBRemovedA:
|
|
||||||
-FromARemovedA:
|
|
||||||
";
|
|
||||||
|
|
||||||
readonly string mixedMergeB = @"
|
|
||||||
Merge:
|
|
||||||
FromB:
|
|
||||||
FromBRemovedA:
|
|
||||||
FromBRemovedB:
|
|
||||||
-FromARemovedB:
|
|
||||||
-FromBRemovedB:
|
|
||||||
";
|
|
||||||
|
|
||||||
readonly string yamlTabStyle = @"
|
readonly string yamlTabStyle = @"
|
||||||
Root1:
|
Root1:
|
||||||
Child1:
|
Child1:
|
||||||
@@ -61,30 +43,6 @@ Root2:
|
|||||||
Attribute1: Test
|
Attribute1: Test
|
||||||
";
|
";
|
||||||
|
|
||||||
[TestCase(TestName = "Merging: mixed addition and removal")]
|
|
||||||
public void MergeYamlA()
|
|
||||||
{
|
|
||||||
var a = MiniYaml.FromString(mixedMergeA, "mixedMergeA");
|
|
||||||
var b = MiniYaml.FromString(mixedMergeB, "mixedMergeB");
|
|
||||||
|
|
||||||
// Merge order should not matter
|
|
||||||
// Note: All the Merge* variants are different plumbing over the same
|
|
||||||
// Internal logic. Testing only Merge is sufficient.
|
|
||||||
TestMixedMerge(MiniYaml.Merge(a, b).First().Value);
|
|
||||||
TestMixedMerge(MiniYaml.Merge(b, a).First().Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestMixedMerge(MiniYaml result)
|
|
||||||
{
|
|
||||||
Console.WriteLine(result.ToLines("result").JoinWith("\n"));
|
|
||||||
Assert.That(result.Nodes.Any(n => n.Key == "FromA"), Is.True, "Node from A");
|
|
||||||
Assert.That(result.Nodes.Any(n => n.Key == "FromB"), Is.True, "Node from B");
|
|
||||||
Assert.That(result.Nodes.Any(n => n.Key == "FromARemovedA"), Is.Not.True, "Node from A removed by A");
|
|
||||||
Assert.That(result.Nodes.Any(n => n.Key == "FromARemovedB"), Is.Not.True, "Node from A removed by B");
|
|
||||||
Assert.That(result.Nodes.Any(n => n.Key == "FromBRemovedA"), Is.Not.True, "Node from B removed by A");
|
|
||||||
Assert.That(result.Nodes.Any(n => n.Key == "FromBRemovedB"), Is.Not.True, "Node from B removed by B");
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase(TestName = "Mixed tabs & spaces indents")]
|
[TestCase(TestName = "Mixed tabs & spaces indents")]
|
||||||
public void TestIndents()
|
public void TestIndents()
|
||||||
{
|
{
|
||||||
@@ -94,5 +52,106 @@ Root2:
|
|||||||
Console.WriteLine(mixed);
|
Console.WriteLine(mixed);
|
||||||
Assert.That(tabs, Is.EqualTo(mixed));
|
Assert.That(tabs, Is.EqualTo(mixed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase(TestName = "Inheritance and removal can be composed")]
|
||||||
|
public void InheritanceAndRemovalCanBeComposed()
|
||||||
|
{
|
||||||
|
var baseYaml = @"
|
||||||
|
^BaseA:
|
||||||
|
MockA2:
|
||||||
|
^BaseB:
|
||||||
|
Inherits@a: ^BaseA
|
||||||
|
MockB2:
|
||||||
|
";
|
||||||
|
var extendedYaml = @"
|
||||||
|
Test:
|
||||||
|
Inherits@b: ^BaseB
|
||||||
|
-MockA2:
|
||||||
|
";
|
||||||
|
var mapYaml = @"
|
||||||
|
^BaseC:
|
||||||
|
MockC2:
|
||||||
|
Test:
|
||||||
|
Inherits@c: ^BaseC
|
||||||
|
";
|
||||||
|
var result = MiniYaml.Merge(new[] { baseYaml, extendedYaml, mapYaml }.Select(s => MiniYaml.FromString(s, "")))
|
||||||
|
.First(n => n.Key == "Test").Value.Nodes;
|
||||||
|
|
||||||
|
Assert.IsFalse(result.Any(n => n.Key == "MockA2"), "Node should not have the MockA2 child, but does.");
|
||||||
|
Assert.IsTrue(result.Any(n => n.Key == "MockB2"), "Node should have the MockB2 child, but does not.");
|
||||||
|
Assert.IsTrue(result.Any(n => n.Key == "MockC2"), "Node should have the MockC2 child, but does not.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(TestName = "Child can be removed after multiple inheritance")]
|
||||||
|
public void ChildCanBeRemovedAfterMultipleInheritance()
|
||||||
|
{
|
||||||
|
var baseYaml = @"
|
||||||
|
^BaseA:
|
||||||
|
MockA2:
|
||||||
|
Test:
|
||||||
|
Inherits: ^BaseA
|
||||||
|
MockA2:
|
||||||
|
";
|
||||||
|
var overrideYaml = @"
|
||||||
|
Test:
|
||||||
|
-MockA2
|
||||||
|
";
|
||||||
|
|
||||||
|
var result = MiniYaml.Merge(new[] { baseYaml, overrideYaml }.Select(s => MiniYaml.FromString(s, "")))
|
||||||
|
.First(n => n.Key == "Test").Value.Nodes;
|
||||||
|
|
||||||
|
Assert.IsFalse(result.Any(n => n.Key == "MockA2"), "Node should not have the MockA2 child, but does.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(TestName = "Child can be removed and later overridden")]
|
||||||
|
public void ChildCanBeRemovedAndLaterOverridden()
|
||||||
|
{
|
||||||
|
var baseYaml = @"
|
||||||
|
^BaseA:
|
||||||
|
MockString:
|
||||||
|
AString: Base
|
||||||
|
Test:
|
||||||
|
Inherits: ^BaseA
|
||||||
|
-MockString:
|
||||||
|
";
|
||||||
|
var overrideYaml = @"
|
||||||
|
Test:
|
||||||
|
MockString:
|
||||||
|
AString: Override
|
||||||
|
";
|
||||||
|
|
||||||
|
var result = MiniYaml.Merge(new[] { baseYaml, overrideYaml }.Select(s => MiniYaml.FromString(s, "")))
|
||||||
|
.First(n => n.Key == "Test").Value.Nodes;
|
||||||
|
|
||||||
|
Assert.IsTrue(result.Any(n => n.Key == "MockString"), "Node should have the MockString child, but does not.");
|
||||||
|
Assert.IsTrue(result.First(n => n.Key == "MockString").Value.ToDictionary()["AString"].Value == "Override",
|
||||||
|
"MockString value has not been set with the correct override value for AString.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(TestName = "Child can be removed from intermediate parent")]
|
||||||
|
public void ChildCanBeOverriddenThenRemoved()
|
||||||
|
{
|
||||||
|
var baseYaml = @"
|
||||||
|
^BaseA:
|
||||||
|
MockString:
|
||||||
|
AString: Base
|
||||||
|
^BaseB:
|
||||||
|
Inherits: ^BaseA
|
||||||
|
MockString:
|
||||||
|
AString: Override
|
||||||
|
";
|
||||||
|
var overrideYaml = @"
|
||||||
|
Test:
|
||||||
|
Inherits: ^BaseB
|
||||||
|
MockString:
|
||||||
|
-AString:
|
||||||
|
";
|
||||||
|
|
||||||
|
var result = MiniYaml.Merge(new[] { baseYaml, overrideYaml }.Select(s => MiniYaml.FromString(s, "")))
|
||||||
|
.First(n => n.Key == "Test").Value.Nodes;
|
||||||
|
Assert.IsTrue(result.Any(n => n.Key == "MockString"), "Node should have the MockString child, but does not.");
|
||||||
|
Assert.IsFalse(result.First(n => n.Key == "MockString").Value.Nodes.Any(n => n.Key == "AString"),
|
||||||
|
"MockString value should have been removed, but was not.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,37 +32,7 @@ vice:
|
|||||||
icon: viceicnh
|
icon: viceicnh
|
||||||
|
|
||||||
pvice:
|
pvice:
|
||||||
idle:
|
Inherits: vice
|
||||||
Length: *
|
|
||||||
muzzle:
|
|
||||||
Combine:
|
|
||||||
chem-n:
|
|
||||||
Length: *
|
|
||||||
Offset: 1,2
|
|
||||||
chem-nw:
|
|
||||||
Length: *
|
|
||||||
Offset: 8,2
|
|
||||||
chem-w:
|
|
||||||
Length: *
|
|
||||||
Offset: 8,-3
|
|
||||||
chem-sw:
|
|
||||||
Length: *
|
|
||||||
Offset: 7,-6
|
|
||||||
chem-s:
|
|
||||||
Length: *
|
|
||||||
Offset: 1,-6
|
|
||||||
chem-se:
|
|
||||||
Length: *
|
|
||||||
Offset: -5,-6
|
|
||||||
chem-e:
|
|
||||||
Length: *
|
|
||||||
Offset: -7,-3
|
|
||||||
chem-ne:
|
|
||||||
Length: *
|
|
||||||
Offset: -3,2
|
|
||||||
Facings: 8
|
|
||||||
Length: 13
|
|
||||||
icon: viceicnh
|
|
||||||
|
|
||||||
e1:
|
e1:
|
||||||
stand:
|
stand:
|
||||||
|
|||||||
@@ -1710,9 +1710,8 @@ Rules:
|
|||||||
RenderSprites:
|
RenderSprites:
|
||||||
Image: E7
|
Image: E7
|
||||||
Colt:
|
Colt:
|
||||||
-Huntable:
|
|
||||||
AutoTargetIgnore:
|
|
||||||
Inherits: ^Defense
|
Inherits: ^Defense
|
||||||
|
AutoTargetIgnore:
|
||||||
Valued:
|
Valued:
|
||||||
Cost: 800
|
Cost: 800
|
||||||
Building:
|
Building:
|
||||||
@@ -1736,6 +1735,7 @@ Rules:
|
|||||||
AttackTurreted:
|
AttackTurreted:
|
||||||
AutoTarget:
|
AutoTarget:
|
||||||
-Selectable:
|
-Selectable:
|
||||||
|
-Huntable:
|
||||||
E1.Autotarget:
|
E1.Autotarget:
|
||||||
Inherits: E1
|
Inherits: E1
|
||||||
Buildable:
|
Buildable:
|
||||||
|
|||||||
Reference in New Issue
Block a user