Exemple #1
0
        public Widget LoadWidget(WidgetArgs args, Widget parent, MiniYamlNode node)
        {
            if (!args.ContainsKey("modData"))
                args = new WidgetArgs(args) { { "modData", modData } };

            var widget = NewWidget(node.Key, args);

            if (parent != null)
                parent.AddChild(widget);

            if (node.Key.Contains("@"))
                FieldLoader.LoadField(widget, "Id", node.Key.Split('@')[1]);

            foreach (var child in node.Value.Nodes)
                if (child.Key != "Children")
                    FieldLoader.LoadField(widget, child.Key, child.Value.Value);

            widget.Initialize(args);

            foreach (var child in node.Value.Nodes)
                if (child.Key == "Children")
                    foreach (var c in child.Value.Nodes)
                        LoadWidget(args, widget, c);

            var logicNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Logic");
            var logic = logicNode == null ? null : logicNode.Value.ToDictionary();
            args.Add("logicArgs", logic);

            widget.PostInit(args);

            args.Remove("logicArgs");

            return widget;
        }
Exemple #2
0
		public Widget LoadWidget(WidgetArgs args, Widget parent, MiniYamlNode node)
		{
			var widget = NewWidget(node.Key, args);

			if (parent != null)
				parent.AddChild( widget );

			if (node.Key.Contains("@"))
				FieldLoader.LoadField(widget, "Id", node.Key.Split('@')[1]);

			foreach (var child in node.Value.Nodes)
				if (child.Key != "Children")
					FieldLoader.LoadField(widget, child.Key, child.Value.Value);

			if (!args.ContainsKey("modRules"))
				args = new WidgetArgs(args) { { "modRules", modData.DefaultRules } };
			widget.Initialize(args);

			foreach (var child in node.Value.Nodes)
				if (child.Key == "Children")
					foreach (var c in child.Value.Nodes)
						LoadWidget( args, widget, c);

			widget.PostInit(args);
			return widget;
		}
Exemple #3
0
        internal static void RenameDamageTypes(MiniYamlNode damageTypes)
        {
            var mod = Game.ModData.Manifest.Mod.Id;
            if (mod == "cnc" || mod == "ra")
            {
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType1", "DefaultDeath");
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType2", "BulletDeath");
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType3", "SmallExplosionDeath");
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType4", "ExplosionDeath");
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType5", "FireDeath");
            }

            if (mod == "cnc")
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType6", "TiberiumDeath");

            if (mod == "ra")
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType6", "ElectricityDeath");

            if (mod == "d2k")
            {
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType1", "ExplosionDeath");
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType2", "SoundDeath");
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType3", "SmallExplosionDeath");
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType4", "BulletDeath");
            }

            if (mod == "ts")
            {
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType1", "BulletDeath");
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType2", "SmallExplosionDeath");
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType3", "ExplosionDeath");
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType5", "FireDeath");
                damageTypes.Value.Value = damageTypes.Value.Value.Replace("DeathType6", "EnergyDeath");
            }
        }
Exemple #4
0
        public Widget LoadWidget( Dictionary<string, object> args, Widget parent, MiniYamlNode node)
        {
            var widget = NewWidget(node.Key, args);
            parent.AddChild( widget );

            foreach (var child in node.Value.Nodes)
                if (child.Key != "Children")
                    FieldLoader.LoadField(widget, child.Key, child.Value.Value);

            widget.Initialize();

            foreach (var child in node.Value.Nodes)
                if (child.Key == "Children")
                    foreach (var c in child.Value.Nodes)
                        LoadWidget( args, widget, c);

            widget.PostInit( args );
            return widget;
        }
Exemple #5
0
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            foreach (var pb in actorNode.ChildrenMatching("ProductionBar"))
            {
                var type = pb.LastChildMatching("ProductionType");
                if (type != null)
                {
                    continue;
                }

                MiniYamlNode production = null;
                foreach (var trait in productionTraits)
                {
                    if (production != null)
                    {
                        break;
                    }

                    production = actorNode.ChildrenMatching(trait).FirstOrDefault();
                }

                if (production == null)
                {
                    continue;
                }

                var produces = production.LastChildMatching("Produces");
                if (produces == null)
                {
                    continue;
                }

                var toAdd = produces.NodeValue <string[]>().FirstOrDefault();
                if (toAdd != null)
                {
                    pb.AddNode("ProductionType", toAdd);
                }
            }

            yield break;
        }
Exemple #6
0
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            foreach (var aircraft in actorNode.ChildrenMatching("Aircraft"))
            {
                var canHover = aircraft.LastChildMatching("CanHover");
                if (canHover != null && canHover.NodeValue <bool>())
                {
                    if (!aircraft.ChildrenMatching("TakeOffOnResupply").Any())
                    {
                        aircraft.AddNode("TakeOffOnResupply", true);
                    }

                    if (!aircraft.ChildrenMatching("VTOL").Any())
                    {
                        aircraft.AddNode("VTOL", true);
                    }
                }
            }

            yield break;
        }
        IReadOnlyDictionary<string, Sequence> CreateUnitSequences(MiniYamlNode node)
        {
            var unitSequences = new Dictionary<string, Sequence>();

            foreach (var kvp in node.Value.ToDictionary())
            {
                using (new Support.PerfTimer("new Sequence(\"{0}\")".F(node.Key), 20))
                {
                    try
                    {
                        unitSequences.Add(kvp.Key, new Sequence(spriteCache.Value, node.Key, kvp.Key, kvp.Value));
                    }
                    catch (FileNotFoundException ex)
                    {
                        Log.Write("debug", ex.Message);
                    }
                }
            }

            return new ReadOnlyDictionary<string, Sequence>(unitSequences);
        }
Exemple #8
0
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            var grants = actorNode.ChildrenMatching("GrantConditionOnDeploy");

            foreach (var g in grants)
            {
                var deploy = g.LastChildMatching("DeploySound");
                if (deploy != null)
                {
                    deploy.RenameKey("DeploySounds");
                }

                var undeploy = g.LastChildMatching("UndeploySound");
                if (undeploy != null)
                {
                    undeploy.RenameKey("UndeploySounds");
                }
            }

            yield break;
        }
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            foreach (var a in actorNode.ChildrenMatching("Armament"))
            {
                var muzzleSplitFacings = a.LastChildMatching("MuzzleSplitFacings");
                var sequenceNode       = a.LastChildMatching("MuzzleSequence");
                if (muzzleSplitFacings != null && sequenceNode != null)
                {
                    var sequence = sequenceNode.Value.Value;
                    var facings  = muzzleSplitFacings.NodeValue <int>() - 1;
                    var actor    = actorNode.Key.ToLowerInvariant();
                    yield return("The Armament muzzle effect has been removed from {0} ({1}).\n".F(actor, actorNode.Location.Filename) +
                                 "If you would like to restore the muzzle effect you must redefine `MuzzleSequence: {0}`\n".F(sequence) +
                                 "and replace the {0}0-{1} sequence definitions with a single `{0}` sequence that uses\n".F(sequence, facings) +
                                 "the Combine syntax to assemble the different facing sprites.");

                    a.RemoveNode(muzzleSplitFacings);
                    a.RemoveNode(sequenceNode);
                }
            }
        }
Exemple #10
0
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            var aircraft = actorNode.LastChildMatching("Aircraft");

            if (aircraft != null)
            {
                var attackAircraft = actorNode.LastChildMatching("AttackAircraft");
                if (attackAircraft == null)
                {
                    yield break;
                }

                var attackType = attackAircraft.LastChildMatching("AttackType");
                if (attackType.NodeValue <AirAttackType>() == AirAttackType.Strafe)
                {
                    attackAircraft.RemoveNode(attackType);
                }

                attackAircraft.RemoveNodes("AttackTurnDelay");
            }
        }
Exemple #11
0
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            var harvAnim = actorNode.LastChildMatching("WithHarvestAnimation");

            if (harvAnim != null)
            {
                var fullnessPrefix = harvAnim.LastChildMatching("PrefixByFullness");

                // If PrefixByFullness is empty, no changes are needed.
                if (fullnessPrefix == null)
                {
                    yield break;
                }

                harvAnim.RemoveNode(fullnessPrefix);

                fullnessPrefixes.Add(Tuple.Create(actorNode.Key, actorNode.Location.Filename));
            }

            yield break;
        }
        public Widget LoadWidget( WidgetArgs args, Widget parent, MiniYamlNode node)
        {
            var widget = NewWidget(node.Key, args);

            if (parent != null)
                parent.AddChild( widget );

            foreach (var child in node.Value.Nodes)
                if (child.Key != "Children")
                    FieldLoader.LoadField(widget, child.Key, child.Value.Value);

            widget.Initialize(args);

            foreach (var child in node.Value.Nodes)
                if (child.Key == "Children")
                    foreach (var c in child.Value.Nodes)
                        LoadWidget( args, widget, c);

            widget.PostInit( args );
            return widget;
        }
        IReadOnlyDictionary <string, Sequence> CreateUnitSequences(MiniYamlNode node)
        {
            var unitSequences = new Dictionary <string, Sequence>();

            foreach (var kvp in node.Value.ToDictionary())
            {
                using (new Support.PerfTimer("new Sequence(\"{0}\")".F(node.Key), 20))
                {
                    try
                    {
                        unitSequences.Add(kvp.Key, new Sequence(spriteLoader.Value, node.Key, kvp.Key, kvp.Value));
                    }
                    catch (FileNotFoundException ex)
                    {
                        Log.Write("debug", ex.Message);
                    }
                }
            }

            return(new ReadOnlyDictionary <string, Sequence>(unitSequences));
        }
Exemple #14
0
        public override IEnumerable <string> UpdateMapActorNode(ModData modData, MiniYamlNode actorNode)
        {
            if (actorNode.RemoveNodes("Plugs") > 0)
            {
                yield return("Initial plugs for actor {0} will need to be reconfigured using the map editor.".F(actorNode.Key));
            }

            if (actorNode.RemoveNodes("TurretFacings") > 0)
            {
                yield return("Initial turret facings for actor {0} will need to be reconfigured using the map editor.".F(actorNode.Key));
            }

            var bodyFacing = WAngle.Zero;

            foreach (var facing in actorNode.ChildrenMatching("Facing"))
            {
                bodyFacing = WAngle.FromFacing(facing.NodeValue <int>());
                facing.ReplaceValue(FieldSaver.FormatValue(bodyFacing));
            }

            var removeNodes = new List <MiniYamlNode>();

            foreach (var facing in actorNode.ChildrenMatching("TurretFacing"))
            {
                var turretFacing = WAngle.FromFacing(facing.NodeValue <int>()) - bodyFacing;
                if (turretFacing == WAngle.Zero)
                {
                    removeNodes.Add(facing);
                }
                else
                {
                    facing.ReplaceValue(FieldSaver.FormatValue(turretFacing));
                }
            }

            foreach (var node in removeNodes)
            {
                actorNode.Value.Nodes.Remove(node);
            }
        }
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            if (actorNode.Key != "Player")
            {
                yield break;
            }

            var hackyAIs = actorNode.ChildrenMatching("HackyAI");

            if (!hackyAIs.Any())
            {
                yield break;
            }

            foreach (var hackyAINode in hackyAIs)
            {
                // We no longer support individual values for each AI,
                // and in practice the default of 5 has proven to be a solid middle-ground,
                // so just removing any custom value and notifying the modder about it should suffice.
                var minQuotient = hackyAINode.LastChildMatching("MinOrderQuotientPerTick");
                if (minQuotient != null)
                {
                    hackyAINode.RemoveNode(minQuotient);
                    if (!showMessage)
                    {
                        showMessage = true;
                    }
                }
            }

            var botOrderManager = actorNode.LastChildMatching("BotOrderManager");

            if (botOrderManager == null)
            {
                var addBotOrderManager = new MiniYamlNode("BotOrderManager", "");
                actorNode.AddNode(addBotOrderManager);
            }

            yield break;
        }
        void LoadBriefing(IniFile file)
        {
            var briefingSection = file.GetSection("Briefing", true);

            if (briefingSection == null)
            {
                return;
            }

            var briefing = new StringBuilder();

            foreach (var s in briefingSection)
            {
                var line = s.Value.Replace("@", "\n");
                briefing.AppendLine(line);
            }

            if (briefing.Length == 0)
            {
                return;
            }

            var worldNode = Map.RuleDefinitions.Nodes.FirstOrDefault(n => n.Key == "World");

            if (worldNode == null)
            {
                worldNode = new MiniYamlNode("World", new MiniYaml("", new List <MiniYamlNode>()));
                Map.RuleDefinitions.Nodes.Add(worldNode);
            }

            var missionData = worldNode.Value.Nodes.FirstOrDefault(n => n.Key == "MissionData");

            if (missionData == null)
            {
                missionData = new MiniYamlNode("MissionData", new MiniYaml("", new List <MiniYamlNode>()));
                worldNode.Value.Nodes.Add(missionData);
            }

            missionData.Value.Nodes.Add(new MiniYamlNode("Briefing", briefing.Replace("\n", " ").ToString()));
        }
Exemple #17
0
        public override IEnumerable <string> UpdateChromeProviderNode(ModData modData, MiniYamlNode chromeProviderNode)
        {
            // Migrate image rectangles
            var regionsNode = new MiniYamlNode("Regions", "");

            foreach (var n in chromeProviderNode.Value.Nodes)
            {
                if (n.Key == "Inherits")
                {
                    continue;
                }

                // Reformat region as a list
                regionsNode.AddNode(n.Key, n.NodeValue <int[]>());

                if (n.Value.Nodes.Any())
                {
                    overrideLocations.Add("{0}.{1} ({2})".F(chromeProviderNode.Key, n.Key, chromeProviderNode.Location.Filename));
                }
            }

            chromeProviderNode.Value.Nodes.RemoveAll(n => n.Key != "Inherits");

            // Migrate image definition
            chromeProviderNode.AddNode(new MiniYamlNode("Image", chromeProviderNode.Value.Value));
            chromeProviderNode.Value.Value = "";

            if (!ExtractPanelDefinition(chromeProviderNode, regionsNode))
            {
                panelLocations.Add("{0} ({1})".F(chromeProviderNode.Key, chromeProviderNode.Location.Filename));
            }

            if (regionsNode.Value.Nodes.Any())
            {
                chromeProviderNode.AddNode(regionsNode);
            }

            yield break;
        }
Exemple #18
0
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            if (actorNode.ChildrenMatching("ResourceLayer").Any() && !actorNode.ChildrenMatching("ResourceRenderer").Any())
            {
                locations.Add($"{actorNode.Key} ({actorNode.Location.Filename})");
                var resourceRenderer = new MiniYamlNode("ResourceRenderer", "");
                resourceRenderer.AddNode("RenderTypes", "");
                actorNode.AddNode(resourceRenderer);
            }

            if (actorNode.ChildrenMatching("D2kResourceLayer").Any() && !actorNode.ChildrenMatching("D2kResourceRenderer").Any())
            {
                actorNode.RenameChildrenMatching("D2kResourceLayer", "ResourceLayer");

                locations.Add($"{actorNode.Key} ({actorNode.Location.Filename})");
                var resourceRenderer = new MiniYamlNode("D2kResourceRenderer", "");
                resourceRenderer.AddNode("RenderTypes", "");
                actorNode.AddNode(resourceRenderer);
            }

            yield break;
        }
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            var nukePowerTraits = actorNode.ChildrenMatching("NukePower");

            foreach (var nukePowerTrait in nukePowerTraits)
            {
                var traitName  = nukePowerTrait.Key;
                var weaponNode = nukePowerTrait.ChildrenMatching("MissileWeapon").FirstOrDefault();
                if (weaponNode == null)
                {
                    continue;
                }

                var weaponName = weaponNode.Value.Value;

                weaponsToUpdate.Add(new Tuple <string, string, string>(weaponName, traitName, "{0} ({1})".F(actorNode.Key, actorNode.Location.Filename)));

                nukePowerTrait.RemoveNodes("FlashType");
            }

            yield break;
        }
Exemple #20
0
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            var aircraftTraits = actorNode.ChildrenMatching("Aircraft");

            foreach (var aircraft in aircraftTraits)
            {
                var canHover     = false;
                var canHoverNode = aircraft.LastChildMatching("CanHover");
                if (canHoverNode != null)
                {
                    canHover = canHoverNode.NodeValue <bool>();
                }
                else
                {
                    yield break;
                }

                aircraft.AddNode("CanSlide", canHover.ToString());
            }

            yield break;
        }
        internal static void FromChromeLayout(ref List<MiniYamlNode> nodes, MiniYamlNode parent, IEnumerable<string> translatables, string container)
        {
            var parentNode = parent != null ? parent.Key.Split('@') : null;
            var parentType = parent != null ? parentNode.First() : null;
            var parentLabel = parent != null ? parentNode.Last() : null;

            if ((parentType == "Background" || parentType == "Container") && parentLabel.IsUppercase())
                container = parentLabel;

            foreach (var node in nodes)
            {
                var alreadyTranslated = node.Value.Value != null && node.Value.Value.Contains('@');
                if (translatables.Contains(node.Key) && !alreadyTranslated && parentLabel != null)
                {
                    var translationKey = "{0}-{1}-{2}".F(container.Replace('_', '-'), parentLabel.Replace('_', '-'), node.Key.ToUpper());
                    Console.WriteLine("\t{0}: {1}", translationKey, node.Value.Value);
                    node.Value.Value = "@{0}@".F(translationKey);
                }

                FromChromeLayout(ref node.Value.Nodes, node, translatables, container);
            }
        }
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            foreach (var wr in actorNode.ChildrenMatching("WithResources"))
            {
                wr.RenameKey("WithResourceLevelOverlay");
            }

            var siloAnims = actorNode.ChildrenMatching("WithSiloAnimation");

            foreach (var sa in siloAnims)
            {
                // If it's a trait removal, we only rename it.
                if (sa.IsRemoval())
                {
                    sa.RenameKey("WithResourceLevelSpriteBody");
                    continue;
                }

                var sequence = sa.LastChildMatching("Sequence");
                var body     = sa.LastChildMatching("Body");

                if (sequence == null)
                {
                    var newSequenceNode = new MiniYamlNode("Sequence", "stages");
                    sa.AddNode(newSequenceNode);
                }

                if (body != null)
                {
                    sa.RemoveNode(body);
                }

                sa.RenameKey("WithResourceLevelSpriteBody");
                locations.Add("{0} ({1})".F(actorNode.Key, sa.Location.Filename));
            }

            yield break;
        }
Exemple #23
0
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            foreach (var t in traits)
            {
                foreach (var traitNode in actorNode.ChildrenMatching(t.Trait))
                {
                    foreach (var f in t.Fields)
                    {
                        var node = traitNode.LastChildMatching(f.Key);
                        if (node == null)
                        {
                            var location = "{0} ({1})".F(actorNode.Key, traitNode.Location.Filename);
                            if (!t.Uses.Contains(location))
                            {
                                t.Uses.Add(location);
                            }
                        }
                    }
                }
            }

            yield break;
        }
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            foreach (var gcod in actorNode.ChildrenMatching("GrantConditionOnDeploy"))
            {
                var canUndeploy = gcod.LastChildMatching("CanUndeploy");
                if (canUndeploy == null)
                {
                    continue;
                }

                gcod.RemoveNode(canUndeploy);

                if (canUndeploy.NodeValue <bool>())
                {
                    continue;
                }

                var deployedCondition = gcod.LastChildMatching("DeployedCondition");
                gcod.AddNode("PauseOnCondition", deployedCondition.Value.Value);
            }

            yield break;
        }
Exemple #25
0
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            var modId = modData.Manifest.Id;

            foreach (var bo in actorNode.ChildrenMatching("BodyOrientation"))
            {
                var usesClassicFacings = false;
                var facingFudgeNode    = bo.LastChildMatching("UseClassicFacingFudge");
                if (facingFudgeNode != null)
                {
                    usesClassicFacings = facingFudgeNode.NodeValue <bool>();
                    bo.RemoveNode(facingFudgeNode);
                }

                if (usesClassicFacings)
                {
                    bo.RenameKey("ClassicFacingBodyOrientation");
                    locations.Add("{0} ({1})".F(actorNode.Key, actorNode.Location.Filename));
                }
            }

            yield break;
        }
        public override IEnumerable <string> UpdateWeaponNode(ModData modData, MiniYamlNode weaponNode)
        {
            var used = new List <string>();

            foreach (var node in weaponNode.ChildrenMatching("Warhead"))
            {
                foreach (var warhead in DamageWarheads)
                {
                    if (node.NodeValue <string>() == warhead && node.ChildrenMatching("Damage").Any(d => d.NodeValue <int>() < 0))
                    {
                        used.Add(node.Key);
                    }
                }

                if (used.Any())
                {
                    var location = "{0} ({1})".F(weaponNode.Key, node.Location.Filename);
                    locations[location] = used;
                }
            }

            yield break;
        }
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            var nukePowers = actorNode.ChildrenMatching("NukePower").ToList();

            foreach (var nuke in nukePowers)
            {
                var activation = nuke.LastChildMatching("ActivationSequence");
                if (activation == null)
                {
                    continue;
                }

                var sequence = activation.NodeValue <string>();
                nuke.RemoveNode(activation);
                actorNode.AddNode("WithNukeLaunchAnimation", "");
                if (sequence != "active")
                {
                    actorNode.LastChildMatching("WithNukeLaunchAnimation").AddNode("Sequence", sequence);
                }
            }

            yield break;
        }
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            var levelUpImageNode = new MiniYamlNode("LevelUpImage", "crate-effects");

            foreach (var ge in actorNode.ChildrenMatching("GainsExperience"))
            {
                ge.AddNode(levelUpImageNode);
            }

            foreach (var t in CrateActionTraits)
            {
                foreach (var ca in actorNode.ChildrenMatching(t))
                {
                    var effect = ca.LastChildMatching("Effect");
                    if (effect != null)
                    {
                        effect.RenameKey("Sequence");
                    }
                }
            }

            yield break;
        }
Exemple #29
0
        static IEnumerable <string> ApplyChromeTransformInner(ModData modData, MiniYamlNode current, UpdateRule.ChromeNodeTransform transform)
        {
            foreach (var manualStep in transform(modData, current))
            {
                yield return(manualStep);
            }

            var childrenNode = current.Value.Nodes.FirstOrDefault(n => n.Key == "Children");

            if (childrenNode != null)
            {
                foreach (var node in childrenNode.Value.Nodes)
                {
                    if (node.Key != null)
                    {
                        foreach (var manualStep in ApplyChromeTransformInner(modData, node, transform))
                        {
                            yield return(manualStep);
                        }
                    }
                }
            }
        }
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            var removed = 0;

            foreach (var node in actorNode.ChildrenMatching("ActorPreviewPlaceBuildingPreview"))
            {
                removed += node.RemoveNodes("OverridePalette");
                removed += node.RemoveNodes("OverridePaletteIsPlayerPalette");
                removed += node.RemoveNodes("LineBuildSegmentPalette");
            }

            foreach (var node in actorNode.ChildrenMatching("D2kActorPreviewPlaceBuildingPreview"))
            {
                removed += node.RemoveNodes("OverridePalette");
                removed += node.RemoveNodes("OverridePaletteIsPlayerPalette");
                removed += node.RemoveNodes("LineBuildSegmentPalette");
            }

            foreach (var node in actorNode.ChildrenMatching("FootprintPlaceBuildingPreview"))
            {
                removed += node.RemoveNodes("LineBuildSegmentPalette");
            }

            foreach (var node in actorNode.ChildrenMatching("SequencePlaceBuildingPreview"))
            {
                removed += node.RemoveNodes("SequencePalette");
                removed += node.RemoveNodes("SequencePaletteIsPlayerPalette");
                removed += node.RemoveNodes("LineBuildSegmentPalette");
            }

            if (removed > 0)
            {
                locations.Add($"{actorNode.Key} ({actorNode.Location.Filename})");
            }

            yield break;
        }
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            foreach (var layer in actorNode.ChildrenMatching("SmudgeLayer"))
            {
                var chance = layer.LastChildMatching("SmokePercentage");
                if (chance != null)
                {
                    chance.RenameKey("SmokeChance");
                }
                else
                {
                    layer.AddNode("SmokeChance", FieldSaver.FormatValue("25"));
                }

                var image = layer.LastChildMatching("SmokeType");
                if (image != null)
                {
                    image.RenameKey("SmokeImage");
                }
                else
                {
                    layer.AddNode("SmokeImage", FieldSaver.FormatValue("smoke_m"));
                }

                var sequences = layer.LastChildMatching("SmokeSequence");
                if (sequences != null)
                {
                    sequences.RenameKey("SmokeSequences");
                }
                else
                {
                    layer.AddNode("SmokeSequences", FieldSaver.FormatValue("idle"));
                }
            }

            yield break;
        }
Exemple #32
0
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            var addNodes = new List <MiniYamlNode>();

            foreach (var burns in actorNode.ChildrenMatching("Burns"))
            {
                var anim      = burns.LastChildMatching("Anim");
                var animValue = anim != null?anim.NodeValue <string>() : "1";

                var damage      = burns.LastChildMatching("Damage");
                var damageValue = damage != null?damage.NodeValue <int>() : 1;

                var interval      = burns.LastChildMatching("Interval");
                var intervalValue = interval != null?interval.NodeValue <int>() : 8;

                var overlay = new MiniYamlNode("WithIdleOverlay@Burns", "");
                overlay.AddNode("Image", FieldSaver.FormatValue("fire"));
                overlay.AddNode("Sequence", FieldSaver.FormatValue(animValue));
                overlay.AddNode("IsDecoration", FieldSaver.FormatValue(true));
                addNodes.Add(overlay);

                var changesHealth = new MiniYamlNode("ChangesHealth", "");
                changesHealth.AddNode("Step", FieldSaver.FormatValue(-damageValue));
                changesHealth.AddNode("StartIfBelow", FieldSaver.FormatValue(101));
                changesHealth.AddNode("Delay", FieldSaver.FormatValue(intervalValue));
                addNodes.Add(changesHealth);
            }

            actorNode.RemoveNodes("Burns");

            foreach (var addNode in addNodes)
            {
                actorNode.AddNode(addNode);
            }

            yield break;
        }
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            var aircraft       = actorNode.LastChildMatching("Aircraft");
            var attackAircraft = actorNode.ChildrenMatching("AttackAircraft");

            if (aircraft != null)
            {
                var abortOnResupply = aircraft.LastChildMatching("AbortOnResupply");
                if (abortOnResupply == null)
                {
                    yield break;
                }

                // Only add field to AttackAircraft if explicitly set to 'false'
                if (!abortOnResupply.NodeValue <bool>())
                {
                    if (attackAircraft.Any())
                    {
                        foreach (var a in attackAircraft)
                        {
                            a.AddNode(abortOnResupply);
                        }
                    }
                    else
                    {
                        var newAttackAircraft = new MiniYamlNode("AttackAircraft", "");
                        newAttackAircraft.AddNode(abortOnResupply);
                        actorNode.AddNode(newAttackAircraft);
                    }
                }

                aircraft.RemoveNode(abortOnResupply);
            }

            yield break;
        }
