Merge pull request #8000 from pchote/bogus-yaml-removals
Fix yaml merging
This commit is contained in:
@@ -16,15 +16,18 @@ using OpenRA.Traits;
|
|||||||
|
|
||||||
namespace OpenRA
|
namespace OpenRA
|
||||||
{
|
{
|
||||||
// TODO: This is not exported into the documentation yet.
|
/// <summary>
|
||||||
[Desc("A unit/building inside the game. Every rules starts with one and adds trait to it.",
|
/// A unit/building inside the game. Every rules starts with one and adds trait to it.
|
||||||
"Special actors like world or player are usually defined in system.yaml and affect everything.")]
|
/// Special actors like world or player are usually defined in system.yaml and affect everything.
|
||||||
|
/// </summary>
|
||||||
public class ActorInfo
|
public class ActorInfo
|
||||||
{
|
{
|
||||||
[Desc("The actor name can be anything, but the sprites used in the Render*: traits default to this one.",
|
/// <summary>
|
||||||
"If you add an ^ in front of the name, the engine will recognize this as a collection of traits",
|
/// The actor name can be anything, but the sprites used in the Render*: traits default to this one.
|
||||||
"that can be inherited by others (using Inherits:) and not a real unit.",
|
/// If you add an ^ in front of the name, the engine will recognize this as a collection of traits
|
||||||
"You can remove inherited traits by adding a - infront of them as in -TraitName: to inherit everything, but this trait.")]
|
/// that can be inherited by others (using Inherits:) and not a real unit.
|
||||||
|
/// You can remove inherited traits by adding a - infront of them as in -TraitName: to inherit everything, but this trait.
|
||||||
|
/// </summary>
|
||||||
public readonly string Name;
|
public readonly string Name;
|
||||||
public readonly TypeDictionary Traits = new TypeDictionary();
|
public readonly TypeDictionary Traits = new TypeDictionary();
|
||||||
List<ITraitInfo> constructOrderCache = null;
|
List<ITraitInfo> constructOrderCache = null;
|
||||||
@@ -33,45 +36,54 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var mergedNode = MergeWithParent(node, allUnits).ToDictionary();
|
var allParents = new HashSet<string>();
|
||||||
|
|
||||||
|
// Guard against circular inheritance
|
||||||
|
allParents.Add(name);
|
||||||
|
var mergedNode = MergeWithParents(node, allUnits, allParents).ToDictionary();
|
||||||
|
|
||||||
Name = name;
|
Name = name;
|
||||||
|
|
||||||
foreach (var t in mergedNode)
|
foreach (var t in mergedNode)
|
||||||
if (t.Key != "Inherits" && !t.Key.StartsWith("-"))
|
{
|
||||||
|
if (t.Key[0] == '-')
|
||||||
|
throw new YamlException("Bogus trait removal: " + t.Key);
|
||||||
|
|
||||||
|
if (t.Key != "Inherits" && !t.Key.StartsWith("Inherits@"))
|
||||||
Traits.Add(LoadTraitInfo(t.Key.Split('@')[0], t.Value));
|
Traits.Add(LoadTraitInfo(t.Key.Split('@')[0], t.Value));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static MiniYaml GetParent(MiniYaml node, Dictionary<string, MiniYaml> allUnits)
|
static Dictionary<string, MiniYaml> GetParents(MiniYaml node, Dictionary<string, MiniYaml> allUnits)
|
||||||
{
|
{
|
||||||
MiniYaml inherits;
|
return node.Nodes.Where(n => n.Key == "Inherits" || n.Key.StartsWith("Inherits@"))
|
||||||
node.ToDictionary().TryGetValue("Inherits", out inherits);
|
.ToDictionary(n => n.Value.Value, n =>
|
||||||
if (inherits == null || string.IsNullOrEmpty(inherits.Value))
|
{
|
||||||
return null;
|
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));
|
||||||
|
|
||||||
MiniYaml parent;
|
return i;
|
||||||
allUnits.TryGetValue(inherits.Value, out parent);
|
});
|
||||||
if (parent == null)
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
"Bogus inheritance -- actor type {0} does not exist".F(inherits.Value));
|
|
||||||
|
|
||||||
return parent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static MiniYaml MergeWithParent(MiniYaml node, Dictionary<string, MiniYaml> allUnits)
|
static MiniYaml MergeWithParents(MiniYaml node, Dictionary<string, MiniYaml> allUnits, HashSet<string> allParents)
|
||||||
{
|
{
|
||||||
var parent = GetParent(node, allUnits);
|
var parents = GetParents(node, allUnits);
|
||||||
if (parent != null)
|
|
||||||
{
|
|
||||||
var result = MiniYaml.MergeStrict(node, MergeWithParent(parent, allUnits));
|
|
||||||
|
|
||||||
// strip the '-'
|
foreach (var kv in parents)
|
||||||
result.Nodes.RemoveAll(a => a.Key.StartsWith("-"));
|
{
|
||||||
return result;
|
if (!allParents.Add(kv.Key))
|
||||||
|
throw new YamlException(
|
||||||
|
"Bogus inheritance -- duplicate inheritance of {0}.".F(kv.Key));
|
||||||
|
|
||||||
|
node = MiniYaml.MergeStrict(node, MergeWithParents(kv.Value, allUnits, allParents));
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
|
|||||||
53
OpenRA.Game/MiniYaml.cs
Executable file → Normal file
53
OpenRA.Game/MiniYaml.cs
Executable file → Normal file
@@ -254,17 +254,17 @@ 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> MergeLiberal(List<MiniYamlNode> a, List<MiniYamlNode> b)
|
public static List<MiniYamlNode> MergeStrict(List<MiniYamlNode> a, List<MiniYamlNode> b)
|
||||||
{
|
{
|
||||||
return Merge(a, b, false);
|
return Merge(a, b, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<MiniYamlNode> MergeStrict(List<MiniYamlNode> a, List<MiniYamlNode> b)
|
public static List<MiniYamlNode> MergeLiberal(List<MiniYamlNode> a, List<MiniYamlNode> b)
|
||||||
{
|
{
|
||||||
return Merge(a, b, true);
|
return Merge(a, b, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<MiniYamlNode> Merge(List<MiniYamlNode> a, List<MiniYamlNode> b, bool throwErrors)
|
static List<MiniYamlNode> Merge(List<MiniYamlNode> a, List<MiniYamlNode> b, bool allowUnresolvedRemoves = false)
|
||||||
{
|
{
|
||||||
if (a.Count == 0)
|
if (a.Count == 0)
|
||||||
return b;
|
return b;
|
||||||
@@ -275,10 +275,11 @@ namespace OpenRA
|
|||||||
|
|
||||||
var dictA = a.ToDictionaryWithConflictLog(x => x.Key, "MiniYaml.Merge", null, x => "{0} (at {1})".F(x.Key, x.Location));
|
var dictA = a.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 dictB = b.ToDictionaryWithConflictLog(x => x.Key, "MiniYaml.Merge", null, x => "{0} (at {1})".F(x.Key, x.Location));
|
||||||
var keys = dictA.Keys.Union(dictB.Keys).ToList();
|
var allKeys = dictA.Keys.Union(dictB.Keys);
|
||||||
|
|
||||||
var noInherit = keys.Where(x => x.Length > 0 && x[0] == '-')
|
var keys = allKeys.Where(x => x.Length == 0 || x[0] != '-').ToList();
|
||||||
.ToDictionary(x => x.Substring(1), x => false);
|
var removeKeys = allKeys.Where(x => x.Length > 0 && x[0] == '-')
|
||||||
|
.Select(k => k.Substring(1)).ToHashSet();
|
||||||
|
|
||||||
foreach (var key in keys)
|
foreach (var key in keys)
|
||||||
{
|
{
|
||||||
@@ -286,47 +287,55 @@ namespace OpenRA
|
|||||||
dictA.TryGetValue(key, out aa);
|
dictA.TryGetValue(key, out aa);
|
||||||
dictB.TryGetValue(key, out bb);
|
dictB.TryGetValue(key, out bb);
|
||||||
|
|
||||||
if (noInherit.ContainsKey(key))
|
if (removeKeys.Contains(key))
|
||||||
{
|
removeKeys.Remove(key);
|
||||||
if (!throwErrors)
|
|
||||||
if (aa != null)
|
|
||||||
ret.Add(aa);
|
|
||||||
|
|
||||||
noInherit[key] = true;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var loc = aa == null ? default(MiniYamlNode.SourceLocation) : aa.Location;
|
var loc = aa == null ? default(MiniYamlNode.SourceLocation) : aa.Location;
|
||||||
var merged = (aa == null || bb == null) ? aa ?? bb : new MiniYamlNode(key, Merge(aa.Value, bb.Value, throwErrors), loc);
|
var merged = (aa == null || bb == null) ? aa ?? bb : new MiniYamlNode(key, Merge(aa.Value, bb.Value, allowUnresolvedRemoves), loc);
|
||||||
ret.Add(merged);
|
ret.Add(merged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (throwErrors && noInherit.ContainsValue(false))
|
if (removeKeys.Any())
|
||||||
throw new YamlException("Bogus yaml removals: {0}".F(
|
{
|
||||||
noInherit.Where(x => !x.Value).JoinWith(", ")));
|
if (allowUnresolvedRemoves)
|
||||||
|
{
|
||||||
|
// Add the removal nodes back for the next pass to deal with
|
||||||
|
foreach (var k in removeKeys)
|
||||||
|
{
|
||||||
|
var key = "-" + k;
|
||||||
|
MiniYamlNode rem;
|
||||||
|
if (!dictA.TryGetValue(key, out rem))
|
||||||
|
rem = dictB[key];
|
||||||
|
ret.Add(rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new YamlException("Bogus yaml removals: {0}".F(removeKeys.JoinWith(", ")));
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MiniYaml MergeLiberal(MiniYaml a, MiniYaml b)
|
public static MiniYaml MergeLiberal(MiniYaml a, MiniYaml b)
|
||||||
{
|
{
|
||||||
return Merge(a, b, false);
|
return Merge(a, b, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MiniYaml MergeStrict(MiniYaml a, MiniYaml b)
|
public static MiniYaml MergeStrict(MiniYaml a, MiniYaml b)
|
||||||
{
|
{
|
||||||
return Merge(a, b, true);
|
return Merge(a, b, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static MiniYaml Merge(MiniYaml a, MiniYaml b, bool throwErrors)
|
static MiniYaml Merge(MiniYaml a, MiniYaml b, bool allowUnresolvedRemoves)
|
||||||
{
|
{
|
||||||
if (a == null)
|
if (a == null)
|
||||||
return b;
|
return b;
|
||||||
if (b == null)
|
if (b == null)
|
||||||
return a;
|
return a;
|
||||||
|
|
||||||
return new MiniYaml(a.Value ?? b.Value, Merge(a.Nodes, b.Nodes, throwErrors));
|
return new MiniYaml(a.Value ?? b.Value, Merge(a.Nodes, b.Nodes, allowUnresolvedRemoves));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> ToLines(string name)
|
public IEnumerable<string> ToLines(string name)
|
||||||
|
|||||||
@@ -21,17 +21,22 @@ namespace OpenRA.Test
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class MiniYamlTest
|
public class MiniYamlTest
|
||||||
{
|
{
|
||||||
readonly string yamlForParent = @"
|
readonly string mixedMergeA = @"
|
||||||
^Parent:
|
Merge:
|
||||||
FromParent:
|
FromA:
|
||||||
FromParentRemove:
|
FromARemovedB:
|
||||||
|
FromARemovedA:
|
||||||
|
-FromBRemovedA:
|
||||||
|
-FromARemovedA:
|
||||||
";
|
";
|
||||||
|
|
||||||
readonly string yamlForChild = @"
|
readonly string mixedMergeB = @"
|
||||||
Child:
|
Merge:
|
||||||
Inherits: ^Parent
|
FromB:
|
||||||
FromChild:
|
FromBRemovedA:
|
||||||
-FromParentRemove:
|
FromBRemovedB:
|
||||||
|
-FromARemovedB:
|
||||||
|
-FromBRemovedB:
|
||||||
";
|
";
|
||||||
|
|
||||||
readonly string yamlTabStyle = @"
|
readonly string yamlTabStyle = @"
|
||||||
@@ -60,58 +65,28 @@ Root2:
|
|||||||
Attribute1: Test
|
Attribute1: Test
|
||||||
";
|
";
|
||||||
|
|
||||||
List<MiniYamlNode> parentList;
|
[TestCase(TestName = "Merging: mixed addition and removal")]
|
||||||
List<MiniYamlNode> childList;
|
|
||||||
MiniYaml parent;
|
|
||||||
MiniYaml child;
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void SetUp()
|
|
||||||
{
|
|
||||||
parentList = MiniYaml.FromString(yamlForParent);
|
|
||||||
childList = MiniYaml.FromString(yamlForChild);
|
|
||||||
parent = parentList.First().Value;
|
|
||||||
child = childList.First().Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InheritanceTest(List<MiniYamlNode> nodes)
|
|
||||||
{
|
|
||||||
Assert.That(nodes.Any(n => n.Key == "FromParent"), Is.True, "Node from parent");
|
|
||||||
Assert.That(nodes.Any(n => n.Key == "FromChild"), Is.True, "Node from child");
|
|
||||||
Assert.That(nodes.Any(n => n.Key == "FromParentRemove"), Is.Not.True, "Node from parent - node from child");
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase(TestName = "MergeStrict(MiniYaml, MiniYaml)")]
|
|
||||||
public void MergeYamlA()
|
public void MergeYamlA()
|
||||||
{
|
{
|
||||||
var res = MiniYaml.MergeStrict(parent, child);
|
var a = MiniYaml.FromString(mixedMergeA, "mixedMergeA");
|
||||||
InheritanceTest(res.Nodes);
|
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 MergeStrict is sufficent.
|
||||||
|
TestMixedMerge(MiniYaml.MergeStrict(a, b).First().Value);
|
||||||
|
TestMixedMerge(MiniYaml.MergeStrict(b, a).First().Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Ignore("Disabled until the code is fixed so we don't break continuous integration.")]
|
void TestMixedMerge(MiniYaml result)
|
||||||
[TestCase(TestName = "MergeLiberal(MiniYaml, MiniYaml)")]
|
|
||||||
public void MergeYamlB()
|
|
||||||
{
|
{
|
||||||
var res = MiniYaml.MergeLiberal(parent, child);
|
Console.WriteLine(result.ToLines("result").JoinWith("\n"));
|
||||||
InheritanceTest(res.Nodes);
|
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");
|
||||||
[Ignore("Disabled until the code is fixed so we don't break continuous integration.")]
|
Assert.That(result.Nodes.Any(n => n.Key == "FromARemovedB"), Is.Not.True, "Node from A removed by B");
|
||||||
[TestCase(TestName = "MergeStrict(List<MiniYamlNode>, List<MiniYamlNode>)")]
|
Assert.That(result.Nodes.Any(n => n.Key == "FromBRemovedA"), Is.Not.True, "Node from B removed by A");
|
||||||
public void MergeYamlC()
|
Assert.That(result.Nodes.Any(n => n.Key == "FromBRemovedB"), Is.Not.True, "Node from B removed by B");
|
||||||
{
|
|
||||||
var res = MiniYaml.MergeStrict(parentList, childList).Last();
|
|
||||||
Assert.That(res.Key, Is.EqualTo("Child"));
|
|
||||||
InheritanceTest(res.Value.Nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Ignore("Disabled until the code is fixed so we don't break continuous integration.")]
|
|
||||||
[TestCase(TestName = "MergeLiberal(List<MiniYamlNode>, List<MiniYamlNode>)")]
|
|
||||||
public void MergeYamlD()
|
|
||||||
{
|
|
||||||
var res = MiniYaml.MergeLiberal(parentList, childList).Last();
|
|
||||||
Assert.That(res.Key, Is.EqualTo("Child"));
|
|
||||||
InheritanceTest(res.Value.Nodes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(TestName = "Mixed tabs & spaces indents")]
|
[TestCase(TestName = "Mixed tabs & spaces indents")]
|
||||||
|
|||||||
@@ -1330,7 +1330,7 @@ Rules:
|
|||||||
Health:
|
Health:
|
||||||
HP: 200
|
HP: 200
|
||||||
E7:
|
E7:
|
||||||
-Selectable:
|
-AnnounceOnKill:
|
||||||
powerproxy.paratroopers:
|
powerproxy.paratroopers:
|
||||||
ParatroopersPower:
|
ParatroopersPower:
|
||||||
DisplayBeacon: false
|
DisplayBeacon: false
|
||||||
|
|||||||
@@ -2102,7 +2102,7 @@ Rules:
|
|||||||
MissionObjectives:
|
MissionObjectives:
|
||||||
EarlyGameOver: true
|
EarlyGameOver: true
|
||||||
World:
|
World:
|
||||||
-CrateDrop:
|
-CrateSpawner:
|
||||||
-SpawnMPUnits:
|
-SpawnMPUnits:
|
||||||
-MPStartLocations:
|
-MPStartLocations:
|
||||||
LuaScript:
|
LuaScript:
|
||||||
|
|||||||
@@ -666,7 +666,7 @@ Rules:
|
|||||||
ParatroopersPower@paratroopers:
|
ParatroopersPower@paratroopers:
|
||||||
ChargeTime: 60
|
ChargeTime: 60
|
||||||
DropItems: E1,E1,E1,E2,E2
|
DropItems: E1,E1,E1,E2,E2
|
||||||
#-RallyPoint: # TODO https://github.com/OpenRA/OpenRA/issues/6818
|
-RallyPoint:
|
||||||
-Sellable:
|
-Sellable:
|
||||||
DOME:
|
DOME:
|
||||||
-Sellable:
|
-Sellable:
|
||||||
|
|||||||
@@ -1029,7 +1029,7 @@ Rules:
|
|||||||
MissionObjectives:
|
MissionObjectives:
|
||||||
EarlyGameOver: true
|
EarlyGameOver: true
|
||||||
World:
|
World:
|
||||||
-CrateDrop:
|
-CrateSpawner:
|
||||||
-SpawnMPUnits:
|
-SpawnMPUnits:
|
||||||
-MPStartLocations:
|
-MPStartLocations:
|
||||||
LuaScript:
|
LuaScript:
|
||||||
|
|||||||
@@ -1,5 +1,40 @@
|
|||||||
^Vehicle:
|
^ExistsInWorld:
|
||||||
AppearsOnRadar:
|
AppearsOnRadar:
|
||||||
|
UpdatesPlayerStatistics:
|
||||||
|
CombatDebugOverlay:
|
||||||
|
DrawLineToTarget:
|
||||||
|
GivesExperience:
|
||||||
|
BodyOrientation:
|
||||||
|
ScriptTriggers:
|
||||||
|
UpgradeManager:
|
||||||
|
Huntable:
|
||||||
|
|
||||||
|
^GainsExperience:
|
||||||
|
GainsExperience:
|
||||||
|
GainsStatUpgrades:
|
||||||
|
SelfHealing@ELITE:
|
||||||
|
Step: 2
|
||||||
|
Ticks: 100
|
||||||
|
HealIfBelow: 1
|
||||||
|
DamageCooldown: 125
|
||||||
|
UpgradeTypes: selfheal
|
||||||
|
UpgradeMinEnabledLevel: 1
|
||||||
|
|
||||||
|
^IronCurtainable:
|
||||||
|
UpgradeOverlay@IRONCURTAIN:
|
||||||
|
UpgradeTypes: invulnerability
|
||||||
|
UpgradeMinEnabledLevel: 1
|
||||||
|
InvulnerabilityUpgrade@IRONCURTAIN:
|
||||||
|
UpgradeTypes: invulnerability
|
||||||
|
UpgradeMinEnabledLevel: 1
|
||||||
|
UpgradeMaxAcceptedLevel: 2
|
||||||
|
TimedUpgradeBar:
|
||||||
|
Upgrade: invulnerability
|
||||||
|
|
||||||
|
^Vehicle:
|
||||||
|
Inherits@1: ^ExistsInWorld
|
||||||
|
Inherits@2: ^GainsExperience
|
||||||
|
Inherits@3: ^IronCurtainable
|
||||||
Mobile:
|
Mobile:
|
||||||
Crushes: mine, crate
|
Crushes: mine, crate
|
||||||
TerrainSpeeds:
|
TerrainSpeeds:
|
||||||
@@ -22,9 +57,6 @@
|
|||||||
CargoType: Vehicle
|
CargoType: Vehicle
|
||||||
AttackMove:
|
AttackMove:
|
||||||
HiddenUnderFog:
|
HiddenUnderFog:
|
||||||
GainsExperience:
|
|
||||||
GivesExperience:
|
|
||||||
DrawLineToTarget:
|
|
||||||
ActorLostNotification:
|
ActorLostNotification:
|
||||||
ProximityCaptor:
|
ProximityCaptor:
|
||||||
Types: Vehicle
|
Types: Vehicle
|
||||||
@@ -32,11 +64,8 @@
|
|||||||
GpsDot:
|
GpsDot:
|
||||||
String: Vehicle
|
String: Vehicle
|
||||||
WithSmoke:
|
WithSmoke:
|
||||||
UpdatesPlayerStatistics:
|
|
||||||
CombatDebugOverlay:
|
|
||||||
Guard:
|
Guard:
|
||||||
Guardable:
|
Guardable:
|
||||||
BodyOrientation:
|
|
||||||
Tooltip:
|
Tooltip:
|
||||||
GenericName: Vehicle
|
GenericName: Vehicle
|
||||||
EjectOnDeath:
|
EjectOnDeath:
|
||||||
@@ -45,36 +74,18 @@
|
|||||||
EjectOnGround: true
|
EjectOnGround: true
|
||||||
EjectInAir: false
|
EjectInAir: false
|
||||||
AllowUnsuitableCell: false
|
AllowUnsuitableCell: false
|
||||||
Huntable:
|
|
||||||
Capturable:
|
Capturable:
|
||||||
Type: vehicle
|
Type: vehicle
|
||||||
CaptureThreshold: 1
|
CaptureThreshold: 1
|
||||||
CancelActivity: True
|
CancelActivity: True
|
||||||
CaptureNotification:
|
CaptureNotification:
|
||||||
Notification: UnitStolen
|
Notification: UnitStolen
|
||||||
ScriptTriggers:
|
|
||||||
GainsStatUpgrades:
|
|
||||||
SelfHealing@ELITE:
|
|
||||||
Step: 2
|
|
||||||
Ticks: 100
|
|
||||||
HealIfBelow: 1
|
|
||||||
DamageCooldown: 125
|
|
||||||
UpgradeTypes: selfheal
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
UpgradeManager:
|
|
||||||
UpgradeOverlay@IRONCURTAIN:
|
|
||||||
UpgradeTypes: invulnerability
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
InvulnerabilityUpgrade@IRONCURTAIN:
|
|
||||||
UpgradeTypes: invulnerability
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
UpgradeMaxAcceptedLevel: 2
|
|
||||||
TimedUpgradeBar:
|
|
||||||
Upgrade: invulnerability
|
|
||||||
MustBeDestroyed:
|
MustBeDestroyed:
|
||||||
|
|
||||||
^Tank:
|
^Tank:
|
||||||
AppearsOnRadar:
|
Inherits@1: ^ExistsInWorld
|
||||||
|
Inherits@2: ^GainsExperience
|
||||||
|
Inherits@3: ^IronCurtainable
|
||||||
Mobile:
|
Mobile:
|
||||||
Crushes: wall, mine, crate
|
Crushes: wall, mine, crate
|
||||||
TerrainSpeeds:
|
TerrainSpeeds:
|
||||||
@@ -97,9 +108,6 @@
|
|||||||
CargoType: Vehicle
|
CargoType: Vehicle
|
||||||
AttackMove:
|
AttackMove:
|
||||||
HiddenUnderFog:
|
HiddenUnderFog:
|
||||||
GainsExperience:
|
|
||||||
GivesExperience:
|
|
||||||
DrawLineToTarget:
|
|
||||||
ActorLostNotification:
|
ActorLostNotification:
|
||||||
ProximityCaptor:
|
ProximityCaptor:
|
||||||
Types: Tank
|
Types: Tank
|
||||||
@@ -107,11 +115,8 @@
|
|||||||
GpsDot:
|
GpsDot:
|
||||||
String: Vehicle
|
String: Vehicle
|
||||||
WithSmoke:
|
WithSmoke:
|
||||||
UpdatesPlayerStatistics:
|
|
||||||
CombatDebugOverlay:
|
|
||||||
Guard:
|
Guard:
|
||||||
Guardable:
|
Guardable:
|
||||||
BodyOrientation:
|
|
||||||
Tooltip:
|
Tooltip:
|
||||||
GenericName: Tank
|
GenericName: Tank
|
||||||
EjectOnDeath:
|
EjectOnDeath:
|
||||||
@@ -120,32 +125,12 @@
|
|||||||
EjectOnGround: true
|
EjectOnGround: true
|
||||||
EjectInAir: false
|
EjectInAir: false
|
||||||
AllowUnsuitableCell: false
|
AllowUnsuitableCell: false
|
||||||
Huntable:
|
|
||||||
Capturable:
|
Capturable:
|
||||||
Type: vehicle
|
Type: vehicle
|
||||||
CaptureThreshold: 1
|
CaptureThreshold: 1
|
||||||
CancelActivity: True
|
CancelActivity: True
|
||||||
CaptureNotification:
|
CaptureNotification:
|
||||||
Notification: UnitStolen
|
Notification: UnitStolen
|
||||||
ScriptTriggers:
|
|
||||||
GainsStatUpgrades:
|
|
||||||
SelfHealing@ELITE:
|
|
||||||
Step: 2
|
|
||||||
Ticks: 100
|
|
||||||
HealIfBelow: 1
|
|
||||||
DamageCooldown: 125
|
|
||||||
UpgradeTypes: selfheal
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
UpgradeManager:
|
|
||||||
UpgradeOverlay@IRONCURTAIN:
|
|
||||||
UpgradeTypes: invulnerability
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
InvulnerabilityUpgrade@IRONCURTAIN:
|
|
||||||
UpgradeTypes: invulnerability
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
UpgradeMaxAcceptedLevel: 2
|
|
||||||
TimedUpgradeBar:
|
|
||||||
Upgrade: invulnerability
|
|
||||||
MustBeDestroyed:
|
MustBeDestroyed:
|
||||||
Parachutable:
|
Parachutable:
|
||||||
ParachuteOffset: 0,0,200
|
ParachuteOffset: 0,0,200
|
||||||
@@ -158,7 +143,8 @@
|
|||||||
WaterCorpsePalette:
|
WaterCorpsePalette:
|
||||||
|
|
||||||
^Infantry:
|
^Infantry:
|
||||||
AppearsOnRadar:
|
Inherits@1: ^ExistsInWorld
|
||||||
|
Inherits@2: ^GainsExperience
|
||||||
Health:
|
Health:
|
||||||
Radius: 128
|
Radius: 128
|
||||||
Armor:
|
Armor:
|
||||||
@@ -189,9 +175,6 @@
|
|||||||
Passenger:
|
Passenger:
|
||||||
CargoType: Infantry
|
CargoType: Infantry
|
||||||
HiddenUnderFog:
|
HiddenUnderFog:
|
||||||
GainsExperience:
|
|
||||||
GivesExperience:
|
|
||||||
DrawLineToTarget:
|
|
||||||
ActorLostNotification:
|
ActorLostNotification:
|
||||||
ProximityCaptor:
|
ProximityCaptor:
|
||||||
Types: Infantry
|
Types: Infantry
|
||||||
@@ -200,11 +183,8 @@
|
|||||||
String: Infantry
|
String: Infantry
|
||||||
Crushable:
|
Crushable:
|
||||||
CrushSound: squishy2.aud
|
CrushSound: squishy2.aud
|
||||||
UpdatesPlayerStatistics:
|
|
||||||
CombatDebugOverlay:
|
|
||||||
Guard:
|
Guard:
|
||||||
Guardable:
|
Guardable:
|
||||||
BodyOrientation:
|
|
||||||
Tooltip:
|
Tooltip:
|
||||||
GenericName: Soldier
|
GenericName: Soldier
|
||||||
SelfHealing@HOSPITAL:
|
SelfHealing@HOSPITAL:
|
||||||
@@ -217,8 +197,6 @@
|
|||||||
GlobalUpgradable:
|
GlobalUpgradable:
|
||||||
Upgrades: hospitalheal
|
Upgrades: hospitalheal
|
||||||
Prerequisites: hosp
|
Prerequisites: hosp
|
||||||
Huntable:
|
|
||||||
ScriptTriggers:
|
|
||||||
DeathSounds@NORMAL:
|
DeathSounds@NORMAL:
|
||||||
DeathTypes: 1, 2, 3, 4
|
DeathTypes: 1, 2, 3, 4
|
||||||
DeathSounds@BURNED:
|
DeathSounds@BURNED:
|
||||||
@@ -236,19 +214,12 @@
|
|||||||
WaterImpactSound: splash9.aud
|
WaterImpactSound: splash9.aud
|
||||||
Cloneable:
|
Cloneable:
|
||||||
Types: Infantry
|
Types: Infantry
|
||||||
GainsStatUpgrades:
|
|
||||||
SelfHealing@ELITE:
|
|
||||||
Step: 2
|
|
||||||
Ticks: 100
|
|
||||||
HealIfBelow: 1
|
|
||||||
DamageCooldown: 125
|
|
||||||
UpgradeTypes: selfheal
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
UpgradeManager:
|
|
||||||
MustBeDestroyed:
|
MustBeDestroyed:
|
||||||
|
|
||||||
^Ship:
|
^Ship:
|
||||||
AppearsOnRadar:
|
Inherits@1: ^ExistsInWorld
|
||||||
|
Inherits@2: ^GainsExperience
|
||||||
|
Inherits@3: ^IronCurtainable
|
||||||
Mobile:
|
Mobile:
|
||||||
Crushes: crate
|
Crushes: crate
|
||||||
TerrainSpeeds:
|
TerrainSpeeds:
|
||||||
@@ -260,9 +231,6 @@
|
|||||||
TargetTypes: Ground, Water, Repair
|
TargetTypes: Ground, Water, Repair
|
||||||
HiddenUnderFog:
|
HiddenUnderFog:
|
||||||
AttackMove:
|
AttackMove:
|
||||||
GainsExperience:
|
|
||||||
GivesExperience:
|
|
||||||
DrawLineToTarget:
|
|
||||||
ActorLostNotification:
|
ActorLostNotification:
|
||||||
Notification: NavalUnitLost
|
Notification: NavalUnitLost
|
||||||
ProximityCaptor:
|
ProximityCaptor:
|
||||||
@@ -271,37 +239,16 @@
|
|||||||
GpsDot:
|
GpsDot:
|
||||||
String: Ship
|
String: Ship
|
||||||
WithSmoke:
|
WithSmoke:
|
||||||
UpdatesPlayerStatistics:
|
|
||||||
CombatDebugOverlay:
|
|
||||||
Guard:
|
Guard:
|
||||||
Guardable:
|
Guardable:
|
||||||
BodyOrientation:
|
|
||||||
Tooltip:
|
Tooltip:
|
||||||
GenericName: Ship
|
GenericName: Ship
|
||||||
Huntable:
|
|
||||||
ScriptTriggers:
|
|
||||||
GainsStatUpgrades:
|
|
||||||
SelfHealing@ELITE:
|
|
||||||
Step: 2
|
|
||||||
Ticks: 100
|
|
||||||
HealIfBelow: 1
|
|
||||||
DamageCooldown: 125
|
|
||||||
UpgradeTypes: selfheal
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
UpgradeManager:
|
|
||||||
UpgradeOverlay@IRONCURTAIN:
|
|
||||||
UpgradeTypes: invulnerability
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
InvulnerabilityUpgrade@IRONCURTAIN:
|
|
||||||
UpgradeTypes: invulnerability
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
UpgradeMaxAcceptedLevel: 2
|
|
||||||
TimedUpgradeBar:
|
|
||||||
Upgrade: invulnerability
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
MustBeDestroyed:
|
MustBeDestroyed:
|
||||||
|
|
||||||
^Plane:
|
^Plane:
|
||||||
|
Inherits@1: ^ExistsInWorld
|
||||||
|
Inherits@2: ^GainsExperience
|
||||||
|
Inherits@3: ^IronCurtainable
|
||||||
AppearsOnRadar:
|
AppearsOnRadar:
|
||||||
UseLocation: true
|
UseLocation: true
|
||||||
SelectionDecorations:
|
SelectionDecorations:
|
||||||
@@ -314,9 +261,6 @@
|
|||||||
AttackMove:
|
AttackMove:
|
||||||
Guard:
|
Guard:
|
||||||
Guardable:
|
Guardable:
|
||||||
GainsExperience:
|
|
||||||
GivesExperience:
|
|
||||||
DrawLineToTarget:
|
|
||||||
ActorLostNotification:
|
ActorLostNotification:
|
||||||
Notification: AirUnitLost
|
Notification: AirUnitLost
|
||||||
ProximityCaptor:
|
ProximityCaptor:
|
||||||
@@ -330,31 +274,8 @@
|
|||||||
GivesBounty:
|
GivesBounty:
|
||||||
GpsDot:
|
GpsDot:
|
||||||
String: Plane
|
String: Plane
|
||||||
UpdatesPlayerStatistics:
|
|
||||||
CombatDebugOverlay:
|
|
||||||
BodyOrientation:
|
|
||||||
Tooltip:
|
Tooltip:
|
||||||
GenericName: Plane
|
GenericName: Plane
|
||||||
Huntable:
|
|
||||||
ScriptTriggers:
|
|
||||||
GainsStatUpgrades:
|
|
||||||
SelfHealing@ELITE:
|
|
||||||
Step: 2
|
|
||||||
Ticks: 100
|
|
||||||
HealIfBelow: 1
|
|
||||||
DamageCooldown: 125
|
|
||||||
UpgradeTypes: selfheal
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
UpgradeManager:
|
|
||||||
UpgradeOverlay@IRONCURTAIN:
|
|
||||||
UpgradeTypes: invulnerability
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
InvulnerabilityUpgrade@IRONCURTAIN:
|
|
||||||
UpgradeTypes: invulnerability
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
UpgradeMaxAcceptedLevel: 2
|
|
||||||
TimedUpgradeBar:
|
|
||||||
Upgrade: invulnerability
|
|
||||||
WithShadow:
|
WithShadow:
|
||||||
MustBeDestroyed:
|
MustBeDestroyed:
|
||||||
|
|
||||||
@@ -367,7 +288,8 @@
|
|||||||
Hovers:
|
Hovers:
|
||||||
|
|
||||||
^Building:
|
^Building:
|
||||||
AppearsOnRadar:
|
Inherits@1: ^ExistsInWorld
|
||||||
|
Inherits@2: ^IronCurtainable
|
||||||
SelectionDecorations:
|
SelectionDecorations:
|
||||||
Selectable:
|
Selectable:
|
||||||
Priority: 3
|
Priority: 3
|
||||||
@@ -393,7 +315,6 @@
|
|||||||
ActorTypes: e1,e1,e1,e1,e1,e1,e1,e1,e1,e1,e1,e1,e1,e1,e1,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,e6,e6,e6,e6,e6
|
ActorTypes: e1,e1,e1,e1,e1,e1,e1,e1,e1,e1,e1,e1,e1,e1,e1,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,e6,e6,e6,e6,e6
|
||||||
MustBeDestroyed:
|
MustBeDestroyed:
|
||||||
RequiredForShortGame: true
|
RequiredForShortGame: true
|
||||||
GivesExperience:
|
|
||||||
CaptureNotification:
|
CaptureNotification:
|
||||||
EditorAppearance:
|
EditorAppearance:
|
||||||
RelativeToTopLeft: true
|
RelativeToTopLeft: true
|
||||||
@@ -404,29 +325,14 @@
|
|||||||
SellSounds: cashturn.aud
|
SellSounds: cashturn.aud
|
||||||
AcceptsSupplies:
|
AcceptsSupplies:
|
||||||
GivesBounty:
|
GivesBounty:
|
||||||
UpdatesPlayerStatistics:
|
|
||||||
CombatDebugOverlay:
|
|
||||||
Guardable:
|
Guardable:
|
||||||
Range: 3
|
Range: 3
|
||||||
BodyOrientation:
|
|
||||||
FrozenUnderFog:
|
FrozenUnderFog:
|
||||||
Tooltip:
|
Tooltip:
|
||||||
GenericName: Structure
|
GenericName: Structure
|
||||||
GpsDot:
|
GpsDot:
|
||||||
String: Structure
|
String: Structure
|
||||||
Huntable:
|
|
||||||
Demolishable:
|
Demolishable:
|
||||||
ScriptTriggers:
|
|
||||||
UpgradeManager:
|
|
||||||
UpgradeOverlay@IRONCURTAIN:
|
|
||||||
UpgradeTypes: invulnerability
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
InvulnerabilityUpgrade@IRONCURTAIN:
|
|
||||||
UpgradeTypes: invulnerability
|
|
||||||
UpgradeMinEnabledLevel: 1
|
|
||||||
UpgradeMaxAcceptedLevel: 2
|
|
||||||
TimedUpgradeBar:
|
|
||||||
Upgrade: invulnerability
|
|
||||||
|
|
||||||
^Defense:
|
^Defense:
|
||||||
Inherits: ^Building
|
Inherits: ^Building
|
||||||
|
|||||||
Reference in New Issue
Block a user