Exemple #34
0
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            if (complete || actorNode.LastChildMatching("IsometricSelectable") != null)
            {
                yield break;
            }

            if (!selectionHeight.TryGetValue(actorNode.Key.ToLowerInvariant(), out var height))
            {
                yield break;
            }

            // Don't redefine the default value
            if (height == 24)
            {
                yield break;
            }

            var selection = new MiniYamlNode("IsometricSelectable", "");

            selection.AddNode("Height", FieldSaver.FormatValue(height));

            actorNode.AddNode(selection);
        }
Exemple #35
0
        public override IEnumerable <string> UpdateWeaponNode(ModData modData, MiniYamlNode weaponNode)
        {
            foreach (var projectileNode in weaponNode.ChildrenMatching("Projectile"))
            {
                if (projectileNode.Value.Value == null)
                {
                    continue;
                }

                if (ProjectileFields.TryGetValue(projectileNode.Value.Value, out var fieldNames))
                {
                    foreach (var fieldName in fieldNames)
                    {
                        foreach (var fieldNode in projectileNode.ChildrenMatching(fieldName))
                        {
                            var value = fieldNode.NodeValue <int>();
                            fieldNode.Value.Value = FieldSaver.FormatValue(value != 255 ? 4 * value : 1023);
                        }
                    }
                }
            }

            yield break;
        }
Exemple #36
0
        /// <summary>Returns true if the node is of the form <match> or <match>@arbitrary</summary>
        public static bool KeyMatches(this MiniYamlNode node, string match, bool ignoreSuffix = true, bool includeRemovals = true)
        {
            if (node.Key == null)
            {
                return(false);
            }

            var prefix = includeRemovals && node.IsRemoval() ? "-" : "";

            if (node.Key == prefix + match)
            {
                return(true);
            }

            // If the previous check didn't return true and we wanted the suffix to match, return false unconditionally here
            if (!ignoreSuffix)
            {
                return(false);
            }

            var atPosition = node.Key.IndexOf('@');

            return(atPosition > 0 && node.Key.Substring(0, atPosition) == prefix + match);
        }
Exemple #37
0
        public override IEnumerable <string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
        {
            var addNodes = new List <MiniYamlNode>();

            var aircraft = modData.DefaultRules.Actors.Values.Where(a => a.HasTraitInfo <AircraftInfo>() && a.HasTraitInfo <AttackBaseInfo>()).Select(a => a.Name);
            var airUnits = new MiniYamlNode("AirUnitsTypes", FieldSaver.FormatValue(aircraft.ToList()));

            addNodes.Add(airUnits);

            var vips       = modData.DefaultRules.Actors.Values.Where(a => a.HasTraitInfo <HarvesterInfo>() || a.HasTraitInfo <BaseBuildingInfo>() || (a.HasTraitInfo <BuildingInfo>() && a.HasTraitInfo <BuildableInfo>() && !a.HasTraitInfo <LineBuildInfo>() && !a.HasTraitInfo <PlugInfo>())).Select(a => a.Name);
            var protection = new MiniYamlNode("ProtectionTypes", FieldSaver.FormatValue(vips.ToList()));

            addNodes.Add(protection);

            foreach (var squadManager in actorNode.ChildrenMatching("SquadManagerBotModule"))
            {
                foreach (var addNode in addNodes)
                {
                    squadManager.AddNode(addNode);
                }
            }

            yield break;
        }
Exemple #38
0
        internal static void UpgradeWeaponRules(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            foreach (var node in nodes)
            {
                if (engineVersion < 20160124)
                {
                    node.Value.Nodes.RemoveAll(x => x.Key == "Charges");
                }

                // Enhance CreateEffectWarhead
                if (engineVersion < 20160131)
                {
                    if (node.Key.StartsWith("Warhead") && node.Value.Value == "CreateEffect")
                    {
                        // Add support for multiple explosions to CreateEffectWarhead
                        var explosionNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "Explosion");
                        if (explosionNode != null)
                            explosionNode.Key = "Explosions";

                        // Add support for multiple impact sounds to CreateEffectWarhead
                        var impactSoundNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "ImpactSound");
                        if (impactSoundNode != null)
                            impactSoundNode.Key = "ImpactSounds";
                    }
                }

                // Rename some speed-related Missile properties
                if (engineVersion < 20160205)
                {
                    var mod = Game.ModData.Manifest.Mod.Id;
                    if (mod == "ts")
                    {
                        if (node.Key == "Projectile" && node.Value.Value == "Missile")
                        {
                            node.Value.Nodes.Add(new MiniYamlNode("MinimumLaunchSpeed", "75"));
                            node.Value.Nodes.Add(new MiniYamlNode("Speed", "384"));
                        }
                    }
                    else
                    {
                        if (node.Key == "MaximumLaunchSpeed" && parent != null && parent.Value.Value == "Missile")
                            node.Key = "Speed";
                    }
                }

                UpgradeWeaponRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
            }
        }
Exemple #39
0
 internal static void UpgradeCursors(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
 {
     foreach (var node in nodes)
     {
         // Add rules here
         UpgradeCursors(engineVersion, ref node.Value.Nodes, node, depth + 1);
     }
 }
Exemple #40
0
        internal static void UpgradeCursors(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            foreach (var node in nodes)
            {
                if (engineVersion < 20141113 && depth == 3)
                {
                    if (node.Key == "start")
                        node.Key = "Start";
                    else if (node.Key == "length")
                        node.Key = "Length";
                    else if (node.Key == "end")
                        node.Key = "End";
                    else if (node.Key == "x")
                        node.Key = "X";
                    else if (node.Key == "y")
                        node.Key = "Y";
                }

                UpgradeCursors(engineVersion, ref node.Value.Nodes, node, depth + 1);
            }
        }
Exemple #41
0
 static void ConvertUpgradesToCondition(MiniYamlNode parent, MiniYamlNode node, string upgradesKey, string conditionKey)
 {
     var upgradesNode = node.Value.Nodes.FirstOrDefault(n => n.Key == upgradesKey);
     if (upgradesNode != null)
     {
         var conditions = FieldLoader.GetValue<string[]>("", upgradesNode.Value.Value);
         if (conditions.Length > 1)
             Console.WriteLine("Unable to automatically migrate {0}:{1} {2} to {3}. This must be corrected manually",
                 parent.Key, node.Key, upgradesKey, conditionKey);
         else
             upgradesNode.Key = conditionKey;
     }
 }
Exemple #42
0
        internal static void UpgradeActorRules(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            foreach (var node in nodes)
            {
                if (engineVersion < 20151225 && depth == 2)
                {
                    if (node.Key == "Color")
                    {
                        if (parent.Key.StartsWith("FixedColorPalette"))
                            TryUpdateHSLColor(ref node.Value.Value);
                        else
                            TryUpdateColor(ref node.Value.Value);
                    }
                    else if (node.Key == "RadarPingColor" || node.Key == "SelectionBoxColor" || node.Key == "BarColor")
                        TryUpdateColor(ref node.Value.Value);
                    else if (node.Key == "Fog" || node.Key == "Shroud" || node.Key == "ParticleColors")
                        TryUpdateColors(ref node.Value.Value);
                }

                // DeathType on Explodes was renamed to DeathTypes
                if (engineVersion < 20151225)
                {
                    if (node.Key == "Explodes")
                    {
                        var dt = node.Value.Nodes.FirstOrDefault(n => n.Key == "DeathType");
                        if (dt != null)
                            dt.Key = "DeathTypes";
                    }
                }

                // Upgrades on DeployToUpgrade were renamed to DeployedUpgrades
                if (engineVersion < 20151122)
                {
                    if (node.Key == "DeployToUpgrade")
                    {
                        var u = node.Value.Nodes.FirstOrDefault(n => n.Key == "Upgrades");
                        if (u != null)
                            u.Key = "DeployedUpgrades";
                    }
                }

                if (engineVersion < 20151225)
                {
                    // Rename WithTurret to WithSpriteTurret
                    if (depth == 1 && node.Key.StartsWith("WithTurret"))
                    {
                        var parts = node.Key.Split('@');
                        node.Key = "WithSpriteTurret";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];
                    }

                    if (depth == 1 && node.Key.StartsWith("-WithTurret"))
                    {
                        var parts = node.Key.Split('@');
                        node.Key = "-WithSpriteTurret";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];
                    }

                    // Rename WithBarrel to WithSpriteBarrel
                    if (depth == 1 && node.Key.StartsWith("WithBarrel"))
                    {
                        var parts = node.Key.Split('@');
                        node.Key = "WithSpriteBarrel";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];
                    }

                    if (depth == 1 && node.Key.StartsWith("-WithBarrel"))
                    {
                        var parts = node.Key.Split('@');
                        node.Key = "-WithSpriteBarrel";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];
                    }

                    // Rename WithReloadingTurret to WithReloadingSpriteTurret
                    if (depth == 1 && node.Key.StartsWith("WithReloadingTurret"))
                    {
                        var parts = node.Key.Split('@');
                        node.Key = "WithReloadingSpriteTurret";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];
                    }

                    if (depth == 1 && node.Key.StartsWith("-WithReloadingTurret"))
                    {
                        var parts = node.Key.Split('@');
                        node.Key = "-WithReloadingSpriteTurret";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];
                    }
                }

                // Mobile actors immobilized by Carryable, Cargo, DeployToUpgrade, and/or others using upgrade(s)
                if (engineVersion < 20151225 && depth == 0)
                {
                    var notMobile = "notmobile";

                    var mobileNode = node.Value.Nodes.Find(n => n.Key == "Mobile");
                    var carryableNode = node.Value.Nodes.Find(n => n.Key == "Carryable");
                    var cargoNode = node.Value.Nodes.Find(n => n.Key == "Cargo");
                    var deployToUpgradeNode = node.Value.Nodes.Find(n => n.Key == "DeployToUpgrade");
                    var disableUpgradeNode = node.Value.Nodes.Find(n => n.Key == "DisableUpgrade");
                    var disableMovementOnUpgradeNode = node.Value.Nodes.Find(n => n.Key == "DisableMovementOnUpgrade");

                    Action<MiniYamlNode, string> addNotMobileToTraitUpgrades = (trait, upgradesKey) =>
                    {
                        if (trait != null)
                        {
                            var upgrades = trait.Value.Nodes.Find(u => u.Key == upgradesKey);
                            if (upgrades == null)
                                trait.Value.Nodes.Add(new MiniYamlNode(upgradesKey, notMobile));
                            else if (string.IsNullOrEmpty(upgrades.Value.Value))
                                upgrades.Value.Value = notMobile;
                            else if (!upgrades.Value.Value.Contains(notMobile))
                                upgrades.Value.Value += ", " + notMobile;
                        }
                    };

                    if (mobileNode != null)
                    {
                        var mobileUpgrades = mobileNode.Value.Nodes.Find(n => n.Key == "UpgradeTypes");
                        var mobileUpgradeMaxEnabledLevel = mobileNode.Value.Nodes.Find(n => n.Key == "UpgradeMaxEnabledLevel");
                        var comma = new char[] { ',' };

                        Func<bool> addUpgradeMaxEnabledLevelNode = () =>
                        {
                            if (mobileUpgradeMaxEnabledLevel == null)
                            {
                                mobileUpgradeMaxEnabledLevel = new MiniYamlNode("UpgradeMaxEnabledLevel", "0");
                                mobileNode.Value.Nodes.Add(mobileUpgradeMaxEnabledLevel);
                                return true;
                            }
                            else
                                return mobileUpgradeMaxEnabledLevel.Value.Value == "0";
                        };

                        // If exactly one upgrade type is in UpgradeTypes and UpgradeMaxEnabledLevel is/can be 0 , then use it as notmobile
                        if (mobileUpgrades != null && !string.IsNullOrEmpty(mobileUpgrades.Value.Value)
                            && !mobileUpgrades.Value.Value.Contains(",") && addUpgradeMaxEnabledLevelNode())
                            notMobile = mobileUpgrades.Value.Value;

                        if (mobileUpgradeMaxEnabledLevel != null && mobileUpgradeMaxEnabledLevel.Value.Value != "0")
                            Console.WriteLine("\t\t" + node.Key + " actor rules may require manual upgrading for immobilization upgrade logic.");
                        else
                        {
                            Action<string> addImmobilizeUpgradeType = upgradeType =>
                            {
                                if (mobileUpgrades == null)
                                {
                                    mobileUpgrades = new MiniYamlNode("UpgradeTypes", upgradeType);
                                    mobileNode.Value.Nodes.Add(mobileUpgrades);
                                }
                                else if (string.IsNullOrEmpty(mobileUpgrades.Value.Value))
                                    mobileUpgrades.Value.Value = upgradeType;
                                else if (!mobileUpgrades.Value.Value.Split(comma).Contains(upgradeType))
                                    mobileUpgrades.Value.Value += ", " + upgradeType;
                            };

                            Predicate<string> addImmobilizeUpgradeTypes = upgradeTypes =>
                            {
                                if (string.IsNullOrEmpty(upgradeTypes))
                                    return false;

                                foreach (var upgradeType in upgradeTypes.Split(comma))
                                    addImmobilizeUpgradeType(upgradeType);
                                return true;
                            };

                            Predicate<MiniYamlNode> addUpgradeTypeFromTrait = trait =>
                            {
                                var upgradeTypesNode = trait.Value.Nodes.Find(n => n.Key == "UpgradeTypes");
                                if (upgradeTypesNode == null)
                                    return false;

                                addUpgradeMaxEnabledLevelNode();
                                return addImmobilizeUpgradeTypes(upgradeTypesNode.Value.Value);
                            };

                            var noticeWritten = false;

                            Action writeNotice = () =>
                            {
                                if (noticeWritten)
                                    return;
                                Console.WriteLine("\t\t" + node.Key + " actor rules may require manual upgrading for immobilization upgrade logic.");
                                noticeWritten = true;
                            };

                            if (disableUpgradeNode != null && !addUpgradeTypeFromTrait(disableUpgradeNode))
                            {
                                writeNotice();
                                Console.WriteLine("\t\t\tOne or more upgrades may need to be copied from the DisableUpgrade trait to the Mobile trait.");
                            }

                            if (disableMovementOnUpgradeNode != null)
                            {
                                if (addUpgradeTypeFromTrait(disableMovementOnUpgradeNode))
                                    parent.Value.Nodes.Remove(disableMovementOnUpgradeNode);
                                else
                                {
                                    writeNotice();
                                    Console.WriteLine("\t\t\tOne or more upgrades may need to be moved from the DisableMovementOnUpgrade trait to the Mobile trait.");
                                    Console.WriteLine("\t\t\t\tRemember to remove the DisableMovementOnUpgrade trait.");
                                }
                            }

                            if (carryableNode != null || cargoNode != null || deployToUpgradeNode != null)
                            {
                                addUpgradeMaxEnabledLevelNode();
                                addImmobilizeUpgradeTypes(notMobile);

                                addNotMobileToTraitUpgrades(carryableNode, "CarryableUpgrades");
                                addNotMobileToTraitUpgrades(cargoNode, "LoadingUpgrades");
                                addNotMobileToTraitUpgrades(deployToUpgradeNode, "DeployedUpgrades");
                            }
                        }
                    }
                    else if (!node.Value.Nodes.Exists(n => n.Key == "Husk" || n.Key == "Building" || n.Key == "Aircraft" || n.Key == "Immobile"))
                    {
                        if (carryableNode != null || cargoNode != null || deployToUpgradeNode != null)
                        {
                            Console.WriteLine("\t\tIf " + node.Key
                                + " has a Mobile trait then adding the following with <upgrade> substituted by an immobilization upgrade for "
                                + node.Key + " may be neeeded:");

                            if (carryableNode != null)
                            {
                                Console.WriteLine("\t\t\tCarryable:");
                                Console.WriteLine("\t\t\t\tCarryableUpgrades: <upgrade>");
                            }

                            if (cargoNode != null)
                            {
                                Console.WriteLine("\t\t\tCargo:");
                                Console.WriteLine("\t\t\t\tLoadingUpgrades: <upgrade>");
                            }

                            if (deployToUpgradeNode != null)
                            {
                                Console.WriteLine("\t\t\tDeployToUpgrade:");
                                Console.WriteLine("\t\t\t\tDeployedUpgrades: <upgrade>");
                            }
                        }

                        var disableUpgradeUpgradeTypesNode = disableUpgradeNode != null
                            ? disableUpgradeNode.Value.Nodes.Find(n => n.Key == "UpgradeTypes")
                            : null;
                        var disableMovementOnUpgradeUpgradeTypesNode = disableMovementOnUpgradeNode != null
                            ? disableMovementOnUpgradeNode.Value.Nodes.Find(n => n.Key == "UpgradeTypes")
                            : null;

                        if (disableUpgradeUpgradeTypesNode != null || disableMovementOnUpgradeUpgradeTypesNode != null)
                            Console.WriteLine("\t\t" + node.Key + " actor rules may require manual upgrading for immobilization upgrade logic.");

                        if (disableUpgradeUpgradeTypesNode != null)
                            Console.WriteLine("\t\t\tDisableUpgrade UpgradeTypes: " + disableUpgradeUpgradeTypesNode.Value.Value);

                        if (disableMovementOnUpgradeUpgradeTypesNode != null)
                            Console.WriteLine("\t\t\tDisableMovementOnUpgrade UpgradeTypes: " + disableMovementOnUpgradeUpgradeTypesNode.Value.Value);

                        if (disableMovementOnUpgradeNode != null)
                            node.Value.Nodes.Remove(disableMovementOnUpgradeNode);
                    }
                }

                // 'CloseEnough' on 'RepairableNear' uses WDist now
                if (engineVersion < 20151225)
                {
                    if (node.Key == "RepairableNear")
                    {
                        var ce = node.Value.Nodes.FirstOrDefault(n => n.Key == "CloseEnough");
                        if (ce != null && !ce.Value.Value.Contains("c"))
                            ce.Value.Value = ce.Value.Value + "c0";
                    }
                }

                // Added width support for line particles
                if (engineVersion < 20151225 && node.Key == "WeatherOverlay")
                {
                    var useSquares = node.Value.Nodes.FirstOrDefault(n => n.Key == "UseSquares");
                    if (useSquares != null && !FieldLoader.GetValue<bool>("UseSquares", useSquares.Value.Value))
                        node.Value.Nodes.Add(new MiniYamlNode("ParticleSize", "1, 1"));
                }

                // Overhauled the actor decorations traits
                if (engineVersion < 20151226)
                {
                    if (depth == 1 && (node.Key.StartsWith("WithDecoration") || node.Key.StartsWith("WithRankDecoration")))
                    {
                        node.Value.Nodes.RemoveAll(n => n.Key == "Scale");
                        node.Value.Nodes.RemoveAll(n => n.Key == "Offset");
                        var sd = node.Value.Nodes.FirstOrDefault(n => n.Key == "SelectionDecoration");
                        if (sd != null)
                            sd.Key = "RequiresSelection";

                        var reference = node.Value.Nodes.FirstOrDefault(n => n.Key == "ReferencePoint");
                        if (reference != null)
                        {
                            var values = FieldLoader.GetValue<string[]>("ReferencePoint", reference.Value.Value);
                            values = values.Where(v => v != "HCenter" && v != "VCenter").ToArray();
                            if (values.Length == 0)
                                values = new[] { "Center" };

                            reference.Value.Value = FieldSaver.FormatValue(values);
                        }

                        var stance = Stance.Ally;
                        var showToAllies = node.Value.Nodes.FirstOrDefault(n => n.Key == "ShowToAllies");
                        if (showToAllies != null && !FieldLoader.GetValue<bool>("ShowToAllies", showToAllies.Value.Value))
                            stance ^= Stance.Ally;
                        var showToEnemies = node.Value.Nodes.FirstOrDefault(n => n.Key == "ShowToEnemies");
                        if (showToEnemies != null && FieldLoader.GetValue<bool>("ShowToEnemies", showToEnemies.Value.Value))
                            stance |= Stance.Enemy;

                        if (stance != Stance.Ally)
                            node.Value.Nodes.Add(new MiniYamlNode("Stance", FieldSaver.FormatValue(stance)));

                        node.Value.Nodes.RemoveAll(n => n.Key == "ShowToAllies");
                        node.Value.Nodes.RemoveAll(n => n.Key == "ShowToEnemies");
                    }

                    if (depth == 1 && node.Key == "Fake")
                    {
                        node.Key = "WithDecoration@fake";
                        node.Value.Nodes.Add(new MiniYamlNode("RequiresSelection", "true"));
                        node.Value.Nodes.Add(new MiniYamlNode("Image", "pips"));
                        node.Value.Nodes.Add(new MiniYamlNode("Sequence", "tag-fake"));
                        node.Value.Nodes.Add(new MiniYamlNode("ReferencePoint", "Top"));
                        node.Value.Nodes.Add(new MiniYamlNode("ZOffset", "256"));
                    }

                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("PrimaryBuilding")))
                    {
                        var decNodes = new List<MiniYamlNode>();
                        decNodes.Add(new MiniYamlNode("RequiresSelection", "true"));
                        decNodes.Add(new MiniYamlNode("Image", "pips"));
                        decNodes.Add(new MiniYamlNode("Sequence", "tag-primary"));
                        decNodes.Add(new MiniYamlNode("ReferencePoint", "Top"));
                        decNodes.Add(new MiniYamlNode("ZOffset", "256"));
                        decNodes.Add(new MiniYamlNode("UpgradeTypes", "primary"));
                        decNodes.Add(new MiniYamlNode("UpgradeMinEnabledLevel", "1"));
                        node.Value.Nodes.Add(new MiniYamlNode("WithDecoration@primary", new MiniYaml("", decNodes)));
                    }
                }

                // Refactored the low resources notification to a separate trait
                if (engineVersion < 20151227 && node.Key == "Player")
                {
                    var resourcesNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "PlayerResources");

                    if (resourcesNode != null)
                    {
                        var intervalNode = resourcesNode.Value.Nodes.FirstOrDefault(x => x.Key == "AdviceInterval");
                        var storageNode = new MiniYamlNode("ResourceStorageWarning", "");

                        if (intervalNode != null)
                        {
                            // The time value is now in seconds, not ticks. We
                            // divide by 25 ticks per second at Normal.
                            int oldInterval;
                            if (int.TryParse(intervalNode.Value.Value, out oldInterval))
                                storageNode.Value.Nodes.Add(new MiniYamlNode("AdviceInterval", (oldInterval / 25).ToString()));
                            resourcesNode.Value.Nodes.Remove(intervalNode);
                        }

                        node.Value.Nodes.Add(storageNode);
                    }
                }

                // Refactored Health.Radius to HitShapes
                if (engineVersion < 20151227)
                {
                    if (node.Key.StartsWith("Health"))
                    {
                        var radius = node.Value.Nodes.FirstOrDefault(x => x.Key == "Radius");
                        if (radius != null)
                        {
                            var radiusValue = FieldLoader.GetValue<string>("Radius", radius.Value.Value);
                            node.Value.Nodes.Add(new MiniYamlNode("Shape", "Circle"));

                            var shape = node.Value.Nodes.First(x => x.Key == "Shape");
                            shape.Value.Nodes.Add(new MiniYamlNode("Radius", radiusValue));

                            node.Value.Nodes.Remove(radius);
                        }
                    }
                }

                // Remove obsolete TransformOnPassenger trait.
                if (engineVersion < 20160102)
                {
                    var removed = node.Value.Nodes.RemoveAll(x => x.Key.Contains("TransformOnPassenger"));
                    if (removed > 0)
                    {
                        Console.WriteLine("TransformOnPassenger has been removed.");
                        Console.WriteLine("Use the upgrades system to apply modifiers to the transport actor instead.");
                    }
                }

                if (engineVersion < 20160103)
                {
                    // Overhauled WithActiveAnimation -> WithIdleAnimation
                    if (node.Key == "WithActiveAnimation")
                    {
                        node.Key = "WithIdleAnimation";
                        foreach (var n in node.Value.Nodes)
                            if (n.Key == "Sequence")
                                n.Key = "Sequences";
                    }
                }

                if (engineVersion < 20160107 && depth == 1 && node.Key.StartsWith("Cloak"))
                {
                    var defaultCloakType = Traits.UncloakType.Attack
                        | Traits.UncloakType.Unload | Traits.UncloakType.Infiltrate | Traits.UncloakType.Demolish;

                    // Merge Uncloak types
                    var t = defaultCloakType;
                    for (var i = node.Value.Nodes.Count - 1; i >= 0; i--)
                    {
                        var n = node.Value.Nodes[i];
                        var v = string.Compare(n.Value.Value, "true", true) == 0;
                        Traits.UncloakType flag;
                        if (n.Key == "UncloakOnAttack")
                            flag = Traits.UncloakType.Attack;
                        else if (n.Key == "UncloakOnMove")
                            flag = Traits.UncloakType.Move;
                        else if (n.Key == "UncloakOnUnload")
                            flag = Traits.UncloakType.Unload;
                        else if (n.Key == "UncloakOnInfiltrate")
                            flag = Traits.UncloakType.Infiltrate;
                        else if (n.Key == "UncloakOnDemolish")
                            flag = Traits.UncloakType.Demolish;
                        else
                            continue;
                        t = v ? t | flag : t & ~flag;
                        node.Value.Nodes.Remove(n);
                    }

                    if (t != defaultCloakType)
                    {
                        Console.WriteLine("\t\tCloak type: " + t.ToString());
                        var ts = new List<string>();
                        if (t.HasFlag(Traits.UncloakType.Attack))
                            ts.Add("Attack");
                        if (t.HasFlag(Traits.UncloakType.Unload))
                            ts.Add("Unload");
                        if (t.HasFlag(Traits.UncloakType.Infiltrate))
                            ts.Add("Infiltrate");
                        if (t.HasFlag(Traits.UncloakType.Demolish))
                            ts.Add("Demolish");
                        if (t.HasFlag(Traits.UncloakType.Move))
                            ts.Add("Move");
                        node.Value.Nodes.Add(new MiniYamlNode("UncloakOn", ts.JoinWith(", ")));
                    }
                }

                // Rename WithDockingOverlay to WithDockedOverlay
                if (engineVersion < 20160116)
                {
                    if (node.Key.StartsWith("WithDockingOverlay"))
                        node.Key = "WithDockedOverlay" + node.Key.Substring(18);
                }

                if (engineVersion < 20160116)
                {
                    if (node.Key == "DemoTruck")
                        node.Key = "AttackSuicides";
                }

                // Replaced GpsRemoveFrozenActor with FrozenUnderFogUpdatedByGps
                if (engineVersion < 20160117)
                {
                    if (node.Key == "GpsRemoveFrozenActor")
                    {
                        node.Key = "FrozenUnderFogUpdatedByGps";
                        node.Value.Nodes.Clear();
                    }
                }

                // Removed arbitrary defaults from InfiltrateForCash
                if (engineVersion < 20160118)
                {
                    if (node.Key == "InfiltrateForCash")
                    {
                        if (!node.Value.Nodes.Any(n => n.Key == "Percentage"))
                            node.Value.Nodes.Add(new MiniYamlNode("Percentage", "50"));

                        if (!node.Value.Nodes.Any(n => n.Key == "Minimum"))
                            node.Value.Nodes.Add(new MiniYamlNode("Minimum", "500"));

                        var sound = node.Value.Nodes.FirstOrDefault(n => n.Key == "SoundToVictim");
                        if (sound != null)
                        {
                            node.Value.Nodes.Remove(sound);
                            Console.WriteLine("The 'SoundToVictim' property of the 'InfiltrateForCash' trait has been");
                            Console.WriteLine("replaced with a 'Notification' property. Please add the sound file");
                            Console.WriteLine("'{0}' to your mod's audio notification yaml and".F(sound.Value.Value));
                            Console.WriteLine("update your mod's rules accordingly.");
                            Console.WriteLine();
                        }
                    }
                }

                if (engineVersion < 20160301)
                {
                    // Renamed ROT -> TurnSpeed
                    if (node.Key == "ROT")
                        node.Key = "TurnSpeed";
                }

                if (engineVersion < 20160320)
                {
                    // Renamed Parachutable.CorpseSequenceCollection to Image
                    if (node.Key == "CorpseSequenceCollection")
                        node.Key = "Image";

                    // Renamed WithBuildingExplosion.SequenceCollection to Image
                    if (node.Key == "SequenceCollection")
                        node.Key = "Image";
                }

                if (engineVersion < 20160321)
                {
                    var parentKey = parent != null ? parent.Key.Split('@').First() : null;
                    if (node.Key == "Ticks" && parentKey == "DrawLineToTarget")
                        node.Key = "Duration";
                    if (node.Key == "ReloadTicks")
                        node.Key = "ReloadDelay";
                    if (node.Key == "SelfReloadTicks")
                        node.Key = "SelfReloadDelay";
                    if (node.Key == "LoadTicksPerBale")
                        node.Key = "BaleLoadDelay";
                    if (node.Key == "UnloadTicksPerBale")
                        node.Key = "BaleUnloadDelay";
                    if (node.Key == "TicksToHold")
                        node.Key = "HoldDuration";
                    if (node.Key == "Ticks" && parentKey == "SelfHealing")
                        node.Key = "Delay";
                    if (node.Key == "TicksToWaitBeforeReducingMoveRadius")
                        node.Key = "ReduceMoveRadiusDelay";
                    if (node.Key == "MinIdleWaitTicks")
                        node.Key = "MinIdleDelay";
                    if (node.Key == "MaxIdleWaitTicks")
                        node.Key = "MaxIdleWaitDelay";
                    if (node.Key == "ReloadTime")
                        node.Key = "ReloadDelay";
                }

                // Got rid of most remaining usages of float in a bid to further reduce desync risk
                if (engineVersion < 20160328)
                {
                    // Migrated ProductionQueue BuildSpeed to use int percentage instead of float
                    if (node.Key.StartsWith("ProductionQueue") || node.Key.StartsWith("ClassicProductionQueue"))
                    {
                        var buildSpeedNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "BuildSpeed");
                        if (buildSpeedNode != null)
                        {
                            // The BuildSpeed value is now an int percentage, so multiply the float with 100.
                            var oldValue = FieldLoader.GetValue<float>("BuildSpeed", buildSpeedNode.Value.Value);
                            var newValue = (int)(oldValue * 100);
                            buildSpeedNode.Value.Value = newValue.ToString();
                        }
                    }

                    // Migrated StrategicVictoryConditions RatioRequired to use int percentage instead of float
                    if (node.Key.StartsWith("StrategicVictoryConditions"))
                    {
                        var ratioNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "RatioRequired");
                        if (ratioNode != null)
                        {
                            // The RatioRequired value is now an int percentage, so multiply the float with 100.
                            var oldValue = FieldLoader.GetValue<float>("RatioRequired", ratioNode.Value.Value);
                            var newValue = (int)(oldValue * 100);
                            ratioNode.Value.Value = newValue.ToString();
                        }
                    }

                    // Migrated Minelayer.MinefieldDepth to use WDist instead of float
                    if (node.Key.StartsWith("Minelayer"))
                    {
                        var depthNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "MinefieldDepth");
                        if (depthNode != null)
                        {
                            // The MinefieldDepth value is now a WDist, so multiply the float value with 1024.
                            var oldValue = FieldLoader.GetValue<float>("MinefieldDepth", depthNode.Value.Value);
                            var newValue = (int)(oldValue * 1024);
                            depthNode.Value.Value = newValue.ToString();
                        }
                    }

                    // Migrated SelfHealing to use int percentage instead of float
                    if (node.Key == "SelfHealing")
                    {
                        var healIfBelowNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "HealIfBelow");
                        if (healIfBelowNode != null)
                        {
                            // The HealIfBelow value is now an int percentage, so multiply the float with 100.
                            var oldValue = FieldLoader.GetValue<float>("HealIfBelow", healIfBelowNode.Value.Value);
                            var newValue = (int)(oldValue * 100);
                            healIfBelowNode.Value.Value = newValue.ToString();
                        }
                    }

                    // Migrated EmitInfantryOnSell to use int percentage instead of float
                    if (node.Key == "EmitInfantryOnSell")
                    {
                        var valueNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "ValuePercent");
                        var minHPNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "MinHpPercent");

                        if (valueNode != null)
                        {
                            // The ValuePercent value is now an int percentage, but was previously geared towards
                            // percentage rather than float and divided by 100 internally so division by 100 is NOT needed.
                            var oldValue = FieldLoader.GetValue<float>("ValuePercent", valueNode.Value.Value);
                            var newValue = (int)oldValue;
                            valueNode.Value.Value = newValue.ToString();
                        }

                        if (minHPNode != null)
                        {
                            // The MinHpPercent value is now an int percentage, but was previously geared towards
                            // percentage rather than float and divided by 100 internally so division by 100 is NOT needed.
                            var oldValue = FieldLoader.GetValue<float>("MinHpPercent", minHPNode.Value.Value);
                            var newValue = (int)oldValue;
                            minHPNode.Value.Value = newValue.ToString();
                        }
                    }

                    // Migrated Captures and Capturable to use int percentage instead of float
                    if (node.Key == "Captures")
                    {
                        var sabotageHPRemNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "SabotageHPRemoval");
                        if (sabotageHPRemNode != null)
                        {
                            // The SabotageHPRemoval value is now an int percentage, so multiply the float with 100.
                            var oldValue = FieldLoader.GetValue<float>("SabotageHPRemoval", sabotageHPRemNode.Value.Value);
                            var newValue = (int)(oldValue * 100);
                            sabotageHPRemNode.Value.Value = newValue.ToString();
                        }
                    }

                    if (node.Key == "Capturable")
                    {
                        var captThreshNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "CaptureThreshold");
                        if (captThreshNode != null)
                        {
                            // The CaptureThreshold value is now an int percentage, so multiply the float with 100.
                            var oldValue = FieldLoader.GetValue<float>("CaptureThreshold", captThreshNode.Value.Value);
                            var newValue = (int)(oldValue * 100);
                            captThreshNode.Value.Value = newValue.ToString();
                        }
                    }
                }

                if (engineVersion < 20160402)
                {
                    // Fix misleading property naming.
                    if (node.Key == "EffectSequence" && parent.Key == "SpawnActorPower")
                        node.Key = "EffectImage";
                }

                if (engineVersion < 20160408)
                {
                    var traitNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "InsufficientFundsWarning");
                    if (traitNode != null)
                    {
                        var prNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "PlayerResources");
                        if (prNode != null)
                            prNode.Value.Nodes.Add(new MiniYamlNode("InsufficientFundsNotification", new MiniYaml("InsufficientFunds")));

                        node.Value.Nodes.Remove(traitNode);
                    }
                }

                if (engineVersion < 20160418)
                {
                    // Removed FrozenUnderFog.StartsRevealed
                    if (node.Key == "FrozenUnderFog")
                        node.Value.Nodes.RemoveAll(x => x.Key == "StartsRevealed");
                }

                UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
            }
        }
Exemple #43
0
		static void UpgradeTileset(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
		{
			var parentKey = parent != null ? parent.Key.Split('@').First() : null;
			var addNodes = new List<MiniYamlNode>();

			foreach (var node in nodes)
			{
				if (engineVersion < 20140104)
				{
					if (depth == 2 && parentKey == "TerrainType" && node.Key.Split('@').First() == "Type")
						addNodes.Add(new MiniYamlNode("TargetTypes", node.Value.Value == "Water" ? "Water" : "Ground"));
				}
				UpgradeTileset(engineVersion, ref node.Value.Nodes, node, depth + 1);
			}

			nodes.AddRange(addNodes);
		}
Exemple #44
0
        internal static void UpgradeWeaponRules(ModData modData, int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            foreach (var node in nodes)
            {
                // Refactor Missile RangeLimit from ticks to WDist
                if (engineVersion < 20160509)
                {
                    var weapRange = node.Value.Nodes.FirstOrDefault(n => n.Key == "Range");
                    var projectile = node.Value.Nodes.FirstOrDefault(n => n.Key == "Projectile");

                    if (projectile != null && weapRange != null && projectile.Value.Value == "Missile")
                    {
                        var oldWDist = FieldLoader.GetValue<WDist>("Range", weapRange.Value.Value);
                        var rangeLimitNode = projectile.Value.Nodes.FirstOrDefault(x => x.Key == "RangeLimit");

                        // RangeLimit is now a WDist value, so for the conversion, we take weapon range and add 20% on top.
                        // Overly complicated calculations using Range, Speed and the old RangeLimit value would be rather pointless,
                        // because currently most mods have somewhat arbitrary, usually too high and in a few cases too low RangeLimits anyway.
                        var newValue = oldWDist.Length * 120 / 100;
                        var newCells = newValue / 1024;
                        var newCellPart = newValue % 1024;

                        if (rangeLimitNode != null)
                            rangeLimitNode.Value.Value = newCells.ToString() + "c" + newCellPart.ToString();
                        else
                        {
                            // Since the old default was 'unlimited', we're using weapon range * 1.2 for missiles not defining a custom RangeLimit as well
                            projectile.Value.Nodes.Add(new MiniYamlNode("RangeLimit", newCells.ToString() + "c" + newCellPart.ToString()));
                        }
                    }
                }

                // Streamline some projectile property names and functionality
                if (engineVersion < 20160601)
                {
                    if (node.Key == "Sequence")
                        node.Key = "Sequences";

                    if (node.Key == "TrailSequence")
                        node.Key = "TrailSequences";

                    if (node.Key == "Trail")
                        node.Key = "TrailImage";

                    if (node.Key == "Velocity")
                        node.Key = "Speed";
                }

                UpgradeWeaponRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
            }
        }
Exemple #45
0
 internal static void UpgradeTileset(ModData modData, int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
 {
     foreach (var node in nodes)
     {
         // Add rules here
         UpgradeTileset(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
     }
 }
Exemple #46
0
        internal static void UpgradeSequences(ModData modData, int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            foreach (var node in nodes)
            {
                if (engineVersion < 20160730 && modData.Manifest.Id == "d2k" && depth == 2)
                {
                    if (node.Key == "Start")
                        node.Value.Value = RemapD2k106Sequence(FieldLoader.GetValue<int>("", node.Value.Value)).ToString();
                    if (node.Key == "Frames")
                        node.Value.Value = FieldLoader.GetValue<int[]>("", node.Value.Value)
                            .Select(RemapD2k106Sequence).JoinWith(", ");
                }

                UpgradeSequences(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
            }
        }
Exemple #47
0
        internal static void UpgradeMapFormat(ModData modData, IReadWritePackage package)
        {
            if (package == null)
                return;

            var yamlStream = package.GetStream("map.yaml");
            if (yamlStream == null)
                return;

            var yaml = new MiniYaml(null, MiniYaml.FromStream(yamlStream, package.Name));
            var nd = yaml.ToDictionary();
            var mapFormat = FieldLoader.GetValue<int>("MapFormat", nd["MapFormat"].Value);
            if (mapFormat < 6)
                throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(mapFormat, package.Name));

            // Format 6 -> 7 combined the Selectable and UseAsShellmap flags into the Class enum
            if (mapFormat < 7)
            {
                MiniYaml useAsShellmap;
                if (nd.TryGetValue("UseAsShellmap", out useAsShellmap) && bool.Parse(useAsShellmap.Value))
                    yaml.Nodes.Add(new MiniYamlNode("Visibility", new MiniYaml("Shellmap")));
                else if (nd["Type"].Value == "Mission" || nd["Type"].Value == "Campaign")
                    yaml.Nodes.Add(new MiniYamlNode("Visibility", new MiniYaml("MissionSelector")));
            }

            // Format 7 -> 8 replaced normalized HSL triples with rgb(a) hex colors
            if (mapFormat < 8)
            {
                var players = yaml.Nodes.FirstOrDefault(n => n.Key == "Players");
                if (players != null)
                {
                    bool noteHexColors = false;
                    bool noteColorRamp = false;
                    foreach (var player in players.Value.Nodes)
                    {
                        var colorRampNode = player.Value.Nodes.FirstOrDefault(n => n.Key == "ColorRamp");
                        if (colorRampNode != null)
                        {
                            Color dummy;
                            var parts = colorRampNode.Value.Value.Split(',');
                            if (parts.Length == 3 || parts.Length == 4)
                            {
                                // Try to convert old normalized HSL value to a rgb hex color
                                try
                                {
                                    HSLColor color = new HSLColor(
                                        (byte)Exts.ParseIntegerInvariant(parts[0].Trim()).Clamp(0, 255),
                                        (byte)Exts.ParseIntegerInvariant(parts[1].Trim()).Clamp(0, 255),
                                        (byte)Exts.ParseIntegerInvariant(parts[2].Trim()).Clamp(0, 255));
                                    colorRampNode.Value.Value = FieldSaver.FormatValue(color);
                                    noteHexColors = true;
                                }
                                catch (Exception)
                                {
                                    throw new InvalidDataException("Invalid ColorRamp value.\n File: " + package.Name);
                                }
                            }
                            else if (parts.Length != 1 || !HSLColor.TryParseRGB(parts[0], out dummy))
                                throw new InvalidDataException("Invalid ColorRamp value.\n File: " + package.Name);

                            colorRampNode.Key = "Color";
                            noteColorRamp = true;
                        }
                    }

                    if (noteHexColors)
                        Console.WriteLine("ColorRamp is now called Color and uses rgb(a) hex value - rrggbb[aa].");
                    else if (noteColorRamp)
                        Console.WriteLine("ColorRamp is now called Color.");
                }
            }

            // Format 8 -> 9 moved map options and videos from the map file itself to traits
            if (mapFormat < 9)
            {
                var rules = yaml.Nodes.FirstOrDefault(n => n.Key == "Rules");
                var worldNode = rules.Value.Nodes.FirstOrDefault(n => n.Key == "World");
                if (worldNode == null)
                    worldNode = new MiniYamlNode("World", new MiniYaml("", new List<MiniYamlNode>()));

                var playerNode = rules.Value.Nodes.FirstOrDefault(n => n.Key == "Player");
                if (playerNode == null)
                    playerNode = new MiniYamlNode("Player", new MiniYaml("", new List<MiniYamlNode>()));

                var visibilityNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Visibility");
                if (visibilityNode != null)
                {
                    var visibility = FieldLoader.GetValue<MapVisibility>("Visibility", visibilityNode.Value.Value);
                    if (visibility.HasFlag(MapVisibility.MissionSelector))
                    {
                        var missionData = new MiniYamlNode("MissionData", new MiniYaml("", new List<MiniYamlNode>()));
                        worldNode.Value.Nodes.Add(missionData);

                        var description = yaml.Nodes.FirstOrDefault(n => n.Key == "Description");
                        if (description != null)
                            missionData.Value.Nodes.Add(new MiniYamlNode("Briefing", description.Value.Value));

                        var videos = yaml.Nodes.FirstOrDefault(n => n.Key == "Videos");
                        if (videos != null && videos.Value.Nodes.Any())
                        {
                            var backgroundVideo = videos.Value.Nodes.FirstOrDefault(n => n.Key == "BackgroundInfo");
                            if (backgroundVideo != null)
                                missionData.Value.Nodes.Add(new MiniYamlNode("BackgroundVideo", backgroundVideo.Value.Value));

                            var briefingVideo = videos.Value.Nodes.FirstOrDefault(n => n.Key == "Briefing");
                            if (briefingVideo != null)
                                missionData.Value.Nodes.Add(new MiniYamlNode("BriefingVideo", briefingVideo.Value.Value));

                            var startVideo = videos.Value.Nodes.FirstOrDefault(n => n.Key == "GameStart");
                            if (startVideo != null)
                                missionData.Value.Nodes.Add(new MiniYamlNode("StartVideo", startVideo.Value.Value));

                            var winVideo = videos.Value.Nodes.FirstOrDefault(n => n.Key == "GameWon");
                            if (winVideo != null)
                                missionData.Value.Nodes.Add(new MiniYamlNode("WinVideo", winVideo.Value.Value));

                            var lossVideo = videos.Value.Nodes.FirstOrDefault(n => n.Key == "GameLost");
                            if (lossVideo != null)
                                missionData.Value.Nodes.Add(new MiniYamlNode("LossVideo", lossVideo.Value.Value));
                        }
                    }
                }

                var mapOptions = yaml.Nodes.FirstOrDefault(n => n.Key == "Options");
                if (mapOptions != null)
                {
                    var cheats = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "Cheats");
                    if (cheats != null)
                    {
                        worldNode.Value.Nodes.Add(new MiniYamlNode("DeveloperMode", new MiniYaml("", new List<MiniYamlNode>()
                        {
                            new MiniYamlNode("Locked", "True"),
                            new MiniYamlNode("Enabled", cheats.Value.Value)
                        })));
                    }

                    var crates = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "Crates");
                    if (crates != null && !worldNode.Value.Nodes.Any(n => n.Key == "-CrateSpawner"))
                    {
                        if (!FieldLoader.GetValue<bool>("crates", crates.Value.Value))
                            worldNode.Value.Nodes.Add(new MiniYamlNode("-CrateSpawner", new MiniYaml("")));
                    }

                    var creeps = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "Creeps");
                    if (creeps != null)
                    {
                        worldNode.Value.Nodes.Add(new MiniYamlNode("MapCreeps", new MiniYaml("", new List<MiniYamlNode>()
                        {
                            new MiniYamlNode("Locked", "True"),
                            new MiniYamlNode("Enabled", creeps.Value.Value)
                        })));
                    }

                    var fog = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "Fog");
                    var shroud = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "Shroud");
                    if (fog != null || shroud != null)
                    {
                        var shroudNode = new MiniYamlNode("Shroud", new MiniYaml("", new List<MiniYamlNode>()));
                        playerNode.Value.Nodes.Add(shroudNode);

                        if (fog != null)
                        {
                            shroudNode.Value.Nodes.Add(new MiniYamlNode("FogLocked", "True"));
                            shroudNode.Value.Nodes.Add(new MiniYamlNode("FogEnabled", fog.Value.Value));
                        }

                        if (shroud != null)
                        {
                            var enabled = FieldLoader.GetValue<bool>("shroud", shroud.Value.Value);
                            shroudNode.Value.Nodes.Add(new MiniYamlNode("ExploredMapLocked", "True"));
                            shroudNode.Value.Nodes.Add(new MiniYamlNode("ExploredMapEnabled", FieldSaver.FormatValue(!enabled)));
                        }
                    }

                    var allyBuildRadius = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "AllyBuildRadius");
                    if (allyBuildRadius != null)
                    {
                        worldNode.Value.Nodes.Add(new MiniYamlNode("MapBuildRadius", new MiniYaml("", new List<MiniYamlNode>()
                        {
                            new MiniYamlNode("AllyBuildRadiusLocked", "True"),
                            new MiniYamlNode("AllyBuildRadiusEnabled", allyBuildRadius.Value.Value)
                        })));
                    }

                    var startingCash = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "StartingCash");
                    if (startingCash != null)
                    {
                        playerNode.Value.Nodes.Add(new MiniYamlNode("PlayerResources", new MiniYaml("", new List<MiniYamlNode>()
                        {
                            new MiniYamlNode("DefaultCashLocked", "True"),
                            new MiniYamlNode("DefaultCash", startingCash.Value.Value)
                        })));
                    }

                    var startingUnits = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "ConfigurableStartingUnits");
                    if (startingUnits != null && !worldNode.Value.Nodes.Any(n => n.Key == "-SpawnMPUnits"))
                    {
                        worldNode.Value.Nodes.Add(new MiniYamlNode("SpawnMPUnits", new MiniYaml("", new List<MiniYamlNode>()
                        {
                            new MiniYamlNode("Locked", "True"),
                        })));
                    }

                    var techLevel = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "TechLevel");
                    var difficulties = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "Difficulties");
                    var shortGame = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "ShortGame");
                    if (techLevel != null || difficulties != null || shortGame != null)
                    {
                        var optionsNode = new MiniYamlNode("MapOptions", new MiniYaml("", new List<MiniYamlNode>()));
                        worldNode.Value.Nodes.Add(optionsNode);

                        if (techLevel != null)
                        {
                            optionsNode.Value.Nodes.Add(new MiniYamlNode("TechLevelLocked", "True"));
                            optionsNode.Value.Nodes.Add(new MiniYamlNode("TechLevel", techLevel.Value.Value));
                        }

                        if (difficulties != null)
                            optionsNode.Value.Nodes.Add(new MiniYamlNode("Difficulties", difficulties.Value.Value));

                        if (shortGame != null)
                        {
                            optionsNode.Value.Nodes.Add(new MiniYamlNode("ShortGameLocked", "True"));
                            optionsNode.Value.Nodes.Add(new MiniYamlNode("ShortGameEnabled", shortGame.Value.Value));
                        }
                    }
                }

                if (worldNode.Value.Nodes.Any() && !rules.Value.Nodes.Contains(worldNode))
                    rules.Value.Nodes.Add(worldNode);

                if (playerNode.Value.Nodes.Any() && !rules.Value.Nodes.Contains(playerNode))
                    rules.Value.Nodes.Add(playerNode);
            }

            // Format 9 -> 10 moved smudges to SmudgeLayer, and uses map.png for all maps
            if (mapFormat < 10)
            {
                ExtractSmudges(yaml);
                if (package.Contains("map.png"))
                    yaml.Nodes.Add(new MiniYamlNode("LockPreview", new MiniYaml("True")));
            }

            // Format 10 -> 11 replaced the single map type field with a list of categories
            if (mapFormat < 11)
            {
                var type = yaml.Nodes.First(n => n.Key == "Type");
                yaml.Nodes.Add(new MiniYamlNode("Categories", type.Value));
                yaml.Nodes.Remove(type);
            }

            if (mapFormat < Map.SupportedMapFormat)
            {
                yaml.Nodes.First(n => n.Key == "MapFormat").Value = new MiniYaml(Map.SupportedMapFormat.ToString());
                Console.WriteLine("Converted {0} to MapFormat {1}.", package.Name, Map.SupportedMapFormat);
            }

            package.Update("map.yaml", Encoding.UTF8.GetBytes(yaml.Nodes.WriteToString()));
        }
Exemple #48
0
        internal static void UpgradeActors(ModData modData, int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            foreach (var node in nodes)
            {
                // Fix RA building footprints to not use _ when it's not necessary
                if (engineVersion < 20160619 && modData.Manifest.Id == "ra" && depth == 1)
                {
                    var buildings = new List<string>() { "tsla", "gap", "agun", "apwr", "fapw" };
                    if (buildings.Contains(parent.Value.Value) && node.Key == "Location")
                        ModifyCPos(ref node.Value.Value, new CVec(0, 1));
                }

                // Fix TD building footprints to not use _ when it's not necessary
                if (engineVersion < 20160619 && modData.Manifest.Id == "cnc" && depth == 1)
                {
                    var buildings = new List<string>() { "atwr", "obli", "tmpl", "weap", "hand" };
                    if (buildings.Contains(parent.Value.Value) && node.Key == "Location")
                        ModifyCPos(ref node.Value.Value, new CVec(0, 1));
                }

                UpgradeActors(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
            }
        }
Exemple #49
0
        internal static void UpgradeActorRules(ModData modData, int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            var addNodes = new List<MiniYamlNode>();

            foreach (var node in nodes)
            {
                if (engineVersion < 20160515)
                {
                    // Use generic naming for building demolition using explosives.
                    if (node.Key == "C4Demolition")
                        node.Key = "Demolition";

                    foreach (var n in node.Value.Nodes)
                        if (n.Key == "C4Delay")
                            n.Key = "DetonationDelay";
                }

                // WithSmoke was refactored to become more generic and Sequence/Image notation has been unified.
                if (engineVersion < 20160528)
                {
                    if (depth == 1 && node.Key.StartsWith("WithSmoke"))
                    {
                        var s = node.Value.Nodes.FirstOrDefault(n => n.Key == "Sequence");
                        if (s != null)
                            s.Key = "Image";

                        var parts = node.Key.Split('@');
                        node.Key = "WithDamageOverlay";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];
                    }
                }

                if (engineVersion < 20160604 && node.Key.StartsWith("ProvidesTechPrerequisite"))
                {
                    var name = node.Value.Nodes.First(n => n.Key == "Name");
                    var id = name.Value.Value.ToLowerInvariant().Replace(" ", "");
                    node.Value.Nodes.Add(new MiniYamlNode("Id", id));
                }

                if (engineVersion < 20160611)
                {
                    // Deprecated WithSpriteRotorOverlay
                    if (depth == 1 && node.Key.StartsWith("WithSpriteRotorOverlay"))
                    {
                        var parts = node.Key.Split('@');
                        node.Key = "WithIdleOverlay";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];

                        Console.WriteLine("The 'WithSpriteRotorOverlay' trait has been removed.");
                        Console.WriteLine("Its functionality can be fully replicated with 'WithIdleOverlay' + upgrades.");
                        Console.WriteLine("Look at the helicopters in our RA / C&C1  mods for implementation details.");
                    }
                }

                // Map difficulty configuration was split to a generic trait
                if (engineVersion < 20160614 && node.Key.StartsWith("MapOptions"))
                {
                    var difficultiesNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Difficulties");
                    if (difficultiesNode != null)
                    {
                        var difficulties = FieldLoader.GetValue<string[]>("Difficulties", difficultiesNode.Value.Value)
                            .ToDictionary(d => d.Replace(" ", "").ToLowerInvariant(), d => d);
                        node.Value.Nodes.Remove(difficultiesNode);

                        var childNodes = new List<MiniYamlNode>()
                        {
                            new MiniYamlNode("ID", "difficulty"),
                            new MiniYamlNode("Label", "Difficulty"),
                            new MiniYamlNode("Values", new MiniYaml("", difficulties.Select(kv => new MiniYamlNode(kv.Key, kv.Value)).ToList()))
                        };

                        var difficultyNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Difficulty");
                        if (difficultyNode != null)
                        {
                            childNodes.Add(new MiniYamlNode("Default", difficultyNode.Value.Value.Replace(" ", "").ToLowerInvariant()));
                            node.Value.Nodes.Remove(difficultyNode);
                        }
                        else
                            childNodes.Add(new MiniYamlNode("Default", difficulties.Keys.First()));

                        var lockedNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "DifficultyLocked");
                        if (lockedNode != null)
                        {
                            childNodes.Add(new MiniYamlNode("Locked", lockedNode.Value.Value));
                            node.Value.Nodes.Remove(lockedNode);
                        }

                        addNodes.Add(new MiniYamlNode("ScriptLobbyDropdown@difficulty", new MiniYaml("", childNodes)));
                    }
                }

                if (engineVersion < 20160702)
                {
                    if (node.Key.StartsWith("GivesExperience"))
                    {
                        var ff = "FriendlyFire";
                        var ffNode = node.Value.Nodes.FirstOrDefault(n => n.Key == ff);
                        if (ffNode != null)
                        {
                            var newStanceStr = "";
                            if (FieldLoader.GetValue<bool>(ff, ffNode.Value.Value))
                                newStanceStr = "Neutral, Enemy, Ally";
                            else
                                newStanceStr = "Neutral, Enemy";

                            node.Value.Nodes.Add(new MiniYamlNode("ValidStances", newStanceStr));
                        }

                        node.Value.Nodes.Remove(ffNode);
                    }
                    else if (node.Key.StartsWith("GivesBounty"))
                    {
                        var stancesNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Stances");
                        if (stancesNode != null)
                            stancesNode.Key = "ValidStances";
                    }
                }

                if (engineVersion < 20160703)
                {
                    if (node.Key.StartsWith("WithDecoration") || node.Key.StartsWith("WithRankDecoration") || node.Key.StartsWith("WithDecorationCarryable"))
                    {
                        var stancesNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Stances");
                        if (stancesNode != null)
                            stancesNode.Key = "ValidStances";
                    }
                }

                if (engineVersion < 20160704)
                {
                    if (node.Key.Contains("PoisonedByTiberium"))
                    {
                        node.Key = node.Key.Replace("PoisonedByTiberium", "DamagedByTerrain");
                        if (!node.Key.StartsWith("-"))
                        {
                            if (node.Value.Nodes.Any(a => a.Key == "Resources"))
                                node.Value.Nodes.Where(n => n.Key == "Resources").Do(n => n.Key = "Terrain");
                            else
                                node.Value.Nodes.Add(new MiniYamlNode("Terrain", new MiniYaml("Tiberium, BlueTiberium")));

                            Console.WriteLine("PoisonedByTiberium: Weapon isn't converted. Copy out the appropriate");
                            Console.WriteLine("weapon's Damage, ReloadDelay and DamageTypes to DamagedByTerrain's Damage,");
                            Console.WriteLine("DamageInterval and DamageTypes, respectively, then remove the Weapon tag.");
                        }
                    }

                    if (node.Key.Contains("DamagedWithoutFoundation"))
                    {
                        node.Key = node.Key.Replace("DamagedWithoutFoundation", "DamagedByTerrain");
                        if (!node.Key.StartsWith("-"))
                        {
                            Console.WriteLine("DamagedWithoutFoundation: Weapon isn't converted. Copy out the appropriate");
                            Console.WriteLine("weapon's Damage, ReloadDelay and DamageTypes to DamagedByTerrain's Damage,");
                            Console.WriteLine("DamageInterval and DamageTypes, respectively, then remove the Weapon tag.");

                            Console.WriteLine("SafeTerrain isn't converted. Setup an inverted check using Terrain.");

                            node.Value.Nodes.Add(new MiniYamlNode("StartOnThreshold", new MiniYaml("true")));
                            if (!node.Value.Nodes.Any(a => a.Key == "DamageThreshold"))
                                node.Value.Nodes.Add(new MiniYamlNode("DamageThreshold", new MiniYaml("50")));
                        }
                    }
                }

                // ParticleDensityFactor was converted from a float to an int
                if (engineVersion < 20160713 && node.Key == "WeatherOverlay")
                {
                    var density = node.Value.Nodes.FirstOrDefault(n => n.Key == "ParticleDensityFactor");
                    if (density != null)
                    {
                        var value = float.Parse(density.Value.Value, CultureInfo.InvariantCulture);
                        value = (int)Math.Round(value * 10000, 0);
                        density.Value.Value = value.ToString();
                    }
                }

                if (engineVersion < 20160717)
                {
                    if (depth == 0)
                    {
                        var selectionDecorations = node.Value.Nodes.FirstOrDefault(n => n.Key == "SelectionDecorations");
                        if (selectionDecorations != null)
                            node.Value.Nodes.Add(selectionDecorations = new MiniYamlNode("WithSpriteControlGroup", ""));
                    }
                }

                if (engineVersion < 20160818)
                {
                    if (depth == 1 && node.Key.StartsWith("UpgradeOnDamage"))
                    {
                        var parts = node.Key.Split('@');
                        node.Key = "UpgradeOnDamageState";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];
                    }
                }

                // DisplayTimer was replaced by DisplayTimerStances
                if (engineVersion < 20160820)
                {
                    if (node.Key == "DisplayTimer")
                    {
                        node.Key = "DisplayTimerStances";

                        if (node.Value.Value.ToLower() == "false")
                            node.Value.Value = "None";
                        else
                            node.Value.Value = "Ally, Neutral, Enemy";
                    }
                }

                if (engineVersion < 20160821)
                {
                    // Shifted custom build time properties to Buildable
                    if (depth == 0)
                    {
                        var cbtv = node.Value.Nodes.FirstOrDefault(n => n.Key == "CustomBuildTimeValue");
                        if (cbtv != null)
                        {
                            var bi = node.Value.Nodes.FirstOrDefault(n => n.Key == "Buildable");

                            if (bi == null)
                                node.Value.Nodes.Add(bi = new MiniYamlNode("Buildable", ""));

                            var value = cbtv.Value.Nodes.First(n => n.Key == "Value");
                            value.Key = "BuildDuration";
                            bi.Value.Nodes.Add(value);
                            bi.Value.Nodes.Add(new MiniYamlNode("BuildDurationModifier", "40"));
                        }

                        node.Value.Nodes.RemoveAll(n => n.Key == "CustomBuildTimeValue");
                        node.Value.Nodes.RemoveAll(n => n.Key == "-CustomBuildTimeValue");
                    }

                    // rename ProductionQueue.BuildSpeed
                    if (node.Key == "BuildSpeed")
                    {
                        node.Key = "BuildDurationModifier";
                        var oldValue = FieldLoader.GetValue<int>(node.Key, node.Value.Value);
                        oldValue = oldValue * 100 / 40;
                        node.Value.Value = oldValue.ToString();
                    }
                }

                if (engineVersion < 20160826 && depth == 0)
                {
                    // Removed debug visualization
                    node.Value.Nodes.RemoveAll(n => n.Key == "PathfinderDebugOverlay");
                }

                // AlliedMissiles on JamsMissiles was changed from a boolean to a Stances field and renamed
                if (engineVersion < 20160827)
                {
                    if (node.Key == "JamsMissiles")
                    {
                        var alliedMissiles = node.Value.Nodes.FirstOrDefault(n => n.Key == "AlliedMissiles");
                        if (alliedMissiles != null)
                        {
                            alliedMissiles.Value.Value = FieldLoader.GetValue<bool>("AlliedMissiles", alliedMissiles.Value.Value) ? "Ally, Neutral, Enemy" : "Neutral, Enemy";
                            alliedMissiles.Key = "DeflectionStances";
                        }
                    }
                }

                // Add a warning to add WithRearmAnimation to actors that might need it.
                // Update rule added during prep-1609 stable period, date needs fixing after release.
                if (engineVersion < 20160918 && depth == 2)
                {
                    if (node.Key == "RearmBuildings")
                        foreach (var host in node.Value.Value.Split(','))
                            Console.WriteLine("Actor type `{0}` is denoted as a RearmBuilding. Consider adding the `WithRearmAnimation` trait to it.".F(host));
                }

                UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
            }

            foreach (var a in addNodes)
                nodes.Add(a);
        }
Exemple #50
0
        internal static void UpgradeActors(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            foreach (var node in nodes)
            {
                if (engineVersion < 20150430)
                {
                    if (node.Key == "Health")
                        ConvertFloatToIntPercentage(ref node.Value.Value);
                }

                if (engineVersion < 20150715)
                {
                    if (node.Key == "Race")
                        node.Key = "Faction";
                }

                UpgradeActors(engineVersion, ref node.Value.Nodes, node, depth + 1);
            }
        }
Exemple #51
0
		static void UpgradeWeaponRules(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
		{
			var parentKey = parent != null ? parent.Key.Split('@').First() : null;

			foreach (var node in nodes)
			{
				// Weapon definitions were converted to world coordinates
				if (engineVersion < 20131226)
				{
					if (depth == 1)
					{
						switch (node.Key)
						{
							case "Range":
							case "MinRange":
								ConvertFloatToRange(ref node.Value.Value);
								break;
							default:
								break;
						}
					}

					if (depth == 2 && parentKey == "Projectile")
					{
						switch (node.Key)
						{
							case "Inaccuracy":
								ConvertPxToRange(ref node.Value.Value);
								break;
							case "Angle":
								ConvertAngle(ref node.Value.Value);
								break;
							case "Speed":
							{
								if (parent.Value.Value == "Missile")
									ConvertPxToRange(ref node.Value.Value, 1, 5);
								if (parent.Value.Value == "Bullet")
									ConvertPxToRange(ref node.Value.Value, 2, 5);
								break;
							}
							default:
								break;
						}
					}

					if (depth == 2 && parentKey == "Warhead")
					{
						switch (node.Key)
						{
							case "Spread":
								ConvertPxToRange(ref node.Value.Value);
								break;
							default:
								break;
						}
					}
				}

				if (engineVersion < 20140615)
				{
					if (depth == 2 && parentKey == "Warhead" && node.Key == "Ore")
						node.Key = "DestroyResources";
				}

				UpgradeWeaponRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
			}
		}
Exemple #52
0
        static void ExtractSmudges(MiniYaml yaml)
        {
            var smudges = yaml.Nodes.FirstOrDefault(n => n.Key == "Smudges");
            if (smudges == null || !smudges.Value.Nodes.Any())
                return;

            var scorches = new List<MiniYamlNode>();
            var craters = new List<MiniYamlNode>();
            foreach (var s in smudges.Value.Nodes)
            {
                // loc=type,loc,depth
                var parts = s.Key.Split(' ');
                var value = "{0},{1}".F(parts[0], parts[2]);
                var node = new MiniYamlNode(parts[1], value);
                if (parts[0].StartsWith("sc"))
                    scorches.Add(node);
                else if (parts[0].StartsWith("cr"))
                    craters.Add(node);
            }

            var rulesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Rules");
            if (rulesNode == null)
            {
                rulesNode = new MiniYamlNode("Rules", new MiniYaml("", new List<MiniYamlNode>()));
                yaml.Nodes.Add(rulesNode);
            }

            var worldNode = rulesNode.Value.Nodes.FirstOrDefault(n => n.Key == "World");
            if (worldNode == null)
            {
                worldNode = new MiniYamlNode("World", new MiniYaml("", new List<MiniYamlNode>()));
                rulesNode.Value.Nodes.Add(rulesNode);
            }

            if (scorches.Any())
            {
                var initialScorches = new MiniYamlNode("InitialSmudges", new MiniYaml("", scorches));
                var smudgeLayer = new MiniYamlNode("SmudgeLayer@SCORCH", new MiniYaml("", new List<MiniYamlNode>() { initialScorches }));
                worldNode.Value.Nodes.Add(smudgeLayer);
            }

            if (craters.Any())
            {
                var initialCraters = new MiniYamlNode("InitialSmudges", new MiniYaml("", craters));
                var smudgeLayer = new MiniYamlNode("SmudgeLayer@CRATER", new MiniYaml("", new List<MiniYamlNode>() { initialCraters }));
                worldNode.Value.Nodes.Add(smudgeLayer);
            }
        }
Exemple #53
0
		static void UpgradeActorRules(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
		{
			var parentKey = parent != null ? parent.Key.Split('@').First() : null;

			foreach (var node in nodes)
			{
				// Weapon definitions were converted to world coordinates
				if (engineVersion < 20131226)
				{
					if (depth == 2 && parentKey == "Exit" && node.Key == "SpawnOffset")
						ConvertInt2ToWVec(ref node.Value.Value);

					if (depth == 2 && (parentKey == "Aircraft" || parentKey == "Helicopter" || parentKey == "Plane"))
					{
						if (node.Key == "CruiseAltitude")
							ConvertPxToRange(ref node.Value.Value);

						if (node.Key == "Speed")
							ConvertPxToRange(ref node.Value.Value, 7, 32);
					}

					if (depth == 2 && parentKey == "Mobile" && node.Key == "Speed")
						ConvertPxToRange(ref node.Value.Value, 1, 3);

					if (depth == 2 && parentKey == "Health" && node.Key == "Radius")
						ConvertPxToRange(ref node.Value.Value);
				}

				// CrateDrop was replaced with CrateSpawner
				if (engineVersion < 20131231)
				{
					if (depth == 1 && parentKey == "World")
					{
						if (node.Key == "CrateDrop")
							node.Key = "CrateSpawner";

						if (node.Key == "-CrateDrop")
							node.Key = "-CrateSpawner";
					}
				}

				// AttackTesla was replaced with AttackCharge
				if (engineVersion < 20140307)
				{
					if (depth == 1)
					{
						if (node.Key == "AttackTesla")
							node.Key = "AttackCharge";

						if (node.Key == "-AttackTesla")
							node.Key = "-AttackCharge";
					}
				}

				// AttackMove was generalized to support all moveable actor types
				if (engineVersion < 20140116)
				{
					if (depth == 1 && node.Key == "AttackMove")
						node.Value.Nodes.RemoveAll(n => n.Key == "JustMove");
				}

				// UnloadFacing was removed from Cargo
				if (engineVersion < 20140212)
				{
					if (depth == 1 && node.Key == "Cargo")
						node.Value.Nodes.RemoveAll(n => n.Key == "UnloadFacing");
				}

				// RevealShroud was updated to use world units.
				if (engineVersion < 20140220)
				{
					if (depth == 2 && parentKey == "RevealsShroud" && node.Key == "Range")
						ConvertFloatToRange(ref node.Value.Value);

					if (depth == 2 && parentKey == "CreatesShroud" && node.Key == "Range")
						ConvertFloatToRange(ref node.Value.Value);
				}

				// Waypoint was renamed to Immobile
				if (engineVersion < 20140312)
				{
					if (depth == 1 && node.Key == "Waypoint")
						node.Key = "Immobile";
				}

				// Spy was renamed to Disguise
				if (engineVersion < 20140314)
				{
					if (depth == 1 && node.Key == "Spy")
						node.Key = "Disguise";

					if (depth == 1 && node.Key == "SpyToolTip")
						node.Key = "DisguiseToolTip";

					if (depth == 1 && node.Key == "RenderSpy")
						node.Key = "RenderDisguise";
				}

				// IOccupySpace was removed from Mine
				if (engineVersion < 20140320)
				{
					if (depth == 0 && node.Value.Nodes.Any(n => n.Key == "Mine"))
						node.Value.Nodes.Add(new MiniYamlNode("Immobile", new MiniYaml("", new List<MiniYamlNode>() { new MiniYamlNode("OccupiesSpace", "true") })));
					else
						foreach (var i in nodes.Where(n => n.Key == "Immobile"))
							if (!i.Value.Nodes.Any(n => n.Key == "OccupiesSpace"))
								i.Value.Nodes.Add(new MiniYamlNode("OccupiesSpace", "false"));
				}

				// Armaments and muzzleflashes were reworked to support garrisoning
				if (engineVersion < 20140321)
				{
					if (depth == 0)
					{
						var muzzles = node.Value.Nodes.Where(n => n.Key.StartsWith("WithMuzzleFlash"));
						var armaments = node.Value.Nodes.Where(n => n.Key.StartsWith("Armament"));

						// Shift muzzle flash definitions to Armament
						foreach (var m in muzzles)
						{
							var muzzleArmNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "Armament");
							var muzzleSequenceNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "Sequence");
							var muzzleSplitFacingsNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "SplitFacings");
							var muzzleFacingsCountNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "FacingCount");

							var muzzleArmName = muzzleArmNode != null ? muzzleArmNode.Value.Value.Trim() : "primary";
							var muzzleSequence = muzzleSequenceNode != null ? muzzleSequenceNode.Value.Value.Trim() : "muzzle";
							var muzzleSplitFacings = muzzleSplitFacingsNode != null ? FieldLoader.GetValue<bool>("SplitFacings", muzzleSplitFacingsNode.Value.Value) : false;
							var muzzleFacingsCount = muzzleFacingsCountNode != null ? FieldLoader.GetValue<int>("FacingsCount", muzzleFacingsCountNode.Value.Value) : 8;

							foreach (var a in armaments)
							{
								var armNameNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "Name");
								var armName = armNameNode != null ? armNameNode.Value.Value.Trim() : "primary";

								if (muzzleArmName == armName)
								{
									a.Value.Nodes.Add(new MiniYamlNode("MuzzleSequence", muzzleSequence));
									if (muzzleSplitFacings)
										a.Value.Nodes.Add(new MiniYamlNode("MuzzleSplitFacings", muzzleFacingsCount.ToString()));
								}
							}
						}

						foreach (var m in muzzles.ToList().Skip(1))
							node.Value.Nodes.Remove(m);
					}

					// Remove all but the first muzzle flash definition
					if (depth == 1 && node.Key.StartsWith("WithMuzzleFlash"))
					{
						node.Key = "WithMuzzleFlash";
						node.Value.Nodes.RemoveAll(n => n.Key == "Armament");
						node.Value.Nodes.RemoveAll(n => n.Key == "Sequence");
					}
				}

				// "disabled" palette overlay has been moved into it's own DisabledOverlay trait
				if (engineVersion < 20140305)
				{
					if (node.Value.Nodes.Any(n => n.Key.StartsWith("RequiresPower"))
						&& !node.Value.Nodes.Any(n => n.Key.StartsWith("DisabledOverlay")))
					{
						node.Value.Nodes.Add(new MiniYamlNode("DisabledOverlay", new MiniYaml("")));
					}
				}

				// ChronoshiftDeploy was replaced with PortableChrono
				if (engineVersion < 20140321)
				{
					if (depth == 1 && node.Key == "ChronoshiftDeploy")
						node.Key = "PortableChrono";

					if (depth == 2 && parentKey == "PortableChrono" && node.Key == "JumpDistance")
						node.Key = "MaxDistance";
				}

				// Added new Lua API
				if (engineVersion < 20140421)
				{
					if (depth == 0 && node.Value.Nodes.Any(n => n.Key == "LuaScriptEvents"))
						node.Value.Nodes.Add(new MiniYamlNode("ScriptTriggers", ""));
				}

				if (engineVersion < 20140517)
				{
					if (depth == 0)
						node.Value.Nodes.RemoveAll(n => n.Key == "TeslaInstantKills");
				}

				if (engineVersion < 20140615)
				{
					if (depth == 1 && node.Key == "StoresOre")
						node.Key = "StoresResources";
				}

				// make animation is now it's own trait
				if (engineVersion < 20140621)
				{
					if (depth == 1 && (node.Key.StartsWith("RenderBuilding")))
						node.Value.Nodes.RemoveAll(n => n.Key == "HasMakeAnimation");

					if (node.Value.Nodes.Any(n => n.Key.StartsWith("RenderBuilding"))
						&& !node.Value.Nodes.Any(n => n.Key == "RenderBuildingWall")
						&& !node.Value.Nodes.Any(n => n.Key == "WithMakeAnimation"))
					{
						node.Value.Nodes.Add(new MiniYamlNode("WithMakeAnimation", new MiniYaml("")));
					}
				}

				// ParachuteAttachment was merged into Parachutable
				if (engineVersion < 20140701)
				{
					if (depth == 1 && node.Key == "ParachuteAttachment")
					{
						node.Key = "Parachutable";

						foreach (var subnode in node.Value.Nodes)
							if (subnode.Key == "Offset")
								subnode.Key = "ParachuteOffset";
					}

					if (depth == 2 && node.Key == "ParachuteSprite")
						node.Key = "ParachuteSequence";
				}

				// SonarPulsePower was implemented as a generic SpawnActorPower
				if (engineVersion < 20140703)
				{
					if (depth == 1 && node.Key == "SonarPulsePower")
						node.Key = "SpawnActorPower";
				}

				if (engineVersion < 20140707)
				{
					// SpyPlanePower was removed (use AirstrikePower instead)
					if (depth == 1 && node.Key == "SpyPlanePower")
					{
						node.Key = "AirstrikePower";

						var revealTime = 6 * 25;
						var revealNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "RevealTime");
						if (revealNode != null)
						{
							revealTime = int.Parse(revealNode.Value.Value) * 25;
							node.Value.Nodes.Remove(revealNode);
						}

						node.Value.Nodes.Add(new MiniYamlNode("CameraActor", new MiniYaml("camera")));
						node.Value.Nodes.Add(new MiniYamlNode("CameraRemoveDelay", new MiniYaml(revealTime.ToString())));
						node.Value.Nodes.Add(new MiniYamlNode("UnitType", new MiniYaml("u2")));
					}

					if (depth == 2 && node.Key == "LZRange" && parentKey == "ParaDrop")
					{
						node.Key = "DropRange";
						ConvertFloatToRange(ref node.Value.Value);
					}
				}

				UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
			}
		}
Exemple #54
0
        internal static void UpgradeActorRules(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            var parentKey = parent != null ? parent.Key.Split('@').First() : null;

            foreach (var node in nodes)
            {
                // Weapon definitions were converted to world coordinates
                if (engineVersion < 20131226)
                {
                    if (depth == 2 && parentKey == "Exit" && node.Key == "SpawnOffset")
                        ConvertInt2ToWVec(ref node.Value.Value);

                    if (depth == 2 && (parentKey == "Aircraft" || parentKey == "Helicopter" || parentKey == "Plane"))
                    {
                        if (node.Key == "CruiseAltitude")
                            ConvertPxToRange(ref node.Value.Value);

                        if (node.Key == "Speed")
                            ConvertPxToRange(ref node.Value.Value, 7, 32);
                    }

                    if (depth == 2 && parentKey == "Mobile" && node.Key == "Speed")
                        ConvertPxToRange(ref node.Value.Value, 1, 3);

                    if (depth == 2 && parentKey == "Health" && node.Key == "Radius")
                        ConvertPxToRange(ref node.Value.Value);
                }

                // CrateDrop was replaced with CrateSpawner
                if (engineVersion < 20131231)
                {
                    if (depth == 1 && parentKey == "World")
                    {
                        if (node.Key == "CrateDrop")
                            node.Key = "CrateSpawner";

                        if (node.Key == "-CrateDrop")
                            node.Key = "-CrateSpawner";
                    }
                }

                // AttackTesla was replaced with AttackCharge
                if (engineVersion < 20140307)
                {
                    if (depth == 1)
                    {
                        if (node.Key == "AttackTesla")
                            node.Key = "AttackCharge";

                        if (node.Key == "-AttackTesla")
                            node.Key = "-AttackCharge";
                    }
                }

                // AttackMove was generalized to support all moveable actor types
                if (engineVersion < 20140116)
                {
                    if (depth == 1 && node.Key == "AttackMove")
                        node.Value.Nodes.RemoveAll(n => n.Key == "JustMove");
                }

                // UnloadFacing was removed from Cargo
                if (engineVersion < 20140212)
                {
                    if (depth == 1 && node.Key == "Cargo")
                        node.Value.Nodes.RemoveAll(n => n.Key == "UnloadFacing");
                }

                // RevealShroud was updated to use world units.
                if (engineVersion < 20140220)
                {
                    if (depth == 2 && parentKey == "RevealsShroud" && node.Key == "Range")
                        ConvertFloatToRange(ref node.Value.Value);

                    if (depth == 2 && parentKey == "CreatesShroud" && node.Key == "Range")
                        ConvertFloatToRange(ref node.Value.Value);
                }

                // Waypoint was renamed to Immobile
                if (engineVersion < 20140312)
                {
                    if (depth == 1 && node.Key == "Waypoint")
                        node.Key = "Immobile";
                }

                // Spy was renamed to Disguise
                if (engineVersion < 20140314)
                {
                    if (depth == 1 && node.Key == "Spy")
                        node.Key = "Disguise";

                    if (depth == 1 && node.Key == "SpyToolTip")
                        node.Key = "DisguiseToolTip";

                    if (depth == 1 && node.Key == "RenderSpy")
                        node.Key = "RenderDisguise";
                }

                // IOccupySpace was removed from Mine
                if (engineVersion < 20140320)
                {
                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key == "Mine"))
                        node.Value.Nodes.Add(new MiniYamlNode("Immobile", new MiniYaml("", new List<MiniYamlNode>() { new MiniYamlNode("OccupiesSpace", "true") })));
                    else
                        foreach (var i in nodes.Where(n => n.Key == "Immobile"))
                            if (!i.Value.Nodes.Any(n => n.Key == "OccupiesSpace"))
                                i.Value.Nodes.Add(new MiniYamlNode("OccupiesSpace", "false"));
                }

                // Armaments and muzzleflashes were reworked to support garrisoning
                if (engineVersion < 20140321)
                {
                    if (depth == 0)
                    {
                        var muzzles = node.Value.Nodes.Where(n => n.Key.StartsWith("WithMuzzleFlash"));
                        var armaments = node.Value.Nodes.Where(n => n.Key.StartsWith("Armament"));

                        // Shift muzzle flash definitions to Armament
                        foreach (var m in muzzles)
                        {
                            var muzzleArmNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "Armament");
                            var muzzleSequenceNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "Sequence");
                            var muzzleSplitFacingsNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "SplitFacings");
                            var muzzleFacingsCountNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "FacingCount");

                            var muzzleArmName = muzzleArmNode != null ? muzzleArmNode.Value.Value.Trim() : "primary";
                            var muzzleSequence = muzzleSequenceNode != null ? muzzleSequenceNode.Value.Value.Trim() : "muzzle";
                            var muzzleSplitFacings = muzzleSplitFacingsNode != null ? FieldLoader.GetValue<bool>("SplitFacings", muzzleSplitFacingsNode.Value.Value) : false;
                            var muzzleFacingsCount = muzzleFacingsCountNode != null ? FieldLoader.GetValue<int>("FacingsCount", muzzleFacingsCountNode.Value.Value) : 8;

                            foreach (var a in armaments)
                            {
                                var armNameNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "Name");
                                var armName = armNameNode != null ? armNameNode.Value.Value.Trim() : "primary";

                                if (muzzleArmName == armName)
                                {
                                    a.Value.Nodes.Add(new MiniYamlNode("MuzzleSequence", muzzleSequence));
                                    if (muzzleSplitFacings)
                                        a.Value.Nodes.Add(new MiniYamlNode("MuzzleSplitFacings", muzzleFacingsCount.ToString()));
                                }
                            }
                        }

                        foreach (var m in muzzles.ToList().Skip(1))
                            node.Value.Nodes.Remove(m);
                    }

                    // Remove all but the first muzzle flash definition
                    if (depth == 1 && node.Key.StartsWith("WithMuzzleFlash"))
                    {
                        node.Key = "WithMuzzleFlash";
                        node.Value.Nodes.RemoveAll(n => n.Key == "Armament");
                        node.Value.Nodes.RemoveAll(n => n.Key == "Sequence");
                    }
                }

                // "disabled" palette overlay has been moved into it's own DisabledOverlay trait
                if (engineVersion < 20140305)
                {
                    if (node.Value.Nodes.Any(n => n.Key.StartsWith("RequiresPower"))
                        && !node.Value.Nodes.Any(n => n.Key.StartsWith("DisabledOverlay")))
                        node.Value.Nodes.Add(new MiniYamlNode("DisabledOverlay", new MiniYaml("")));
                }

                // ChronoshiftDeploy was replaced with PortableChrono
                if (engineVersion < 20140321)
                {
                    if (depth == 1 && node.Key == "ChronoshiftDeploy")
                        node.Key = "PortableChrono";

                    if (depth == 2 && parentKey == "PortableChrono" && node.Key == "JumpDistance")
                        node.Key = "MaxDistance";
                }

                // Added new Lua API
                if (engineVersion < 20140421)
                {
                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key == "LuaScriptEvents"))
                        node.Value.Nodes.Add(new MiniYamlNode("ScriptTriggers", ""));
                }

                if (engineVersion < 20140517)
                {
                    if (depth == 0)
                        node.Value.Nodes.RemoveAll(n => n.Key == "TeslaInstantKills");
                }

                if (engineVersion < 20140615)
                {
                    if (depth == 1 && node.Key == "StoresOre")
                        node.Key = "StoresResources";
                }

                // make animation is now its own trait
                if (engineVersion < 20140621)
                {
                    if (depth == 1 && node.Key.StartsWith("RenderBuilding"))
                        node.Value.Nodes.RemoveAll(n => n.Key == "HasMakeAnimation");

                    if (node.Value.Nodes.Any(n => n.Key.StartsWith("RenderBuilding"))
                        && !node.Value.Nodes.Any(n => n.Key == "RenderBuildingWall")
                        && !node.Value.Nodes.Any(n => n.Key == "WithMakeAnimation"))
                        node.Value.Nodes.Add(new MiniYamlNode("WithMakeAnimation", new MiniYaml("")));
                }

                // ParachuteAttachment was merged into Parachutable
                if (engineVersion < 20140701)
                {
                    if (depth == 1 && node.Key == "ParachuteAttachment")
                    {
                        node.Key = "Parachutable";

                        foreach (var subnode in node.Value.Nodes)
                            if (subnode.Key == "Offset")
                                subnode.Key = "ParachuteOffset";
                    }

                    if (depth == 2 && node.Key == "ParachuteSprite")
                        node.Key = "ParachuteSequence";
                }

                // SonarPulsePower was implemented as a generic SpawnActorPower
                if (engineVersion < 20140703)
                {
                    if (depth == 1 && node.Key == "SonarPulsePower")
                        node.Key = "SpawnActorPower";
                }

                if (engineVersion < 20140707)
                {
                    // SpyPlanePower was removed (use AirstrikePower instead)
                    if (depth == 1 && node.Key == "SpyPlanePower")
                    {
                        node.Key = "AirstrikePower";

                        var revealTime = 6 * 25;
                        var revealNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "RevealTime");
                        if (revealNode != null)
                        {
                            revealTime = int.Parse(revealNode.Value.Value) * 25;
                            node.Value.Nodes.Remove(revealNode);
                        }

                        node.Value.Nodes.Add(new MiniYamlNode("CameraActor", new MiniYaml("camera")));
                        node.Value.Nodes.Add(new MiniYamlNode("CameraRemoveDelay", new MiniYaml(revealTime.ToString())));
                        node.Value.Nodes.Add(new MiniYamlNode("UnitType", new MiniYaml("u2")));
                    }

                    if (depth == 2 && node.Key == "LZRange" && parentKey == "ParaDrop")
                    {
                        node.Key = "DropRange";
                        ConvertFloatToRange(ref node.Value.Value);
                    }
                }

                // GiveUnitCrateAction and GiveMcvCrateAction were updated to allow multiple units
                if (engineVersion < 20140723)
                {
                    if (depth == 2 && !string.IsNullOrEmpty(parentKey))
                    {
                        if (parentKey.Contains("GiveMcvCrateAction"))
                            if (node.Key == "Unit")
                                node.Key = "Units";

                        if (parentKey.Contains("GiveUnitCrateAction"))
                            if (node.Key == "Unit")
                                node.Key = "Units";
                    }
                }

                // Power from Building was moved out into Power and ScalePowerWithHealth traits
                if (engineVersion < 20140823)
                {
                    if (depth == 0)
                    {
                        var actorTraits = node.Value.Nodes;
                        var building = actorTraits.FirstOrDefault(t => t.Key == "Building");
                        if (building != null)
                        {
                            var buildingFields = building.Value.Nodes;
                            var power = buildingFields.FirstOrDefault(n => n.Key == "Power");
                            if (power != null)
                            {
                                buildingFields.Remove(power);

                                var powerFields = new List<MiniYamlNode> { new MiniYamlNode("Amount", power.Value) };
                                actorTraits.Add(new MiniYamlNode("Power", new MiniYaml("", powerFields)));

                                if (FieldLoader.GetValue<int>("Power", power.Value.Value) > 0)
                                    actorTraits.Add(new MiniYamlNode("ScaleWithHealth", ""));
                            }
                        }
                    }
                }

                if (engineVersion < 20140803)
                {
                    // ContainsCrate was removed (use LeavesHusk instead)
                    if (depth == 1 && node.Key == "ContainsCrate")
                    {
                        node.Key = "LeavesHusk";
                        node.Value.Nodes.Add(new MiniYamlNode("HuskActor", new MiniYaml("crate")));
                    }
                }

                if (engineVersion < 20140806)
                {
                    // remove ConquestVictoryConditions when StrategicVictoryConditions is set
                    if (depth == 0 && node.Key == "Player" && node.Value.Nodes.Any(n => n.Key == "StrategicVictoryConditions"))
                        node.Value.Nodes.Add(new MiniYamlNode("-ConquestVictoryConditions", ""));

                    // the objectives panel trait and its properties have been renamed
                    if (depth == 1 && node.Key == "ConquestObjectivesPanel")
                    {
                        node.Key = "ObjectivesPanel";
                        node.Value.Nodes.RemoveAll(_ => true);
                        node.Value.Nodes.Add(new MiniYamlNode("PanelName", new MiniYaml("SKIRMISH_STATS")));
                    }
                }

                // Veterancy was changed to use the upgrades system
                if (engineVersion < 20140807)
                {
                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("GainsExperience")))
                        node.Value.Nodes.Add(new MiniYamlNode("GainsStatUpgrades", new MiniYaml("")));

                    if (depth == 1 && node.Key == "-CloakCrateAction")
                        node.Key = "-UnitUpgradeCrateAction@cloak";

                    if (depth == 1 && node.Key == "CloakCrateAction")
                    {
                        node.Key = "UnitUpgradeCrateAction@cloak";
                        node.Value.Nodes.Add(new MiniYamlNode("Upgrades", new MiniYaml("cloak")));
                    }

                    if (depth == 2 && node.Key == "RequiresCrate" && parentKey == "Cloak")
                    {
                        node.Key = "RequiresUpgrade";
                        node.Value.Value = "cloak";
                    }
                }

                // Modifiers were changed to integer percentages
                if (engineVersion < 20140812)
                {
                    if (depth == 2 && node.Key == "ClosedDamageMultiplier" && parentKey == "AttackPopupTurreted")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);

                    if (depth == 2 && node.Key == "ArmorModifier" && parentKey == "GainsStatUpgrades")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);

                    if (depth == 2 && node.Key == "FullyLoadedSpeed" && parentKey == "Harvester")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);

                    if (depth == 2 && node.Key == "PanicSpeedModifier" && parentKey == "ScaredyCat")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);

                    if (depth == 2 && node.Key == "ProneSpeed" && parentKey == "TakeCover")
                    {
                        node.Key = "SpeedModifier";
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);
                    }

                    if (depth == 2 && node.Key == "SpeedModifier" && parentKey == "GainsStatUpgrades")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);

                    if (depth == 2 && node.Key == "FirepowerModifier" && parentKey == "GainsStatUpgrades")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);
                }

                // RemoveImmediately was replaced with RemoveOnConditions
                if (engineVersion < 20140821)
                {
                    if (depth == 1)
                    {
                        if (node.Key == "RemoveImmediately")
                            node.Key = "RemoveOnConditions";

                        if (node.Key == "-RemoveImmediately")
                            node.Key = "-RemoveOnConditions";
                    }
                }

                if (engineVersion < 20140823)
                {
                    if (depth == 2 && node.Key == "ArmorUpgrade" && parentKey == "GainsStatUpgrades")
                        node.Key = "DamageUpgrade";

                    if (depth == 2 && node.Key == "ArmorModifier" && parentKey == "GainsStatUpgrades")
                    {
                        node.Key = "DamageModifier";
                        node.Value.Value = string.Join(", ", node.Value.Value.Split(',')
                            .Select(s => ((int)(100 * 100 / float.Parse(s))).ToString()));
                    }

                    if (depth == 3 && parentKey == "Upgrades")
                        node.Value.Value = node.Value.Value.Replace("armor", "damage");
                }

                // RenderInfantryProne and RenderInfantryPanic was merged into RenderInfantry
                if (engineVersion < 20140824)
                {
                    var renderInfantryRemoval = node.Value.Nodes.FirstOrDefault(n => n.Key == "-RenderInfantry");
                    if (depth == 0 && renderInfantryRemoval != null && !node.Value.Nodes.Any(n => n.Key == "RenderDisguise"))
                        node.Value.Nodes.Remove(renderInfantryRemoval);

                    if (depth == 1 && (node.Key == "RenderInfantryProne" || node.Key == "RenderInfantryPanic"))
                        node.Key = "RenderInfantry";
                }

                // InfDeath was renamed to DeathType
                if (engineVersion < 20140830)
                {
                    if (depth == 2 && parentKey.StartsWith("DeathSounds") && node.Key == "InfDeaths")
                        node.Key = "DeathTypes";

                    if (depth == 2 && parentKey == "SpawnsViceroid" && node.Key == "InfDeath")
                        node.Key = "DeathType";

                    if (depth == 2 && parentKey == "Explodes" && node.Key == "InfDeath")
                        node.Key = "DeathType";
                }

                // SellSounds from Building was moved into Sellable
                if (engineVersion < 20140904)
                {
                    if (depth == 0)
                    {
                        var actorTraits = node.Value.Nodes;
                        var building = actorTraits.FirstOrDefault(t => t.Key == "Building");
                        if (building != null)
                        {
                            var buildingFields = building.Value.Nodes;
                            var sellSounds = buildingFields.FirstOrDefault(n => n.Key == "SellSounds");
                            if (sellSounds != null)
                            {
                                buildingFields.Remove(sellSounds);
                                var sellable = actorTraits.FirstOrDefault(t => t.Key == "Sellable");
                                if (sellable != null)
                                    sellable.Value.Nodes.Add(sellSounds);
                                else
                                {
                                    Console.WriteLine("Warning: Adding Sellable trait to {0} in {1}".F(node.Key, node.Location.Filename));
                                    actorTraits.Add(new MiniYamlNode("Sellable", new MiniYaml("", new List<MiniYamlNode> { sellSounds })));
                                }
                            }
                        }
                    }
                }

                // DuplicateUnitCrateAction was tidied up
                if (engineVersion < 20140912)
                {
                    if (depth == 2 && node.Key == "MaxDuplicatesWorth" && parentKey == "DuplicateUnitCrateAction")
                        node.Key = "MaxDuplicateValue";

                    if (depth == 2 && node.Key == "ValidDuplicateTypes" && parentKey == "DuplicateUnitCrateAction")
                        node.Key = "ValidTargets";
                }

                // Added WithDeathAnimation
                if (engineVersion < 20140913)
                {
                    var spawnsCorpseRemoval = node.Value.Nodes.FirstOrDefault(n => n.Key == "SpawnsCorpse");

                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("RenderInfantry")) && spawnsCorpseRemoval == null)
                        node.Value.Nodes.Add(new MiniYamlNode("WithDeathAnimation", new MiniYaml("")));

                    if (depth == 2 && node.Key == "SpawnsCorpse" && parentKey == "RenderInfantry")
                        node.Value.Nodes.Remove(spawnsCorpseRemoval);

                    // CrushableInfantry renamed to Crushable
                    if (depth == 1)
                    {
                        if (node.Key == "CrushableInfantry")
                            node.Key = "Crushable";

                        if (node.Key == "-CrushableInfantry")
                            node.Key = "-Crushable";
                    }
                }

                // Replaced Wall with Crushable + BlocksBullets
                if (engineVersion < 20140914)
                {
                    if (depth == 0)
                    {
                        var actorTraits = node.Value.Nodes;
                        var wall = actorTraits.FirstOrDefault(t => t.Key == "Wall");
                        if (wall != null)
                            node.Value.Nodes.Add(new MiniYamlNode("BlocksBullets", new MiniYaml("")));

                        var blocksBullets = actorTraits.FirstOrDefault(t => t.Key == "BlocksBullets");
                        if (depth == 1 && node.Key == "Wall" && blocksBullets != null)
                            node.Key = "Crushable";
                    }
                }

                if (engineVersion < 20140927)
                {
                    if (depth == 0)
                        node.Value.Nodes.RemoveAll(n => n.Key == "SelfHealingTech");

                    if (depth == 2 && node.Key == "RequiresTech" && parentKey.StartsWith("SelfHealing"))
                    {
                        node.Key = "RequiresUpgrade";
                        node.Value.Value = "selfhealing-needs-reconfiguration";
                    }
                }

                if (engineVersion < 20141001)
                {
                    // Routed unit upgrades via the UnitUpgradeManager trait
                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("GainsStatUpgrades")))
                        node.Value.Nodes.Add(new MiniYamlNode("UnitUpgradeManager", new MiniYaml("")));

                    // Replaced IronCurtainPower -> GrantUpgradePower
                    if (depth == 1 && node.Key == "IronCurtainPower")
                    {
                        node.Key = "GrantUpgradePower@IRONCURTAIN";
                        node.Value.Nodes.Add(new MiniYamlNode("Upgrades", "invulnerability"));

                        var durationNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Duration");
                        if (durationNode != null)
                            durationNode.Value.Value = (int.Parse(durationNode.Value.Value) * 25).ToString();
                        else
                            node.Value.Nodes.Add(new MiniYamlNode("Duration", "600"));

                        var soundNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "IronCurtainSound");
                        if (soundNode != null)
                            soundNode.Key = "GrantUpgradeSound";
                    }

                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("IronCurtainable")))
                    {
                        node.Value.Nodes.RemoveAll(n => n.Key.StartsWith("IronCurtainable"));

                        var overlayKeys = new List<MiniYamlNode>();
                        overlayKeys.Add(new MiniYamlNode("RequiresUpgrade", "invulnerability"));
                        node.Value.Nodes.Add(new MiniYamlNode("UpgradeOverlay@IRONCURTAIN", new MiniYaml("", overlayKeys)));

                        var invulnKeys = new List<MiniYamlNode>();
                        invulnKeys.Add(new MiniYamlNode("RequiresUpgrade", "invulnerability"));
                        node.Value.Nodes.Add(new MiniYamlNode("InvulnerabilityUpgrade@IRONCURTAIN", new MiniYaml("", invulnKeys)));

                        var barKeys = new List<MiniYamlNode>();
                        barKeys.Add(new MiniYamlNode("Upgrade", "invulnerability"));
                        node.Value.Nodes.Add(new MiniYamlNode("TimedUpgradeBar", new MiniYaml("", barKeys)));

                        if (!node.Value.Nodes.Any(n => n.Key.StartsWith("UnitUpgradeManager")))
                            node.Value.Nodes.Add(new MiniYamlNode("UnitUpgradeManager", new MiniYaml("")));
                    }

                    if (depth == 1 && node.Key == "-IronCurtainable")
                        node.Key = "-InvulnerabilityUpgrade@IRONCURTAIN";

                    // Replaced RemoveOnConditions with KillsSelf
                    if (depth == 1 && node.Key == "RemoveOnConditions")
                    {
                        node.Key = "KillsSelf";
                        node.Value.Nodes.Add(new MiniYamlNode("RemoveInstead", new MiniYaml("true")));
                    }

                    if (depth == 1 && node.Key.StartsWith("UnitUpgradeCrateAction"))
                    {
                        var parts = node.Key.Split('@');
                        node.Key = "GrantUpgradeCrateAction";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];
                    }

                    if (depth == 1 && node.Key.StartsWith("-UnitUpgradeCrateAction"))
                    {
                        var parts = node.Key.Split('@');
                        node.Key = "-GrantUpgradeCrateAction";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];
                    }
                }

                if (engineVersion < 20141002)
                {
                    if (node.Key == "AlignWhenIdle" && parentKey == "Turreted")
                    {
                        node.Key = "RealignDelay";
                        node.Value.Value = "0";
                    }
                }

                // Replaced BelowUnits with per sequence ZOffsets
                if (engineVersion < 20141030)
                {
                    if (depth == 0)
                    {
                        node.Value.Nodes.RemoveAll(n => n.Key == "BelowUnits");
                        node.Value.Nodes.RemoveAll(n => n.Key == "-BelowUnits");
                    }
                }

                if (engineVersion < 20141121)
                {
                    if (depth == 1)
                    {
                        if (node.Value.Nodes.Exists(n => n.Key == "RestrictedByUpgrade"))
                        {
                            node.Value.Nodes.Add(new MiniYamlNode("UpgradeMaxEnabledLevel", "0"));
                            node.Value.Nodes.Add(new MiniYamlNode("UpgradeMaxAcceptedLevel", "1"));
                        }
                        else if (node.Value.Nodes.Exists(n => n.Key == "RequiresUpgrade"))
                            node.Value.Nodes.Add(new MiniYamlNode("UpgradeMinEnabledLevel", "1"));

                        if (node.Key.StartsWith("DisableUpgrade") && !node.Value.Nodes.Any(n => n.Key == "RequiresUpgrade" || n.Key == "UpgradeTypes"))
                            node.Value.Nodes.Add(new MiniYamlNode("UpgradeTypes", "disable"));

                        if (node.Key.StartsWith("InvulnerabilityUpgrade") && !node.Value.Nodes.Any(n => n.Key == "RequiresUpgrade" || n.Key == "UpgradeTypes"))
                            node.Value.Nodes.Add(new MiniYamlNode("UpgradeTypes", "invulnerability"));
                    }
                    else if (depth == 2)
                    {
                        if (node.Key == "RequiresUpgrade" || node.Key == "RestrictedByUpgrade")
                            node.Key = "UpgradeTypes";
                        else if (node.Key == "-RequiresUpgrade" || node.Key == "-RestrictedByUpgrade")
                            node.Key = "-UpgradeTypes";
                    }
                }

                // Adjust MustBeDestroyed for short games
                if (engineVersion < 20141218)
                    if (depth == 1 && node.Key == "MustBeDestroyed")
                        node.Value.Nodes.Add(new MiniYamlNode("RequiredForShortGame", "true"));

                if (engineVersion < 20150125)
                {
                    // Remove PlayMusicOnMapLoad
                    if (depth == 0 && node.Value.Nodes.Exists(n => n.Key == "PlayMusicOnMapLoad"))
                    {
                        node.Value.Nodes.RemoveAll(n => n.Key == "PlayMusicOnMapLoad");
                        Console.WriteLine("The 'PlayMusicOnMapLoad' trait has been removed.");
                        Console.WriteLine("Please use the Lua API function 'PlayMusic' instead.");
                        Console.WriteLine("See http://wiki.openra.net/Lua-API for details.");
                    }

                    // Remove TiberiumRefinery and OreRefinery
                    if (node.Key == "TiberiumRefinery" || node.Key == "OreRefinery")
                        node.Key = "Refinery";
                }

                // Append an 's' as the fields were changed from string to string[]
                if (engineVersion < 20150311)
                {
                    if (depth == 2 && parentKey == "SoundOnDamageTransition")
                    {
                        if (node.Key == "DamagedSound")
                            node.Key = "DamagedSounds";
                        else if (node.Key == "DestroyedSound")
                            node.Key = "DestroyedSounds";
                    }
                }

                if (engineVersion < 20150321)
                {
                    // Note: These rules are set up to do approximately the right thing for maps, but
                    // mods need additional manual tweaks. This is the best we can do without having
                    // much smarter rules parsing, because we currently can't reason about inherited traits.
                    if (depth == 0)
                    {
                        var childKeys = new[] { "MinIdleWaitTicks", "MaxIdleWaitTicks", "MoveAnimation", "AttackAnimation", "IdleAnimations", "StandAnimations" };

                        var ri = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("RenderInfantry"));
                        if (ri != null)
                        {
                            ri.Key = "WithInfantryBody";

                            var rsNodes = ri.Value.Nodes.Where(n => !childKeys.Contains(n.Key)).ToList();
                            if (rsNodes.Any())
                                node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", new MiniYaml("", rsNodes)));

                            ri.Value.Nodes.RemoveAll(n => rsNodes.Contains(n));
                        }

                        var rri = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("-RenderInfantry"));
                        if (rri != null)
                            rri.Key = "-WithInfantryBody";

                        var rdi = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("RenderDisguise"));
                        if (rdi != null)
                        {
                            rdi.Key = "WithDisguisingInfantryBody";

                            var rsNodes = rdi.Value.Nodes.Where(n => !childKeys.Contains(n.Key)).ToList();
                            if (rsNodes.Any())
                                node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", new MiniYaml("", rsNodes)));

                            rdi.Value.Nodes.RemoveAll(n => rsNodes.Contains(n));
                        }

                        var rrdi = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("-RenderDisguise"));
                        if (rrdi != null)
                            rrdi.Key = "-WithDisguisingInfantryBody";
                    }

                    if (depth == 2 && node.Key == "MoveAnimation")
                        node.Key = "MoveSequence";

                    if (depth == 2 && node.Key == "AttackAnimation")
                        node.Key = "AttackSequence";

                    if (depth == 2 && node.Key == "IdleAnimations")
                        node.Key = "IdleSequences";

                    if (depth == 2 && node.Key == "StandAnimations")
                        node.Key = "StandSequences";
                }

                if (engineVersion < 20150323)
                {
                    // Moved Reloads functionality to LimitedAmmo and refactored the latter into AmmoPool
                    if (depth == 0)
                    {
                        var actorTraits = node.Value.Nodes;
                        var limitedAmmo = actorTraits.FirstOrDefault(l => l.Key == "LimitedAmmo");
                        var reloads = actorTraits.FirstOrDefault(r => r.Key == "Reloads");

                        if (reloads != null)
                        {
                            var reloadsFields = reloads.Value.Nodes;
                            var limitedAmmoFields = limitedAmmo.Value.Nodes;
                            var count = reloadsFields.FirstOrDefault(c => c.Key == "Count");
                            var period = reloadsFields.FirstOrDefault(p => p.Key == "Period");
                            var resets = reloadsFields.FirstOrDefault(res => res.Key == "ResetOnFire");

                            var reloadsCount = count != null ? FieldLoader.GetValue<int>("Count", count.Value.Value) : -1;
                            var reloadsPeriod = period != null ? FieldLoader.GetValue<int>("Period", period.Value.Value) : 50;
                            var reloadsResetOnFire = resets != null ? FieldLoader.GetValue<bool>("ResetOnFire", resets.Value.Value) : false;

                            limitedAmmoFields.Add(new MiniYamlNode("SelfReloads", "true"));
                            limitedAmmoFields.Add(new MiniYamlNode("ReloadCount", reloadsCount.ToString()));
                            limitedAmmoFields.Add(new MiniYamlNode("SelfReloadTicks", reloadsPeriod.ToString()));
                            limitedAmmoFields.Add(new MiniYamlNode("ResetOnFire", reloadsResetOnFire.ToString()));

                            node.Value.Nodes.RemoveAll(n => n.Key == "Reloads");
                            node.Value.Nodes.RemoveAll(n => n.Key == "-Reloads");
                        }
                    }

                    // Moved RearmSound from Minelayer to LimitedAmmo/AmmoPool
                    if (depth == 0)
                    {
                        var actorTraits = node.Value.Nodes;
                        var limitedAmmo = actorTraits.FirstOrDefault(la => la.Key == "LimitedAmmo");
                        var minelayer = actorTraits.FirstOrDefault(ml => ml.Key == "Minelayer");

                        if (minelayer != null)
                        {
                            var minelayerFields = minelayer.Value.Nodes;
                            var limitedAmmoFields = limitedAmmo.Value.Nodes;
                            var rearmSound = minelayerFields.FirstOrDefault(rs => rs.Key == "RearmSound");
                            var minelayerRearmSound = rearmSound != null ? FieldLoader.GetValue<string>("RearmSound", rearmSound.Value.Value) : "minelay1.aud";

                            limitedAmmoFields.Add(new MiniYamlNode("RearmSound", minelayerRearmSound));
                            minelayerFields.Remove(rearmSound);
                        }
                    }

                    // Rename LimitedAmmo to AmmoPool
                    if (node.Key == "LimitedAmmo")
                        node.Key = "AmmoPool";
                }

                if (engineVersion < 20150326)
                {
                    // Rename BlocksBullets to BlocksProjectiles
                    if (node.Key == "BlocksBullets")
                        node.Key = "BlocksProjectiles";
                }

                if (engineVersion < 20150425)
                {
                    if (depth == 0)
                    {
                        var warFact = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("RenderBuildingWarFactory"));
                        if (warFact != null)
                        {
                            warFact.Key = "RenderBuilding";

                            if (node.Value.Nodes.Any(w => w.Key == "-RenderBuilding"))
                                node.Value.Nodes.RemoveAll(p => p.Key == "-RenderBuilding");

                            var doorOverlay = new MiniYamlNode("WithProductionDoorOverlay", "");
                            doorOverlay.Value.Nodes.Add(new MiniYamlNode("Sequence", "build-top"));
                            node.Value.Nodes.Add(doorOverlay);
                        }
                    }
                }

                if (engineVersion < 20150426)
                {
                    // Add DamageModifiers to TakeCover with a "Prone50Percent" default
                    // Add ProneTriggers to TakeCover with a "TriggerProne" default
                    if (node.Key == "TakeCover")
                    {
                        var percent = new MiniYamlNode("Prone50Percent", "50");
                        var dictionary = new MiniYamlNode("DamageModifiers", "");
                        dictionary.Value.Nodes.Add(percent);

                        if (node.Value.Nodes.All(x => x.Key != "DamageModifiers"))
                            node.Value.Nodes.Add(dictionary);

                        node.Value.Nodes.Add(new MiniYamlNode("DamageTriggers", "TriggerProne"));
                    }
                }

                if (engineVersion < 20150427)
                    if (node.Key.StartsWith("WithRotor"))
                        node.Value.Nodes.RemoveAll(p => p.Key == "Id");

                if (engineVersion < 20150430)
                {
                    if (node.Key.StartsWith("ProductionQueue@") || node.Key.StartsWith("ClassicProductionQueue@"))
                        node.Value.Nodes.RemoveAll(n => n.Key == "RequireOwner");

                    if (node.Key == "Buildable")
                    {
                        var removed = node.Value.Nodes.RemoveAll(n => n.Key == "Owner");
                        if (removed > 0)
                        {
                            Console.WriteLine("The 'Owner' field has been removed.");
                            Console.WriteLine("Please use prerequisites instead.");
                        }
                    }
                }

                if (engineVersion < 20150501)
                {
                    // Change RenderFlare to RenderSprites + WithSpriteBody
                    var flares = node.Value.Nodes.Where(x => x.Key == "RenderFlare");
                    if (flares.Any())
                    {
                        flares.Do(x => x.Key = "RenderSprites");
                        node.Value.Nodes.Add(new MiniYamlNode("WithSpriteBody", "", new List<MiniYamlNode>
                        {
                            new MiniYamlNode("StartSequence", "open")
                        }));
                    }

                    // Change WithFire to RenderSprites + WithSpriteBody
                    var fire = node.Value.Nodes.Where(x => x.Key == "WithFire");
                    if (fire.Any())
                    {
                        fire.Do(x => x.Key = "RenderSprites");
                        node.Value.Nodes.Add(new MiniYamlNode("WithSpriteBody", "", new List<MiniYamlNode>
                        {
                            new MiniYamlNode("StartSequence", "fire-start"),
                            new MiniYamlNode("Sequence", "fire-loop")
                        }));
                    }
                }

                if (engineVersion < 20150504)
                {
                    // Made buildings grant prerequisites explicitly.
                    if (depth == 0 && node.Value.Nodes.Exists(n => n.Key == "Inherits" &&
                        (n.Value.Value == "^Building" || n.Value.Value == "^BaseBuilding")))
                        node.Value.Nodes.Add(new MiniYamlNode("ProvidesCustomPrerequisite@buildingname", ""));

                    // Rename the ProvidesCustomPrerequisite trait.
                    if (node.Key.StartsWith("ProvidesCustomPrerequisite"))
                        node.Key = node.Key.Replace("ProvidesCustomPrerequisite", "ProvidesPrerequisite");
                }

                if (engineVersion < 20150509)
                {
                    if (depth == 0 && node.Value.Nodes.Exists(n => n.Key == "Selectable"))
                    {
                        var selectable = node.Value.Nodes.FirstOrDefault(n => n.Key == "Selectable");
                        var selectableNodes = selectable.Value.Nodes;
                        var voice = selectableNodes.FirstOrDefault(n => n.Key == "Voice");
                        var selectableVoice = voice != null ? FieldLoader.GetValue<string>("Voice", voice.Value.Value) : "";

                        if (voice != null)
                        {
                            node.Value.Nodes.Add(new MiniYamlNode("Voiced", "", new List<MiniYamlNode>
                            {
                                new MiniYamlNode("VoiceSet", selectableVoice),
                            }));
                        }
                    }

                    if (node.Key.StartsWith("Selectable"))
                        node.Value.Nodes.RemoveAll(p => p.Key == "Voice");
                }

                if (engineVersion < 20150524)
                {
                    // Replace numbers with strings for DeathSounds.DeathType
                    if (node.Key.StartsWith("DeathSounds"))
                    {
                        var deathTypes = node.Value.Nodes.FirstOrDefault(x => x.Key == "DeathTypes");
                        if (deathTypes != null)
                        {
                            var types = FieldLoader.GetValue<string[]>("DeathTypes", deathTypes.Value.Value);
                            deathTypes.Value.Value = string.Join(", ", types.Select(type => "DeathType" + type));

                            RenameDamageTypes(deathTypes);
                        }
                    }
                }

                if (engineVersion < 20150528)
                {
                    // Note (stolen from WithInfantryBody upgrade rule):
                    // These rules are set up to do approximately the right thing for maps, but
                    // mods need additional manual tweaks. This is the best we can do without having
                    // much smarter rules parsing, because we currently can't reason about inherited traits.
                    if (depth == 0)
                    {
                        var childKeys = new[] { "Sequence" };

                        var ru = node.Value.Nodes.FirstOrDefault(n => n.Key == "RenderUnit");
                        if (ru != null)
                        {
                            ru.Key = "WithFacingSpriteBody";
                            node.Value.Nodes.Add(new MiniYamlNode("AutoSelectionSize", ""));

                            var rsNodes = ru.Value.Nodes.Where(n => !childKeys.Contains(n.Key)).ToList();
                            if (rsNodes.Any())
                                node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", new MiniYaml("", rsNodes)));
                            else
                                node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", ""));

                            ru.Value.Nodes.RemoveAll(n => rsNodes.Contains(n));
                        }

                        var rru = node.Value.Nodes.FirstOrDefault(n => n.Key == "-RenderUnit");
                        if (rru != null)
                            rru.Key = "-WithFacingSpriteBody";
                    }

                    // For RenderUnitReload
                    var rur = node.Value.Nodes.Where(x => x.Key == "RenderUnitReload");
                    if (rur.Any())
                    {
                        rur.Do(x => x.Key = "RenderSprites");
                        node.Value.Nodes.Add(new MiniYamlNode("AutoSelectionSize", ""));
                        node.Value.Nodes.Add(new MiniYamlNode("WithFacingSpriteBody", "", new List<MiniYamlNode>
                        {
                            new MiniYamlNode("Sequence", "idle")
                        }));
                        node.Value.Nodes.Add(new MiniYamlNode("WithAttackAnimation", "", new List<MiniYamlNode>
                        {
                            new MiniYamlNode("AimSequence", "aim"),
                            new MiniYamlNode("ReloadPrefix", "empty-")
                        }));

                        var rrur = node.Value.Nodes.FirstOrDefault(n => n.Key == "-RenderUnitReload");
                        if (rrur != null)
                            rrur.Key = "-WithFacingSpriteBody";
                    }

                    // For RenderUnitFlying
                    var ruf = node.Value.Nodes.Where(x => x.Key == "RenderUnitFlying");
                    if (ruf.Any())
                    {
                        ruf.Do(x => x.Key = "RenderSprites");
                        node.Value.Nodes.Add(new MiniYamlNode("AutoSelectionSize", ""));
                        node.Value.Nodes.Add(new MiniYamlNode("WithFacingSpriteBody", ""));
                        node.Value.Nodes.Add(new MiniYamlNode("WithMoveAnimation", "", new List<MiniYamlNode>
                        {
                            new MiniYamlNode("MoveSequence", "move")
                        }));

                        var rruf = node.Value.Nodes.FirstOrDefault(n => n.Key == "-RenderUnitFlying");
                        if (rruf != null)
                            rruf.Key = "-WithFacingSpriteBody";
                    }
                }

                if (engineVersion < 20150607)
                {
                    // Add WithRankDecoration to all actors using GainsExperience
                    var ge = node.Value.Nodes.FirstOrDefault(n => n.Key == "GainsExperience");
                    if (ge != null)
                    {
                        var nodeUpgrades = ge.Value.Nodes.FirstOrDefault(n => n.Key == "Upgrades");
                        var upgrades = nodeUpgrades != null ? nodeUpgrades.Value.Nodes.Count() : 4;

                        var nodeChPal = ge.Value.Nodes.FirstOrDefault(n => n.Key == "ChevronPalette");
                        var chPal = nodeChPal != null && !string.IsNullOrEmpty(nodeChPal.Value.Value) ? nodeChPal.Value.Value : "effect";
                        ge.Value.Nodes.Remove(nodeChPal);

                        if (upgrades != 0 && nodeUpgrades != null)
                        {
                            foreach (var nodeUpgrade in nodeUpgrades.Value.Nodes)
                                nodeUpgrade.Value.Value = "rank" + (string.IsNullOrEmpty(nodeUpgrade.Value.Value) ? null : ", ") + nodeUpgrade.Value.Value;

                            node.Value.Nodes.Add(new MiniYamlNode("WithRankDecoration", null, new List<MiniYamlNode>
                            {
                                new MiniYamlNode("Image", "rank"),
                                new MiniYamlNode("Sequence", "rank"),
                                new MiniYamlNode("Palette", chPal),
                                new MiniYamlNode("ReferencePoint", "Bottom, Right"),
                                new MiniYamlNode("Offset", "2, 2"),
                                new MiniYamlNode("UpgradeTypes", "rank"),
                                new MiniYamlNode("ZOffset", "256"),
                                new MiniYamlNode("UpgradeMinEnabledLevel", "1"),
                                new MiniYamlNode("UpgradeMaxAcceptedLevel", upgrades.ToString())
                            }));
                        }
                    }
                }

                // Images from WithCrateBody was moved into RenderSprites
                if (engineVersion < 20150608)
                {
                    if (depth == 0)
                    {
                        var actorTraits = node.Value.Nodes;
                        var withCrateBody = actorTraits.FirstOrDefault(t => t.Key == "WithCrateBody");
                        if (withCrateBody != null)
                        {
                            var withCrateBodyFields = withCrateBody.Value.Nodes;
                            var images = withCrateBodyFields.FirstOrDefault(n => n.Key == "Images");
                            if (images == null)
                                images = new MiniYamlNode("Images", "crate");
                            else
                                withCrateBodyFields.Remove(images);

                            images.Key = "Image";

                            var renderSprites = actorTraits.FirstOrDefault(t => t.Key == "RenderSprites");
                            if (renderSprites != null)
                                renderSprites.Value.Nodes.Add(images);
                            else
                            {
                                Console.WriteLine("Warning: Adding RenderSprites trait to {0} in {1}".F(node.Key, node.Location.Filename));
                                actorTraits.Add(new MiniYamlNode("RenderSprites", new MiniYaml("", new List<MiniYamlNode> { images })));
                            }
                        }
                    }
                }

                // 'Selectable' boolean was removed from selectable trait.
                if (engineVersion < 20150619)
                {
                    if (depth == 1 && node.Value.Nodes.Exists(n => n.Key == "Selectable"))
                    {
                        var selectable = node.Value.Nodes.FirstOrDefault(n => n.Key == "Selectable");
                        if (node.Key == "Selectable" && selectable.Value.Value == "false")
                            node.Key = "SelectableRemoveMe";

                        // To cover rare cases where the boolean was 'true'
                        if (node.Key == "Selectable" && selectable.Value.Value == "true")
                            node.Value.Nodes.Remove(selectable);
                    }

                    if (depth == 0 && node.Value.Nodes.Exists(n => n.Key == "SelectableRemoveMe"))
                    {
                        node.Value.Nodes.RemoveAll(n => n.Key == "SelectableRemoveMe");
                        Console.WriteLine("The 'Selectable' boolean has been removed from the Selectable trait.");
                        Console.WriteLine("If you just want to disable an inherited Selectable trait, use -Selectable instead.");
                        Console.WriteLine("For special cases like bridge huts, which need bounds to be targetable by C4 and engineers,");
                        Console.WriteLine("give them the CustomSelectionSize trait with CustomBounds.");
                        Console.WriteLine("See RA and C&C bridge huts or crates for reference.");
                    }
                }

                if (engineVersion < 20150620)
                {
                    if (depth == 2)
                    {
                        if (node.Key == "DeathSound")
                            node.Key = "Voice";

                        if (node.Key == "KillVoice")
                            node.Key = "Voice";

                        if (node.Key == "BuildVoice")
                            node.Key = "Voice";
                    }
                }

                // WinForms editor was removed
                if (engineVersion < 20150620)
                {
                    if (depth == 0 && node.Value.Nodes.Exists(n => n.Key == "EditorAppearance"))
                        node.Value.Nodes.RemoveAll(n => n.Key == "EditorAppearance");

                    if (depth == 1 && node.Value.Nodes.Exists(n => n.Key == "ResourceType"))
                    {
                        var editorSprite = node.Value.Nodes.FirstOrDefault(n => n.Key == "EditorSprite");
                        if (editorSprite != null)
                            node.Value.Nodes.Remove(editorSprite);
                    }
                }

                // VisibilityType was introduced
                if (engineVersion < 20150704)
                {
                    if (depth == 0 && node.Value.Nodes.Exists(n => n.Key == "Helicopter" || n.Key == "Plane" || n.Key == "Immobile"))
                    {
                        var visibility = node.Value.Nodes.FirstOrDefault(n => n.Key == "HiddenUnderShroud" || n.Key == "HiddenUnderFog");
                        if (visibility != null)
                            visibility.Value.Nodes.Add(new MiniYamlNode("Type", "CenterPosition"));

                        var reveals = node.Value.Nodes.FirstOrDefault(n => n.Key == "RevealsShroud");
                        if (reveals != null)
                            reveals.Value.Nodes.Add(new MiniYamlNode("Type", "CenterPosition"));
                    }
                }

                // Removed RenderUnit
                if (engineVersion < 20150704)
                {
                    // Renamed WithHarvestAnimation to WithHarvestOverlay
                    if (node.Key == "WithHarvestAnimation")
                        node.Key = "WithHarvestOverlay";

                    // Replaced RenderLandingCraft with WithFacingSpriteBody + WithLandingCraftAnimation.
                    // Note: These rules are set up to do approximately the right thing for maps, but
                    // mods might need additional manual tweaks. This is the best we can do without having
                    // much smarter rules parsing, because we currently can't reason about inherited traits.
                    if (depth == 0)
                    {
                        var childKeySequence = new[] { "Sequence" };
                        var childKeysExcludeFromRS = new[] { "Sequence", "OpenTerrainTypes", "OpenSequence", "UnloadSequence" };

                        var rlc = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("RenderLandingCraft"));
                        if (rlc != null)
                        {
                            rlc.Key = "WithLandingCraftAnimation";

                            var rsNodes = rlc.Value.Nodes.Where(n => !childKeysExcludeFromRS.Contains(n.Key)).ToList();
                            var wfsbNodes = rlc.Value.Nodes.Where(n => childKeySequence.Contains(n.Key)).ToList();

                            if (rsNodes.Any())
                                node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", new MiniYaml("", rsNodes)));
                            else
                                node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", ""));

                            // Note: For the RA landing craft WithSpriteBody would be sufficient since it has no facings,
                            // but WithFacingSpriteBody works as well and covers the potential case where a third-party mod
                            // might have given their landing craft multiple facings.
                            if (wfsbNodes.Any())
                                node.Value.Nodes.Add(new MiniYamlNode("WithFacingSpriteBody", new MiniYaml("", wfsbNodes)));
                            else
                                node.Value.Nodes.Add(new MiniYamlNode("WithFacingSpriteBody", ""));

                            node.Value.Nodes.Add(new MiniYamlNode("AutoSelectionSize", ""));

                            rlc.Value.Nodes.RemoveAll(n => rsNodes.Contains(n));
                            rlc.Value.Nodes.RemoveAll(n => wfsbNodes.Contains(n));
                        }

                        var rrlc = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("-RenderLandingCraft"));
                        if (rrlc != null)
                            rrlc.Key = "-WithLandingCraftAnimation";
                    }

                    // Replaced RenderHarvester with WithFacingSpriteBody + WithHarvestAnimation + WithDockingAnimation.
                    // Note: These rules are set up to do approximately the right thing for maps, but
                    // mods might need additional manual tweaks. This is the best we can do without having
                    // much smarter rules parsing, because we currently can't reason about inherited traits.
                    if (depth == 0)
                    {
                        var childKeySequence = new[] { "Sequence" };
                        var childKeyIBF = new[] { "ImagesByFullness" };
                        var childKeysExcludeFromRS = new[] { "Sequence", "ImagesByFullness", "HarvestSequence" };

                        var rh = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("RenderHarvester"));
                        if (rh != null)
                        {
                            rh.Key = "WithHarvestAnimation";

                            var rsNodes = rh.Value.Nodes.Where(n => !childKeysExcludeFromRS.Contains(n.Key)).ToList();
                            var wfsbNodes = rh.Value.Nodes.Where(n => childKeySequence.Contains(n.Key)).ToList();
                            var ibfNode = rh.Value.Nodes.Where(n => childKeyIBF.Contains(n.Key)).ToList();

                            if (rsNodes.Any())
                                node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", new MiniYaml("", rsNodes)));
                            else
                                node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", ""));

                            if (wfsbNodes.Any())
                                node.Value.Nodes.Add(new MiniYamlNode("WithFacingSpriteBody", new MiniYaml("", wfsbNodes)));
                            else
                                node.Value.Nodes.Add(new MiniYamlNode("WithFacingSpriteBody", ""));

                            node.Value.Nodes.Add(new MiniYamlNode("AutoSelectionSize", ""));
                            node.Value.Nodes.Add(new MiniYamlNode("WithDockingAnimation", ""));

                            rh.Value.Nodes.RemoveAll(n => rsNodes.Contains(n));
                            rh.Value.Nodes.RemoveAll(n => wfsbNodes.Contains(n));

                            if (ibfNode.Any())
                            {
                                rh.Value.Nodes.RemoveAll(n => ibfNode.Contains(n));
                                Console.WriteLine("The 'ImagesByFullness' property from the removed RenderHarvester trait has been");
                                Console.WriteLine("replaced with a 'PrefixByFullness' property on the new WithHarvestAnimation trait.");
                                Console.WriteLine("This cannot be reliably upgraded, as the actor sequences need to be adapted as well.");
                                Console.WriteLine("Therefore, WithHarvestAnimation will use the default (no prefix) after upgrading.");
                                Console.WriteLine("See RA's harvester for reference on how to re-implement this feature using the new trait.");
                            }
                        }

                        var rrh = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("-RenderHarvester"));
                        if (rrh != null)
                            rrh.Key = "-WithHarvestAnimation";
                    }

                    // Replace RenderUnit with RenderSprites + WithFacingSpriteBody + AutoSelectionSize.
                    // Normally this should have been removed by previous upgrade rules, but let's run this again
                    // to make sure to get rid of potential left-over cases like D2k sandworms and harvesters.
                    if (depth == 0)
                    {
                        var childKeys = new[] { "Sequence" };

                        var ru = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("RenderUnit"));
                        if (ru != null)
                        {
                            ru.Key = "WithFacingSpriteBody";

                            var rsNodes = ru.Value.Nodes.Where(n => !childKeys.Contains(n.Key)).ToList();

                            if (rsNodes.Any())
                                node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", new MiniYaml("", rsNodes)));
                            else
                                node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", ""));

                            node.Value.Nodes.Add(new MiniYamlNode("AutoSelectionSize", ""));

                            ru.Value.Nodes.RemoveAll(n => rsNodes.Contains(n));

                            Console.WriteLine("RenderUnit has now been removed from code.");
                            Console.WriteLine("Use RenderSprites + WithFacingSpriteBody (+ AutoSelectionSize, if necessary) instead.");
                        }

                        var rru = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("-RenderUnit"));
                        if (rru != null)
                            rru.Key = "-WithFacingSpriteBody";
                    }
                }

                // Generalized the flash palette trait
                if (engineVersion < 20150627)
                {
                    if (node.Key == "NukePaletteEffect")
                        node.Key = "FlashPaletteEffect";
                }

                // InitialActivity on FreeActor and Buildable was removed (due to being too hacky)
                if (engineVersion < 20150707)
                {
                    if (depth == 1)
                        node.Value.Nodes.RemoveAll(n => n.Key == "InitialActivity");
                }

                // Make default upgrades explicit for GainsExperience
                if (engineVersion < 20150709)
                {
                    if (depth == 1 && (node.Key == "GainsExperience" || node.Key.StartsWith("GainsExperience@"))
                            && node.Value.Nodes.FirstOrDefault(n => n.Key == "Upgrades") == null)
                        node.Value.Nodes.Add(new MiniYamlNode("Upgrades", new MiniYaml("", new List<MiniYamlNode> {
                            new MiniYamlNode("200", "firepower, damage, speed, reload, inaccuracy, rank"),
                            new MiniYamlNode("400", "firepower, damage, speed, reload, inaccuracy, rank"),
                            new MiniYamlNode("800", "firepower, damage, speed, reload, inaccuracy, rank"),
                            new MiniYamlNode("1600", "firepower, damage, speed, reload, inaccuracy, rank, eliteweapon, selfheal")
                        })));
                }

                if (engineVersion < 20150711)
                {
                    if (depth == 0)
                    {
                        var emptyYaml = new MiniYaml(null);

                        // Replace -GainsStatUpgrades
                        var trait = node.Value.Nodes.FirstOrDefault(n => n.Key == "-GainsStatUpgrades");
                        if (trait != null)
                        {
                            node.Value.Nodes.Add(new MiniYamlNode("-FirepowerMultiplier@EXPERIENCE", emptyYaml));
                            node.Value.Nodes.Add(new MiniYamlNode("-DamageMultiplier@EXPERIENCE", emptyYaml));
                            node.Value.Nodes.Add(new MiniYamlNode("-SpeedMultiplier@EXPERIENCE", emptyYaml));
                            node.Value.Nodes.Add(new MiniYamlNode("-ReloadDelayMultiplier@EXPERIENCE", emptyYaml));
                            node.Value.Nodes.Add(new MiniYamlNode("-InaccuracyMultiplier@EXPERIENCE", emptyYaml));
                            node.Value.Nodes.Remove(trait);
                        }

                        // Replace GainsStatUpgrades
                        trait = node.Value.Nodes.FirstOrDefault(n => n.Key == "GainsStatUpgrades");
                        if (trait != null)
                        {
                            // Common code for making each trait
                            Action<string, string, string> addTrait = (type, newType, values) =>
                                {
                                    var upgradeTypes = trait.Value.Nodes.FirstOrDefault(n => n.Key == type + "Upgrade");
                                    var modifier = trait.Value.Nodes.FirstOrDefault(n => n.Key == type + "Modifier");

                                    if (upgradeTypes == null || !string.IsNullOrEmpty(upgradeTypes.Value.Value) || modifier == null ||
                                        !string.IsNullOrEmpty(modifier.Value.Value))
                                    {
                                        var yaml = new MiniYaml(null);
                                        if (modifier == null)
                                            modifier = new MiniYamlNode("Modifier", new MiniYaml(values));
                                        else
                                            modifier.Key = "Modifier";
                                        yaml.Nodes.Add(modifier);

                                        if (upgradeTypes == null)
                                            upgradeTypes = new MiniYamlNode("UpgradeTypes", new MiniYaml(type.ToLowerInvariant()));
                                        else
                                            upgradeTypes.Key = "UpgradeTypes";
                                        yaml.Nodes.Add(upgradeTypes);

                                        node.Value.Nodes.Add(new MiniYamlNode((newType ?? type) + "Multiplier@EXPERIENCE", yaml));
                                    }
                                };

                            // Execute common code for each trait
                            addTrait("Firepower", null, "110, 115, 120, 130");
                            addTrait("Damage", null, "91, 87, 83, 65");
                            addTrait("Speed", null, "110, 115, 120, 150");
                            addTrait("Reload", "ReloadDelay", "95, 90, 85, 75");
                            addTrait("Inaccuracy", null, "90, 80, 70, 50");

                            // Remove GainsStatUpgrades
                            node.Value.Nodes.Remove(trait);
                        }

                        // Replace -InvulnerabilityUpgrade
                        trait = node.Value.Nodes.FirstOrDefault(n => n.Key == "-InvulnerabilityUpgrade");
                        if (trait != null)
                            trait.Key = "-DamageMultiplier@INVULNERABILITY_UPGRADE";

                        // Replace InvulnerabilityUpgrade with DamageMultiplier@INVULNERABILITY_UPGRADE
                        trait = node.Value.Nodes.FirstOrDefault(n => n.Key == "InvulnerabilityUpgrade");
                        if (trait != null)
                        {
                            trait.Key = "DamageMultiplier@INVULNERABILITY_UPGRADE";
                            trait.Value.Nodes.Add(new MiniYamlNode("Modifier", "0"));

                            // Use UpgradeMinEnabledLevel as BaseLevel; otherwise, 1
                            var min = trait.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeMinEnabledLevel");
                            if (min != null)
                            {
                                if (min.Value.Value != "1")
                                    min.Key = "BaseLevel";
                                else
                                    trait.Value.Nodes.Remove(min);
                            }

                            // Remove since level cap is based of Modifier.Length + BaseLevel
                            trait.Value.Nodes.RemoveAll(n => n.Key == "UpgradeMaxAcceptedLevel");
                            trait.Value.Nodes.RemoveAll(n => n.Key == "UpgradeMaxEnabledLevel");
                        }

                        // Replace -InvulnerabilityUpgrade@* with -DamageMultiplier@*
                        foreach (var n in node.Value.Nodes.Where(n => n.Key.StartsWith("-InvulnerabilityUpgrade@")))
                            n.Key = "-DamageMultiplier@" + n.Key.Substring("-InvulnerabilityUpgrade@".Length);

                        // Replace InvulnerabilityUpgrade@* with DamageMultiplier@*
                        foreach (var t in node.Value.Nodes.Where(n => n.Key.StartsWith("InvulnerabilityUpgrade@")))
                        {
                            t.Key = "DamageMultiplier@" + t.Key.Substring("InvulnerabilityUpgrade@".Length);
                            t.Value.Nodes.Add(new MiniYamlNode("Modifier", "0"));

                            // Use UpgradeMinEnabledLevel as BaseLevel; otherwise, 1
                            var min = t.Value.Nodes.FirstOrDefault(n => n.Key == "UpgradeMinEnabledLevel");
                            if (min != null)
                            {
                                if (min.Value.Value != "1")
                                    min.Key = "BaseLevel";
                                else
                                    t.Value.Nodes.Remove(min);
                            }

                            // Remove since level cap is based of Modifier.Length + BaseLevel
                            t.Value.Nodes.RemoveAll(n => n.Key == "UpgradeMaxAcceptedLevel");
                            t.Value.Nodes.RemoveAll(n => n.Key == "UpgradeMaxEnabledLevel");
                        }

                        // Replace -Invulnerable with -DamageMultiplier@INVULNERABLE
                        trait = node.Value.Nodes.FirstOrDefault(n => n.Key == "-Invulnerable");
                        if (trait != null)
                            trait.Key = "-DamageMultiplier@INVULNERABLE";

                        // Invulnerable with DamageMultiplier@INVULNERABLE
                        trait = node.Value.Nodes.FirstOrDefault(n => n.Key == "Invulnerable");
                        if (trait != null)
                        {
                            trait.Key = "DamageMultiplier@INVULNERABLE";
                            trait.Value.Nodes.Add(new MiniYamlNode("Modifier", "0"));
                        }
                    }
                }

                // Rename the `Country` trait to `Faction`
                if (engineVersion < 20150714)
                {
                    var split = node.Key.Split('@');
                    if (split.Any() && split[0] == "Country")
                    {
                        node.Key = node.Key.Replace("Country", "Faction");

                        var race = node.Value.Nodes.FirstOrDefault(x => x.Key == "Race");
                        if (race != null)
                            race.Key = "InternalName";

                        var randomRace = node.Value.Nodes.FirstOrDefault(x => x.Key == "RandomRaceMembers");
                        if (randomRace != null)
                            randomRace.Key = "RandomFactionMembers";
                    }
                }

                if (engineVersion < 20150714)
                {
                    // Move certain properties from Parachutable to new WithParachute trait
                    // Add dependency traits to actors implementing Parachutable
                    // Make otherwise targetable parachuting actors untargetable
                    var par = node.Value.Nodes.FirstOrDefault(n => n.Key == "Parachutable");
                    if (par != null)
                    {
                        var withParachute = new MiniYamlNode("WithParachute", null, new List<MiniYamlNode>
                        {
                            new MiniYamlNode("UpgradeTypes", "parachute"),
                            new MiniYamlNode("UpgradeMinEnabledLevel", "1")
                        });

                        var copyProp = new Action<string, string, string>((srcName, dstName, defValue) =>
                        {
                            var prop = par.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith(srcName));
                            if (prop != null && prop.Value.Value != defValue)
                                withParachute.Value.Nodes.Add(new MiniYamlNode(dstName, prop.Value.Value));
                        });

                        var moveProp = new Action<string, string, string>((srcName, dstName, defValue) =>
                        {
                            copyProp(srcName, dstName, defValue);
                            par.Value.Nodes.RemoveAll(n => n.Key.StartsWith(srcName));
                        });

                        if (par.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("ShadowSequence")) != null)
                        {
                            moveProp("ShadowSequence", "ShadowImage", null);
                            copyProp("ParachuteIdleSequence", "ShadowSequence", null);
                        }

                        moveProp("ParachuteSequence", "Image", null);
                        moveProp("ParachuteIdleSequence", "Sequence", null);

                        moveProp("ParachuteOpenSequence", "OpeningSequence", null);

                        moveProp("ParachutePalette", "Palette", "player");
                        moveProp("ShadowPalette", "ShadowPalette", "player");

                        moveProp("ParachuteOffset", "Offset", "player");

                        par.Value.Nodes.RemoveAll(n => n.Key.StartsWith("ParachuteShadowPalette"));

                        node.Value.Nodes.Add(withParachute);

                        var otherNodes = nodes;
                        var inherits = new Func<string, bool>(traitName => node.Value.Nodes.Where(n => n.Key.StartsWith("Inherits"))
                                .Any(inh => otherNodes.First(n => n.Key.StartsWith(inh.Value.Value)).Value.Nodes.Any(n => n.Key.StartsWith(traitName))));

                        // For actors that have or inherit a TargetableUnit, disable the trait while parachuting
                        var tu = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("TargetableUnit"));
                        if (tu != null)
                        {
                            tu.Value.Nodes.Add(new MiniYamlNode("UpgradeTypes", "parachute"));
                            tu.Value.Nodes.Add(new MiniYamlNode("UpgradeMaxEnabledLevel", "0"));
                        }
                        else
                        {
                            if (inherits("TargetableUnit"))
                            {
                                node.Value.Nodes.Add(new MiniYamlNode("TargetableUnit", null, new List<MiniYamlNode>
                                {
                                    new MiniYamlNode("UpgradeTypes", "parachute"),
                                    new MiniYamlNode("UpgradeMaxEnabledLevel", "0")
                                }));
                                break;
                            }
                        }

                        var has = new Func<string, bool>(traitName => node.Value.Nodes.Any(n => n.Key.StartsWith(traitName)));

                        // If actor does not have nor inherits an UpgradeManager, add one
                        if (!has("UpgradeManager") && !inherits("UpgradeManager"))
                                node.Value.Nodes.Add(new MiniYamlNode("UpgradeManager", ""));

                        // If actor does not have nor inherits a BodyOrientation, add one
                        if (!has("BodyOrientation") && !inherits("BodyOrientation"))
                            node.Value.Nodes.Add(new MiniYamlNode("BodyOrientation", ""));
                    }
                }

                if (engineVersion < 20150715)
                {
                    // Replaced RenderGunboat with RenderSprites + WithGunboatBody.
                    if (depth == 0)
                    {
                        var childKeysGunboat = new[] { "Turret", "LeftSequence", "RightSequence", "WakeLeftSequence", "WakeRightSequence" };

                        var rgb = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("RenderGunboat"));
                        if (rgb != null)
                        {
                            rgb.Key = "WithGunboatBody";

                            var rsNodes = rgb.Value.Nodes.Where(n => !childKeysGunboat.Contains(n.Key)).ToList();
                            if (rsNodes.Any())
                                node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", new MiniYaml("", rsNodes)));
                            else
                                node.Value.Nodes.Add(new MiniYamlNode("RenderSprites", ""));

                            node.Value.Nodes.Add(new MiniYamlNode("AutoSelectionSize", ""));

                            rgb.Value.Nodes.RemoveAll(n => rsNodes.Contains(n));
                            rgb.Value.Nodes.Add(new MiniYamlNode("Sequence", "left"));
                        }

                        var rrgb = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("-RenderGunboat"));
                        if (rrgb != null)
                            rrgb.Key = "-WithGunboatBody";
                    }
                }

                if (engineVersion < 20150720)
                {
                    // Rename RenderEditorOnly to RenderSpritesEditorOnly
                    if (depth == 0)
                    {
                        var reo = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("RenderEditorOnly"));
                        if (reo != null)
                        {
                            reo.Key = "RenderSpritesEditorOnly";

                            var wsbNodes = reo.Value.Nodes.Where(n => n.Key == "Sequence").ToList();

                            if (wsbNodes.Any())
                                node.Value.Nodes.Add(new MiniYamlNode("WithSpriteBody", new MiniYaml("", wsbNodes)));
                            else
                                node.Value.Nodes.Add(new MiniYamlNode("WithSpriteBody", ""));

                            reo.Value.Nodes.RemoveAll(n => wsbNodes.Contains(n));
                        }

                        var rreo = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("-RenderEditorOnly"));
                        if (rreo != null)
                            rreo.Key = "-RenderSpritesEditorOnly";
                    }
                }

                if (engineVersion < 20150731)
                {
                    if (node.Key.StartsWith("ProvidesPrerequisite"))
                    {
                        var raceNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "Race");
                        if (raceNode != null)
                            raceNode.Key = "Factions";
                    }

                    if (node.Key.StartsWith("Buildable"))
                    {
                        var raceNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "ForceRace");
                        if (raceNode != null)
                            raceNode.Key = "ForceFaction";
                    }
                }

                // WithBuildingExplosion received support for sequence randomization
                if (engineVersion < 20150803)
                {
                    if (depth == 2 && parentKey == "WithBuildingExplosion" && node.Key == "Sequence")
                        node.Key = "Sequences";
                }

                UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
            }
        }
Exemple #55
0
 static void RenameNodeKey(MiniYamlNode node, string key)
 {
     var parts = node.Key.Split('@');
     node.Key = key;
     if (parts.Length > 1)
         node.Key += "@" + parts[1];
 }
Exemple #56
0
        internal static void UpgradeWeaponRules(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            var parentKey = parent != null ? parent.Key.Split('@').First() : null;

            foreach (var node in nodes)
            {
                // Weapon definitions were converted to world coordinates
                if (engineVersion < 20131226)
                {
                    if (depth == 1)
                    {
                        switch (node.Key)
                        {
                            case "Range":
                            case "MinRange":
                                ConvertFloatToRange(ref node.Value.Value);
                                break;
                            default:
                                break;
                        }
                    }

                    if (depth == 2 && parentKey == "Projectile")
                    {
                        switch (node.Key)
                        {
                            case "Inaccuracy":
                                ConvertPxToRange(ref node.Value.Value);
                                break;
                            case "Angle":
                                ConvertAngle(ref node.Value.Value);
                                break;
                            case "Speed":
                            {
                                if (parent.Value.Value == "Missile")
                                    ConvertPxToRange(ref node.Value.Value, 1, 5);
                                if (parent.Value.Value == "Bullet")
                                    ConvertPxToRange(ref node.Value.Value, 2, 5);
                                break;
                            }
                            default:
                                break;
                        }
                    }

                    if (depth == 2 && parentKey == "Warhead")
                    {
                        switch (node.Key)
                        {
                            case "Spread":
                                ConvertPxToRange(ref node.Value.Value);
                                break;
                            default:
                                break;
                        }
                    }
                }

                if (engineVersion < 20140615)
                {
                    if (depth == 2 && parentKey == "Warhead" && node.Key == "Ore")
                        node.Key = "DestroyResources";
                }

                if (engineVersion < 20140720)
                {
                    // Split out the warheads to individual warhead types.
                    if (depth == 0)
                    {
                        var validTargets = node.Value.Nodes.FirstOrDefault(n => n.Key == "ValidTargets"); // Weapon's ValidTargets need to be copied to the warheads, so find it
                        var invalidTargets = node.Value.Nodes.FirstOrDefault(n => n.Key == "InvalidTargets"); // Weapon's InvalidTargets need to be copied to the warheads, so find it

                        var warheadCounter = 0;
                        foreach(var curNode in node.Value.Nodes.ToArray())
                        {
                            if (curNode.Key.Contains("Warhead") && curNode.Value.Value == null)
                            {
                                var newNodes = new List<MiniYamlNode>();
                                var oldNodeAtName = "";
                                if (curNode.Key.Contains('@'))
                                    oldNodeAtName = "_" + curNode.Key.Split('@')[1];

                                // Per Cell Damage Model
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("DamageModel") &&
                                        n.Value.Value.Contains("PerCell")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Size"); // New PerCell warhead allows 2 sizes, as opposed to 1 size
                                    if (temp != null)
                                    {
                                        var newValue = temp.Value.Value.Split(',').First();
                                        newYaml.Add(new MiniYamlNode("Size", newValue));
                                    }

                                    var keywords = new List<string> { "Damage", "InfDeath", "PreventProne", "ProneModifier", "Delay" };

                                    foreach(var keyword in keywords)
                                    {
                                        var temp2 = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp2 != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp2.Value.Value));
                                    }

                                    if (validTargets != null)
                                        newYaml.Add(validTargets);
                                    if (invalidTargets != null)
                                        newYaml.Add(invalidTargets);

                                    var tempVersus = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Versus");
                                    if (tempVersus != null)
                                        newYaml.Add(new MiniYamlNode("Versus", tempVersus.Value));

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Dam" + oldNodeAtName, "PerCellDamage", newYaml));
                                }

                                // HealthPercentage damage model
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("DamageModel") &&
                                        n.Value.Value.Contains("HealthPercentage")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Size"); // New HealthPercentage warhead allows 2 spreads, as opposed to 1 size
                                    if (temp != null)
                                    {
                                        var newValue = temp.Value.Value.Split(',').First() + "c0";
                                        newYaml.Add(new MiniYamlNode("Spread", newValue));
                                    }

                                    var keywords = new List<string> { "Damage", "InfDeath", "PreventProne", "ProneModifier", "Delay" };

                                    foreach(var keyword in keywords)
                                    {
                                        var temp2 = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp2 != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp2.Value.Value));
                                    }

                                    if (validTargets != null)
                                        newYaml.Add(validTargets);
                                    if (invalidTargets != null)
                                        newYaml.Add(invalidTargets);

                                    var tempVersus = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Versus");
                                    if (tempVersus != null)
                                        newYaml.Add(new MiniYamlNode("Versus", tempVersus.Value));

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Dam" + oldNodeAtName, "HealthPercentageDamage", newYaml));
                                }

                                // SpreadDamage
                                { // Always occurs, since by definition all warheads were SpreadDamage warheads before
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var keywords = new List<string> { "Spread", "Damage", "InfDeath", "PreventProne", "ProneModifier", "Delay" };

                                    foreach(var keyword in keywords)
                                    {
                                        var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
                                    }

                                    if (validTargets != null)
                                        newYaml.Add(validTargets);
                                    if (invalidTargets != null)
                                        newYaml.Add(invalidTargets);

                                    var tempVersus = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Versus");
                                    if (tempVersus != null)
                                        newYaml.Add(new MiniYamlNode("Versus", tempVersus.Value));

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Dam" + oldNodeAtName, "SpreadDamage", newYaml));
                                }

                                // DestroyResource
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("DestroyResources") ||
                                        n.Key.Contains("Ore")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var keywords = new List<string> { "Size", "Delay", "ValidTargets", "InvalidTargets" };
                                    foreach(var keyword in keywords)
                                    {
                                        var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
                                    }

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Res" + oldNodeAtName, "DestroyResource", newYaml));
                                }

                                // CreateResource
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("AddsResourceType")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var keywords = new List<string> { "AddsResourceType", "Size", "Delay", "ValidTargets", "InvalidTargets" };

                                    foreach(var keyword in keywords)
                                    {
                                        var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
                                    }

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Res" + oldNodeAtName, "CreateResource", newYaml));
                                }

                                // LeaveSmudge
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("SmudgeType")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var keywords = new List<string> { "SmudgeType", "Size", "Delay", "ValidTargets", "InvalidTargets" };

                                    foreach(var keyword in keywords)
                                    {
                                        var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
                                    }

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Smu" + oldNodeAtName, "LeaveSmudge", newYaml));
                                }

                                // CreateEffect - Explosion
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("Explosion") ||
                                        n.Key.Contains("ImpactSound")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var keywords = new List<string> { "Explosion", "ImpactSound", "Delay", "ValidTargets", "InvalidTargets", "ValidImpactTypes", "InvalidImpactTypes" };

                                    foreach(var keyword in keywords)
                                    {
                                        var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
                                    }
                                    newYaml.Add(new MiniYamlNode("InvalidImpactTypes", "Water"));

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Eff" + oldNodeAtName, "CreateEffect", newYaml));
                                }

                                // CreateEffect - Water Explosion
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("WaterExplosion") ||
                                        n.Key.Contains("WaterImpactSound")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var keywords = new List<string> { "WaterExplosion", "WaterImpactSound", "Delay", "ValidTargets", "InvalidTargets", "ValidImpactTypes", "InvalidImpactTypes" };

                                    foreach(var keyword in keywords)
                                    {
                                        var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp != null)
                                        {
                                            if (temp.Key == "WaterExplosion")
                                                temp.Key = "Explosion";
                                            if (temp.Key == "WaterImpactSound")
                                                temp.Key = "ImpactSound";
                                            newYaml.Add(new MiniYamlNode(temp.Key, temp.Value.Value));
                                        }
                                    }
                                    newYaml.Add(new MiniYamlNode("ValidImpactTypes", "Water"));

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Eff" + oldNodeAtName, "CreateEffect", newYaml));
                                }
                                node.Value.Nodes.InsertRange(node.Value.Nodes.IndexOf(curNode),newNodes);
                                node.Value.Nodes.Remove(curNode);
                            }
                        }
                    }
                }

                if (engineVersion < 20140818)
                {
                    if (depth == 1)
                    {
                        if (node.Key == "ROF")
                            node.Key = "ReloadDelay";
                    }
                }

                if (engineVersion < 20140821)
                {
                    // Converted versus definitions to integers
                    if (depth == 3 && parentKey == "Versus")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);
                }

                if (engineVersion < 20140830)
                {
                    if (depth == 2)
                    {
                        if (node.Key == "InfDeath")
                            node.Key = "DeathType";
                    }
                }

                UpgradeWeaponRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
            }
        }
Exemple #57
0
        internal static void UpgradeWeaponRules(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            var parentKey = parent != null ? parent.Key.Split('@').First() : null;

            foreach (var node in nodes)
            {
                // Weapon definitions were converted to world coordinates
                if (engineVersion < 20131226)
                {
                    if (depth == 1)
                    {
                        switch (node.Key)
                        {
                            case "Range":
                            case "MinRange":
                                ConvertFloatToRange(ref node.Value.Value);
                                break;
                            default:
                                break;
                        }
                    }

                    if (depth == 2 && parentKey == "Projectile")
                    {
                        switch (node.Key)
                        {
                            case "Inaccuracy":
                                ConvertPxToRange(ref node.Value.Value);
                                break;
                            case "Angle":
                                ConvertAngle(ref node.Value.Value);
                                break;
                            case "Speed":
                                {
                                    if (parent.Value.Value == "Missile")
                                        ConvertPxToRange(ref node.Value.Value, 1, 5);
                                    if (parent.Value.Value == "Bullet")
                                        ConvertPxToRange(ref node.Value.Value, 2, 5);
                                    break;
                                }

                            default:
                                break;
                        }
                    }

                    if (depth == 2 && parentKey == "Warhead")
                    {
                        switch (node.Key)
                        {
                            case "Spread":
                                ConvertPxToRange(ref node.Value.Value);
                                break;
                            default:
                                break;
                        }
                    }
                }

                if (engineVersion < 20140615)
                {
                    if (depth == 2 && parentKey == "Warhead" && node.Key == "Ore")
                        node.Key = "DestroyResources";
                }

                if (engineVersion < 20140720)
                {
                    // Split out the warheads to individual warhead types.
                    if (depth == 0)
                    {
                        // Weapon's ValidTargets need to be copied to the warheads, so find it
                        var validTargets = node.Value.Nodes.FirstOrDefault(n => n.Key == "ValidTargets");

                        // Weapon's InvalidTargets need to be copied to the warheads, so find it
                        var invalidTargets = node.Value.Nodes.FirstOrDefault(n => n.Key == "InvalidTargets");

                        var warheadCounter = 0;
                        foreach (var curNode in node.Value.Nodes.ToArray())
                        {
                            if (curNode.Key.Contains("Warhead") && curNode.Value.Value == null)
                            {
                                var newNodes = new List<MiniYamlNode>();
                                var oldNodeAtName = "";
                                if (curNode.Key.Contains('@'))
                                    oldNodeAtName = "_" + curNode.Key.Split('@')[1];

                                // Per Cell Damage Model
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("DamageModel") &&
                                        n.Value.Value.Contains("PerCell")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Size"); // New PerCell warhead allows 2 sizes, as opposed to 1 size
                                    if (temp != null)
                                    {
                                        var newValue = temp.Value.Value.Split(',').First();
                                        newYaml.Add(new MiniYamlNode("Size", newValue));
                                    }

                                    var keywords = new List<string> { "Damage", "InfDeath", "PreventProne", "ProneModifier", "Delay" };

                                    foreach (var keyword in keywords)
                                    {
                                        var temp2 = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp2 != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp2.Value.Value));
                                    }

                                    if (validTargets != null)
                                        newYaml.Add(validTargets);
                                    if (invalidTargets != null)
                                        newYaml.Add(invalidTargets);

                                    var tempVersus = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Versus");
                                    if (tempVersus != null)
                                        newYaml.Add(new MiniYamlNode("Versus", tempVersus.Value));

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Dam" + oldNodeAtName, "PerCellDamage", newYaml));
                                }

                                // HealthPercentage damage model
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("DamageModel") &&
                                        n.Value.Value.Contains("HealthPercentage")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Size"); // New HealthPercentage warhead allows 2 spreads, as opposed to 1 size
                                    if (temp != null)
                                    {
                                        var newValue = temp.Value.Value.Split(',').First() + "c0";
                                        newYaml.Add(new MiniYamlNode("Spread", newValue));
                                    }

                                    var keywords = new List<string> { "Damage", "InfDeath", "PreventProne", "ProneModifier", "Delay" };

                                    foreach (var keyword in keywords)
                                    {
                                        var temp2 = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp2 != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp2.Value.Value));
                                    }

                                    if (validTargets != null)
                                        newYaml.Add(validTargets);
                                    if (invalidTargets != null)
                                        newYaml.Add(invalidTargets);

                                    var tempVersus = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Versus");
                                    if (tempVersus != null)
                                        newYaml.Add(new MiniYamlNode("Versus", tempVersus.Value));

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Dam" + oldNodeAtName, "HealthPercentageDamage", newYaml));
                                }

                                // SpreadDamage
                                { // Always occurs, since by definition all warheads were SpreadDamage warheads before
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var keywords = new List<string> { "Spread", "Damage", "InfDeath", "PreventProne", "ProneModifier", "Delay" };

                                    foreach (var keyword in keywords)
                                    {
                                        var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
                                    }

                                    if (validTargets != null)
                                        newYaml.Add(validTargets);
                                    if (invalidTargets != null)
                                        newYaml.Add(invalidTargets);

                                    var tempVersus = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Versus");
                                    if (tempVersus != null)
                                        newYaml.Add(new MiniYamlNode("Versus", tempVersus.Value));

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Dam" + oldNodeAtName, "SpreadDamage", newYaml));
                                }

                                // DestroyResource
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("DestroyResources") ||
                                        n.Key.Contains("Ore")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var keywords = new List<string> { "Size", "Delay", "ValidTargets", "InvalidTargets" };
                                    foreach (var keyword in keywords)
                                    {
                                        var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
                                    }

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Res" + oldNodeAtName, "DestroyResource", newYaml));
                                }

                                // CreateResource
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("AddsResourceType")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var keywords = new List<string> { "AddsResourceType", "Size", "Delay", "ValidTargets", "InvalidTargets" };

                                    foreach (var keyword in keywords)
                                    {
                                        var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
                                    }

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Res" + oldNodeAtName, "CreateResource", newYaml));
                                }

                                // LeaveSmudge
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("SmudgeType")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var keywords = new List<string> { "SmudgeType", "Size", "Delay", "ValidTargets", "InvalidTargets" };

                                    foreach (var keyword in keywords)
                                    {
                                        var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
                                    }

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Smu" + oldNodeAtName, "LeaveSmudge", newYaml));
                                }

                                // CreateEffect - Explosion
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("Explosion") ||
                                        n.Key.Contains("ImpactSound")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var keywords = new List<string> { "Explosion", "ImpactSound", "Delay",
                                        "ValidTargets", "InvalidTargets", "ValidImpactTypes", "InvalidImpactTypes" };

                                    foreach (var keyword in keywords)
                                    {
                                        var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp != null)
                                            newYaml.Add(new MiniYamlNode(keyword, temp.Value.Value));
                                    }

                                    newYaml.Add(new MiniYamlNode("InvalidImpactTypes", "Water"));
                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Eff" + oldNodeAtName, "CreateEffect", newYaml));
                                }

                                // CreateEffect - Water Explosion
                                if (curNode.Value.Nodes.Where(n => n.Key.Contains("WaterExplosion") ||
                                        n.Key.Contains("WaterImpactSound")).Any())
                                {
                                    warheadCounter++;

                                    var newYaml = new List<MiniYamlNode>();

                                    var keywords = new List<string> { "WaterExplosion", "WaterImpactSound", "Delay",
                                        "ValidTargets", "InvalidTargets", "ValidImpactTypes", "InvalidImpactTypes" };

                                    foreach (var keyword in keywords)
                                    {
                                        var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == keyword);
                                        if (temp != null)
                                        {
                                            if (temp.Key == "WaterExplosion")
                                                temp.Key = "Explosion";
                                            if (temp.Key == "WaterImpactSound")
                                                temp.Key = "ImpactSound";
                                            newYaml.Add(new MiniYamlNode(temp.Key, temp.Value.Value));
                                        }
                                    }

                                    newYaml.Add(new MiniYamlNode("ValidImpactTypes", "Water"));

                                    newNodes.Add(new MiniYamlNode("Warhead@" + warheadCounter.ToString() + "Eff" + oldNodeAtName, "CreateEffect", newYaml));
                                }

                                node.Value.Nodes.InsertRange(node.Value.Nodes.IndexOf(curNode), newNodes);
                                node.Value.Nodes.Remove(curNode);
                            }
                        }
                    }
                }

                if (engineVersion < 20140818)
                {
                    if (depth == 1)
                    {
                        if (node.Key == "ROF")
                            node.Key = "ReloadDelay";
                    }
                }

                if (engineVersion < 20140821)
                {
                    // Converted versus definitions to integers
                    if (depth == 3 && parentKey == "Versus")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);
                }

                if (engineVersion < 20140830)
                {
                    if (depth == 2)
                    {
                        if (node.Key == "InfDeath")
                            node.Key = "DeathType";
                    }
                }

                // Remove PerCellDamageWarhead
                if (engineVersion < 20150213)
                {
                    if (depth == 1 && node.Value.Nodes.Exists(n => n.Key == "PerCellDamage"))
                    {
                        node.Value.Nodes.RemoveAll(n => n.Key == "PerCellDamage");
                        Console.WriteLine("The 'PerCellDamage' warhead has been removed.");
                        Console.WriteLine("Please use the 'SpreadDamage' warhead instead.");
                    }
                }

                if (engineVersion < 20150326)
                {
                    // Remove TurboBoost from missiles
                    if (depth == 1 && node.Key == "Projectile" && node.Value.Nodes.Exists(n => n.Key == "TurboBoost"))
                    {
                        node.Value.Nodes.RemoveAll(n => n.Key == "TurboBoost");
                        Console.WriteLine("'TurboBoost' has been removed.");
                        Console.WriteLine("If you want to reproduce its behavior, create a duplicate");
                        Console.WriteLine("of the weapon in question, change it to be anti-air only,");
                        Console.WriteLine("increase its speed, make the original weapon anti-ground only,");
                        Console.WriteLine("and add the new weapon as additional armament to the actor.");
                    }

                    // Rename ROT to RateOfTurn
                    if (depth == 2 && node.Key == "ROT")
                        node.Key = "RateOfTurn";

                    // Rename High to Blockable
                    if (depth == 2 && parentKey == "Projectile" && node.Key == "High")
                    {
                        var highField = node.Value.Value != null ? FieldLoader.GetValue<bool>("High", node.Value.Value) : false;
                        var blockable = !highField;

                        node.Value.Value = blockable.ToString().ToLowerInvariant();
                        node.Key = "Blockable";
                    }

                    // Move Palette from weapon to projectiles
                    if (depth == 0)
                    {
                        var weapons = node.Value.Nodes;
                        var palette = weapons.FirstOrDefault(p => p.Key == "Palette");
                        var projectile = weapons.FirstOrDefault(r => r.Key == "Projectile");

                        if (palette != null)
                        {
                            var projectileFields = projectile.Value.Nodes;
                            var paletteName = palette.Value.Value != null ? FieldLoader.GetValue<string>("Palette", palette.Value.Value) : "effect";

                            projectileFields.Add(new MiniYamlNode("Palette", paletteName.ToString()));
                            weapons.Remove(palette);
                        }
                    }
                }

                if (engineVersion < 20150421)
                {
                    if (node.Key.StartsWith("Warhead") && node.Value.Value == "SpreadDamage")
                    {
                        // Add DamageTypes property to DamageWarheads with a default value "Prone50Percent"
                        if (node.Value.Nodes.All(x => x.Key != "DamageTypes"))
                        {
                            var damage = node.Value.Nodes.FirstOrDefault(x => x.Key == "Damage");
                            var damageValue = damage != null ? FieldLoader.GetValue<int>("Damage", damage.Value.Value) : -1;

                            var prone = node.Value.Nodes.FirstOrDefault(x => x.Key == "PreventProne");
                            var preventsProne = prone != null && FieldLoader.GetValue<bool>("PreventProne", prone.Value.Value);

                            var proneModifier = node.Value.Nodes.FirstOrDefault(x => x.Key == "ProneModifier");
                            var modifierValue = proneModifier == null ? "50" : proneModifier.Value.Value;

                            var value = new List<string>();

                            if (damageValue > 0)
                                value.Add("Prone{0}Percent".F(modifierValue));

                            if (!preventsProne)
                                value.Add("TriggerProne");

                            if (value.Any())
                                node.Value.Nodes.Add(new MiniYamlNode("DamageTypes", value.JoinWith(", ")));
                        }

                        // Remove obsolete PreventProne and ProneModifier
                        node.Value.Nodes.RemoveAll(x => x.Key == "PreventProne");
                        node.Value.Nodes.RemoveAll(x => x.Key == "ProneModifier");
                    }
                }

                if (engineVersion < 20150524)
                {
                    // Remove DeathType from DamageWarhead
                    if (node.Key.StartsWith("Warhead") && node.Value.Value == "SpreadDamage")
                    {
                        var deathTypeNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "DeathType");
                        var deathType = deathTypeNode == null ? "1" : FieldLoader.GetValue<string>("DeathType", deathTypeNode.Value.Value);
                        var damageTypes = node.Value.Nodes.FirstOrDefault(x => x.Key == "DamageTypes");
                        if (damageTypes != null)
                            damageTypes.Value.Value += ", DeathType" + deathType;
                        else
                            node.Value.Nodes.Add(new MiniYamlNode("DamageTypes", "DeathType" + deathType));

                        node.Value.Nodes.RemoveAll(x => x.Key == "DeathType");
                    }

                    // Replace "DeathTypeX" damage types with proper words
                    if (node.Key.StartsWith("Warhead") && node.Value.Value == "SpreadDamage")
                    {
                        var damageTypes = node.Value.Nodes.FirstOrDefault(x => x.Key == "DamageTypes");
                        if (damageTypes != null)
                            RenameDamageTypes(damageTypes);
                    }
                }

                if (engineVersion < 20150526)
                {
                    var isNukePower = node.Key == "NukePower";
                    var isIonCannonPower = node.Key == "IonCannonPower";

                    if ((isNukePower || isIonCannonPower) && !node.Value.Nodes.Any(n => n.Key == "Cursor"))
                    {
                        var cursor = isIonCannonPower ? "ioncannon" : "nuke";
                        node.Value.Nodes.Add(new MiniYamlNode("Cursor", cursor));
                    }
                }

                UpgradeWeaponRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
            }
        }
Exemple #58
0
 public MiniYamlNode Serialize()
 {
     var data = new MiniYamlNode("GlobalSettings", FieldSaver.Save(this));
     var options = LobbyOptions.Select(kv => new MiniYamlNode(kv.Key, FieldSaver.Save(kv.Value))).ToList();
     data.Value.Nodes.Add(new MiniYamlNode("Options", new MiniYaml(null, options)));
     return data;
 }
Exemple #59
0
        internal static void UpgradeActorRules(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            var parentKey = parent != null ? parent.Key.Split('@').First() : null;

            foreach (var node in nodes)
            {
                // Weapon definitions were converted to world coordinates
                if (engineVersion < 20131226)
                {
                    if (depth == 2 && parentKey == "Exit" && node.Key == "SpawnOffset")
                        ConvertInt2ToWVec(ref node.Value.Value);

                    if (depth == 2 && (parentKey == "Aircraft" || parentKey == "Helicopter" || parentKey == "Plane"))
                    {
                        if (node.Key == "CruiseAltitude")
                            ConvertPxToRange(ref node.Value.Value);

                        if (node.Key == "Speed")
                            ConvertPxToRange(ref node.Value.Value, 7, 32);
                    }

                    if (depth == 2 && parentKey == "Mobile" && node.Key == "Speed")
                        ConvertPxToRange(ref node.Value.Value, 1, 3);

                    if (depth == 2 && parentKey == "Health" && node.Key == "Radius")
                        ConvertPxToRange(ref node.Value.Value);
                }

                // CrateDrop was replaced with CrateSpawner
                if (engineVersion < 20131231)
                {
                    if (depth == 1 && parentKey == "World")
                    {
                        if (node.Key == "CrateDrop")
                            node.Key = "CrateSpawner";

                        if (node.Key == "-CrateDrop")
                            node.Key = "-CrateSpawner";
                    }
                }

                // AttackTesla was replaced with AttackCharge
                if (engineVersion < 20140307)
                {
                    if (depth == 1)
                    {
                        if (node.Key == "AttackTesla")
                            node.Key = "AttackCharge";

                        if (node.Key == "-AttackTesla")
                            node.Key = "-AttackCharge";
                    }
                }

                // AttackMove was generalized to support all moveable actor types
                if (engineVersion < 20140116)
                {
                    if (depth == 1 && node.Key == "AttackMove")
                        node.Value.Nodes.RemoveAll(n => n.Key == "JustMove");
                }

                // UnloadFacing was removed from Cargo
                if (engineVersion < 20140212)
                {
                    if (depth == 1 && node.Key == "Cargo")
                        node.Value.Nodes.RemoveAll(n => n.Key == "UnloadFacing");
                }

                // RevealShroud was updated to use world units.
                if (engineVersion < 20140220)
                {
                    if (depth == 2 && parentKey == "RevealsShroud" && node.Key == "Range")
                        ConvertFloatToRange(ref node.Value.Value);

                    if (depth == 2 && parentKey == "CreatesShroud" && node.Key == "Range")
                        ConvertFloatToRange(ref node.Value.Value);
                }

                // Waypoint was renamed to Immobile
                if (engineVersion < 20140312)
                {
                    if (depth == 1 && node.Key == "Waypoint")
                        node.Key = "Immobile";
                }

                // Spy was renamed to Disguise
                if (engineVersion < 20140314)
                {
                    if (depth == 1 && node.Key == "Spy")
                        node.Key = "Disguise";

                    if (depth == 1 && node.Key == "SpyToolTip")
                        node.Key = "DisguiseToolTip";

                    if (depth == 1 && node.Key == "RenderSpy")
                        node.Key = "RenderDisguise";
                }

                // IOccupySpace was removed from Mine
                if (engineVersion < 20140320)
                {
                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key == "Mine"))
                        node.Value.Nodes.Add(new MiniYamlNode("Immobile", new MiniYaml("", new List<MiniYamlNode>() { new MiniYamlNode("OccupiesSpace", "true") })));
                    else
                        foreach (var i in nodes.Where(n => n.Key == "Immobile"))
                            if (!i.Value.Nodes.Any(n => n.Key == "OccupiesSpace"))
                                i.Value.Nodes.Add(new MiniYamlNode("OccupiesSpace", "false"));
                }

                // Armaments and muzzleflashes were reworked to support garrisoning
                if (engineVersion < 20140321)
                {
                    if (depth == 0)
                    {
                        var muzzles = node.Value.Nodes.Where(n => n.Key.StartsWith("WithMuzzleFlash"));
                        var armaments = node.Value.Nodes.Where(n => n.Key.StartsWith("Armament"));

                        // Shift muzzle flash definitions to Armament
                        foreach (var m in muzzles)
                        {
                            var muzzleArmNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "Armament");
                            var muzzleSequenceNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "Sequence");
                            var muzzleSplitFacingsNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "SplitFacings");
                            var muzzleFacingsCountNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "FacingCount");

                            var muzzleArmName = muzzleArmNode != null ? muzzleArmNode.Value.Value.Trim() : "primary";
                            var muzzleSequence = muzzleSequenceNode != null ? muzzleSequenceNode.Value.Value.Trim() : "muzzle";
                            var muzzleSplitFacings = muzzleSplitFacingsNode != null ? FieldLoader.GetValue<bool>("SplitFacings", muzzleSplitFacingsNode.Value.Value) : false;
                            var muzzleFacingsCount = muzzleFacingsCountNode != null ? FieldLoader.GetValue<int>("FacingsCount", muzzleFacingsCountNode.Value.Value) : 8;

                            foreach (var a in armaments)
                            {
                                var armNameNode = m.Value.Nodes.SingleOrDefault(n => n.Key == "Name");
                                var armName = armNameNode != null ? armNameNode.Value.Value.Trim() : "primary";

                                if (muzzleArmName == armName)
                                {
                                    a.Value.Nodes.Add(new MiniYamlNode("MuzzleSequence", muzzleSequence));
                                    if (muzzleSplitFacings)
                                        a.Value.Nodes.Add(new MiniYamlNode("MuzzleSplitFacings", muzzleFacingsCount.ToString()));
                                }
                            }
                        }

                        foreach (var m in muzzles.ToList().Skip(1))
                            node.Value.Nodes.Remove(m);
                    }

                    // Remove all but the first muzzle flash definition
                    if (depth == 1 && node.Key.StartsWith("WithMuzzleFlash"))
                    {
                        node.Key = "WithMuzzleFlash";
                        node.Value.Nodes.RemoveAll(n => n.Key == "Armament");
                        node.Value.Nodes.RemoveAll(n => n.Key == "Sequence");
                    }
                }

                // "disabled" palette overlay has been moved into it's own DisabledOverlay trait
                if (engineVersion < 20140305)
                {
                    if (node.Value.Nodes.Any(n => n.Key.StartsWith("RequiresPower"))
                        && !node.Value.Nodes.Any(n => n.Key.StartsWith("DisabledOverlay")))
                    {
                        node.Value.Nodes.Add(new MiniYamlNode("DisabledOverlay", new MiniYaml("")));
                    }
                }

                // ChronoshiftDeploy was replaced with PortableChrono
                if (engineVersion < 20140321)
                {
                    if (depth == 1 && node.Key == "ChronoshiftDeploy")
                        node.Key = "PortableChrono";

                    if (depth == 2 && parentKey == "PortableChrono" && node.Key == "JumpDistance")
                        node.Key = "MaxDistance";
                }

                // Added new Lua API
                if (engineVersion < 20140421)
                {
                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key == "LuaScriptEvents"))
                        node.Value.Nodes.Add(new MiniYamlNode("ScriptTriggers", ""));
                }

                if (engineVersion < 20140517)
                {
                    if (depth == 0)
                        node.Value.Nodes.RemoveAll(n => n.Key == "TeslaInstantKills");
                }

                if (engineVersion < 20140615)
                {
                    if (depth == 1 && node.Key == "StoresOre")
                        node.Key = "StoresResources";
                }

                // make animation is now its own trait
                if (engineVersion < 20140621)
                {
                    if (depth == 1 && (node.Key.StartsWith("RenderBuilding")))
                        node.Value.Nodes.RemoveAll(n => n.Key == "HasMakeAnimation");

                    if (node.Value.Nodes.Any(n => n.Key.StartsWith("RenderBuilding"))
                        && !node.Value.Nodes.Any(n => n.Key == "RenderBuildingWall")
                        && !node.Value.Nodes.Any(n => n.Key == "WithMakeAnimation"))
                    {
                        node.Value.Nodes.Add(new MiniYamlNode("WithMakeAnimation", new MiniYaml("")));
                    }
                }

                // ParachuteAttachment was merged into Parachutable
                if (engineVersion < 20140701)
                {
                    if (depth == 1 && node.Key == "ParachuteAttachment")
                    {
                        node.Key = "Parachutable";

                        foreach (var subnode in node.Value.Nodes)
                            if (subnode.Key == "Offset")
                                subnode.Key = "ParachuteOffset";
                    }

                    if (depth == 2 && node.Key == "ParachuteSprite")
                        node.Key = "ParachuteSequence";
                }

                // SonarPulsePower was implemented as a generic SpawnActorPower
                if (engineVersion < 20140703)
                {
                    if (depth == 1 && node.Key == "SonarPulsePower")
                        node.Key = "SpawnActorPower";
                }

                if (engineVersion < 20140707)
                {
                    // SpyPlanePower was removed (use AirstrikePower instead)
                    if (depth == 1 && node.Key == "SpyPlanePower")
                    {
                        node.Key = "AirstrikePower";

                        var revealTime = 6 * 25;
                        var revealNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "RevealTime");
                        if (revealNode != null)
                        {
                            revealTime = int.Parse(revealNode.Value.Value) * 25;
                            node.Value.Nodes.Remove(revealNode);
                        }

                        node.Value.Nodes.Add(new MiniYamlNode("CameraActor", new MiniYaml("camera")));
                        node.Value.Nodes.Add(new MiniYamlNode("CameraRemoveDelay", new MiniYaml(revealTime.ToString())));
                        node.Value.Nodes.Add(new MiniYamlNode("UnitType", new MiniYaml("u2")));
                    }

                    if (depth == 2 && node.Key == "LZRange" && parentKey == "ParaDrop")
                    {
                        node.Key = "DropRange";
                        ConvertFloatToRange(ref node.Value.Value);
                    }
                }

                // GiveUnitCrateAction and GiveMcvCrateAction were updated to allow multiple units
                if (engineVersion < 20140723)
                {
                    if (depth == 2 && parentKey.Contains("GiveMcvCrateAction"))
                        if (node.Key == "Unit")
                            node.Key = "Units";

                    if (depth == 2 && parentKey.Contains("GiveUnitCrateAction"))
                        if (node.Key == "Unit")
                            node.Key = "Units";
                }

                // Power from Building was moved out into Power and ScalePowerWithHealth traits
                if (engineVersion < 20140823)
                {
                    if (depth == 0)
                    {
                        var actorTraits = node.Value.Nodes;
                        var building = actorTraits.FirstOrDefault(t => t.Key == "Building");
                        if (building != null)
                        {
                            var buildingFields = building.Value.Nodes;
                            var power = buildingFields.FirstOrDefault(n => n.Key == "Power");
                            if (power != null)
                            {
                                buildingFields.Remove(power);

                                var powerFields = new List<MiniYamlNode> { new MiniYamlNode("Amount", power.Value) };
                                actorTraits.Add(new MiniYamlNode("Power", new MiniYaml("", powerFields)));

                                if (FieldLoader.GetValue<int>("Power", power.Value.Value) > 0)
                                    actorTraits.Add(new MiniYamlNode("ScaleWithHealth", ""));
                            }
                        }
                    }
                }

                if (engineVersion < 20140803)
                {
                    // ContainsCrate was removed (use LeavesHusk instead)
                    if (depth == 1 && node.Key == "ContainsCrate")
                    {
                        node.Key = "LeavesHusk";
                        node.Value.Nodes.Add(new MiniYamlNode("HuskActor", new MiniYaml("crate")));
                    }
                }

                if (engineVersion < 20140806)
                {
                    // remove ConquestVictoryConditions when StrategicVictoryConditions is set
                    if (depth == 0 && node.Key == "Player" && node.Value.Nodes.Any(n => n.Key == "StrategicVictoryConditions"))
                        node.Value.Nodes.Add(new MiniYamlNode("-ConquestVictoryConditions", ""));

                    // the objectives panel trait and its properties have been renamed
                    if (depth == 1 && node.Key == "ConquestObjectivesPanel")
                    {
                        node.Key = "ObjectivesPanel";
                        node.Value.Nodes.RemoveAll(_ => true);
                        node.Value.Nodes.Add(new MiniYamlNode("PanelName", new MiniYaml("SKIRMISH_STATS")));
                    }

                }

                // Veterancy was changed to use the upgrades system
                if (engineVersion < 20140807)
                {
                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("GainsExperience")))
                        node.Value.Nodes.Add(new MiniYamlNode("GainsStatUpgrades", new MiniYaml("")));

                    if (depth == 1 && node.Key == "-CloakCrateAction")
                        node.Key = "-UnitUpgradeCrateAction@cloak";

                    if (depth == 1 && node.Key == "CloakCrateAction")
                    {
                        node.Key = "UnitUpgradeCrateAction@cloak";
                        node.Value.Nodes.Add(new MiniYamlNode("Upgrades", new MiniYaml("cloak")));
                    }

                    if (depth == 2 && node.Key == "RequiresCrate" && parentKey == "Cloak")
                    {
                        node.Key = "RequiresUpgrade";
                        node.Value.Value = "cloak";
                    }
                }

                // Modifiers were changed to integer percentages
                if (engineVersion < 20140812)
                {
                    if (depth == 2 && node.Key == "ClosedDamageMultiplier" && parentKey == "AttackPopupTurreted")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);

                    if (depth == 2 && node.Key == "ArmorModifier" && parentKey == "GainsStatUpgrades")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);

                    if (depth == 2 && node.Key == "FullyLoadedSpeed" && parentKey == "Harvester")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);

                    if (depth == 2 && node.Key == "PanicSpeedModifier" && parentKey == "ScaredyCat")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);

                    if (depth == 2 && node.Key == "ProneSpeed" && parentKey == "TakeCover")
                    {
                        node.Key = "SpeedModifier";
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);
                    }

                    if (depth == 2 && node.Key == "SpeedModifier" && parentKey == "GainsStatUpgrades")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);

                    if (depth == 2 && node.Key == "FirepowerModifier" && parentKey == "GainsStatUpgrades")
                        ConvertFloatArrayToPercentArray(ref node.Value.Value);
                }

                // RemoveImmediately was replaced with RemoveOnConditions
                if (engineVersion < 20140821)
                {
                    if (depth == 1)
                    {
                        if (node.Key == "RemoveImmediately")
                            node.Key = "RemoveOnConditions";

                        if (node.Key == "-RemoveImmediately")
                            node.Key = "-RemoveOnConditions";
                    }
                }

                if (engineVersion < 20140823)
                {
                    if (depth == 2 && node.Key == "ArmorUpgrade" && parentKey == "GainsStatUpgrades")
                        node.Key = "DamageUpgrade";

                    if (depth == 2 && node.Key == "ArmorModifier" && parentKey == "GainsStatUpgrades")
                    {
                        node.Key = "DamageModifier";
                        node.Value.Value = string.Join(", ", node.Value.Value.Split(',')
                            .Select(s => ((int)(100 * 100 / float.Parse(s))).ToString()));
                    }
                    if (depth == 3 && parentKey == "Upgrades")
                        node.Value.Value = node.Value.Value.Replace("armor", "damage");
                }

                // RenderInfantryProne and RenderInfantryPanic was merged into RenderInfantry
                if (engineVersion < 20140824)
                {
                    var renderInfantryRemoval = node.Value.Nodes.FirstOrDefault(n => n.Key == "-RenderInfantry");
                    if (depth == 0 && renderInfantryRemoval != null && !node.Value.Nodes.Any(n => n.Key == "RenderDisguise"))
                        node.Value.Nodes.Remove(renderInfantryRemoval);

                    if (depth == 1 && (node.Key == "RenderInfantryProne" || node.Key == "RenderInfantryPanic"))
                        node.Key = "RenderInfantry";
                }

                // InfDeath was renamed to DeathType
                if (engineVersion < 20140830)
                {

                    if (depth == 2 && parentKey.StartsWith("DeathSounds") && node.Key == "InfDeaths")
                        node.Key = "DeathTypes";

                    if (depth == 2 && parentKey == "SpawnsViceroid" && node.Key == "InfDeath")
                        node.Key = "DeathType";

                    if (depth == 2 && parentKey == "Explodes" && node.Key == "InfDeath")
                        node.Key = "DeathType";
                }

                // SellSounds from Building was moved into Sellable
                if (engineVersion < 20140904)
                {
                    if (depth == 0)
                    {
                        var actorTraits = node.Value.Nodes;
                        var building = actorTraits.FirstOrDefault(t => t.Key == "Building");
                        if (building != null)
                        {
                            var buildingFields = building.Value.Nodes;
                            var sellSounds = buildingFields.FirstOrDefault(n => n.Key == "SellSounds");
                            if (sellSounds != null)
                            {
                                buildingFields.Remove(sellSounds);
                                var sellable = actorTraits.FirstOrDefault(t => t.Key == "Sellable");
                                if (sellable != null)
                                    sellable.Value.Nodes.Add(sellSounds);
                                else
                                {
                                    Console.WriteLine("Warning: Adding Sellable trait to {0} in {1}".F(node.Key, node.Location.Filename));
                                    actorTraits.Add(new MiniYamlNode("Sellable", new MiniYaml("", new List<MiniYamlNode> { sellSounds })));
                                }
                            }
                        }
                    }
                }

                // DuplicateUnitCrateAction was tidied up
                if (engineVersion < 20140912)
                {
                    if (depth == 2 && node.Key == "MaxDuplicatesWorth" && parentKey == "DuplicateUnitCrateAction")
                        node.Key = "MaxDuplicateValue";

                    if (depth == 2 && node.Key == "ValidDuplicateTypes" && parentKey == "DuplicateUnitCrateAction")
                        node.Key = "ValidTargets";
                }

                // Added WithDeathAnimation
                if (engineVersion < 20140913)
                {
                    var spawnsCorpseRemoval = node.Value.Nodes.FirstOrDefault(n => n.Key == "SpawnsCorpse");

                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("RenderInfantry")) && spawnsCorpseRemoval == null)
                        node.Value.Nodes.Add(new MiniYamlNode("WithDeathAnimation", new MiniYaml("")));

                    if (depth == 2 && node.Key == "SpawnsCorpse" && parentKey == "RenderInfantry")
                        node.Value.Nodes.Remove(spawnsCorpseRemoval);

                    // CrushableInfantry renamed to Crushable
                    if (depth == 1)
                    {
                        if (node.Key == "CrushableInfantry")
                            node.Key = "Crushable";

                        if (node.Key == "-CrushableInfantry")
                            node.Key = "-Crushable";
                    }
                }

                // Replaced Wall with Crushable + BlocksBullets
                if (engineVersion < 20140914)
                {
                    if (depth == 0)
                    {
                        var actorTraits = node.Value.Nodes;
                        var wall = actorTraits.FirstOrDefault(t => t.Key == "Wall");
                        if (wall != null)
                            node.Value.Nodes.Add(new MiniYamlNode("BlocksBullets", new MiniYaml("")));

                        var blocksBullets = actorTraits.FirstOrDefault(t => t.Key == "BlocksBullets");
                        if (depth == 1 && node.Key == "Wall" && blocksBullets != null)
                            node.Key = "Crushable";
                    }
                }

                if (engineVersion < 20140927)
                {
                    if (depth == 0)
                        node.Value.Nodes.RemoveAll(n => n.Key == "SelfHealingTech");

                    if (depth == 2 && node.Key == "RequiresTech" && parentKey.StartsWith("SelfHealing"))
                    {
                        node.Key = "RequiresUpgrade";
                        node.Value.Value = "selfhealing-needs-reconfiguration";
                    }
                }

                if (engineVersion < 20141001)
                {
                    // Routed unit upgrades via the UnitUpgradeManager trait
                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("GainsStatUpgrades")))
                        node.Value.Nodes.Add(new MiniYamlNode("UnitUpgradeManager", new MiniYaml("")));

                    // Replaced IronCurtainPower -> GrantUpgradePower
                    if (depth == 1 && node.Key == "IronCurtainPower")
                    {
                        node.Key = "GrantUpgradePower@IRONCURTAIN";
                        node.Value.Nodes.Add(new MiniYamlNode("Upgrades", "invulnerability"));

                        var durationNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Duration");
                        if (durationNode != null)
                            durationNode.Value.Value = (int.Parse(durationNode.Value.Value) * 25).ToString();
                        else
                            node.Value.Nodes.Add(new MiniYamlNode("Duration", "600"));

                        var soundNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "IronCurtainSound");
                        if (soundNode != null)
                            soundNode.Key = "GrantUpgradeSound";
                    }

                    if (depth == 0 && node.Value.Nodes.Any(n => n.Key.StartsWith("IronCurtainable")))
                    {
                        node.Value.Nodes.RemoveAll(n => n.Key.StartsWith("IronCurtainable"));

                        var overlayKeys = new List<MiniYamlNode>();
                        overlayKeys.Add(new MiniYamlNode("RequiresUpgrade", "invulnerability"));
                        node.Value.Nodes.Add(new MiniYamlNode("UpgradeOverlay@IRONCURTAIN", new MiniYaml("", overlayKeys)));

                        var invulnKeys = new List<MiniYamlNode>();
                        invulnKeys.Add(new MiniYamlNode("RequiresUpgrade", "invulnerability"));
                        node.Value.Nodes.Add(new MiniYamlNode("InvulnerabilityUpgrade@IRONCURTAIN", new MiniYaml("", invulnKeys)));

                        var barKeys = new List<MiniYamlNode>();
                        barKeys.Add(new MiniYamlNode("Upgrade", "invulnerability"));
                        node.Value.Nodes.Add(new MiniYamlNode("TimedUpgradeBar", new MiniYaml("", barKeys)));

                        if (!node.Value.Nodes.Any(n => n.Key.StartsWith("UnitUpgradeManager")))
                            node.Value.Nodes.Add(new MiniYamlNode("UnitUpgradeManager", new MiniYaml("")));
                    }

                    if (depth == 1 && node.Key == "-IronCurtainable")
                        node.Key = "-InvulnerabilityUpgrade@IRONCURTAIN";

                    // Replaced RemoveOnConditions with KillsSelf
                    if (depth == 1 && node.Key == "RemoveOnConditions")
                    {
                        node.Key = "KillsSelf";
                        node.Value.Nodes.Add(new MiniYamlNode("RemoveInstead", new MiniYaml("true")));
                    }

                    if (depth == 1 && node.Key.StartsWith("UnitUpgradeCrateAction"))
                    {
                        var parts = node.Key.Split('@');
                        node.Key = "GrantUpgradeCrateAction";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];
                    }

                    if (depth == 1 && node.Key.StartsWith("-UnitUpgradeCrateAction"))
                    {
                        var parts = node.Key.Split('@');
                        node.Key = "-GrantUpgradeCrateAction";
                        if (parts.Length > 1)
                            node.Key += "@" + parts[1];
                    }
                }

                if (engineVersion < 20141002)
                {
                    if (node.Key == "AlignWhenIdle" && parentKey == "Turreted")
                    {
                        node.Key = "RealignDelay";
                        node.Value.Value = "0";
                    }
                }

                UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
            }
        }
Exemple #60
0
        internal static void UpgradePlayers(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
        {
            foreach (var node in nodes)
            {
                // Rename PlayerReference.Race and LockRace to Faction and LockFaction
                if (engineVersion < 20150706)
                {
                    var race = node.Value.Nodes.FirstOrDefault(x => x.Key == "Race");
                    if (race != null)
                        race.Key = "Faction";

                    var lockRace = node.Value.Nodes.FirstOrDefault(x => x.Key == "LockRace");
                    if (lockRace != null)
                        lockRace.Key = "LockFaction";
                }

                UpgradePlayers(engineVersion, ref node.Value.Nodes, node, depth + 1);
            }
        }