private static void Go(Engine engine)
        {
            var playerClassDefinitionClass = engine.GetClass("WillowGame.PlayerClassDefinition");

            if (playerClassDefinitionClass == null)
            {
                throw new InvalidOperationException();
            }

            using (var output = Dataminer.NewDump("Player Classes.json"))
                using (var writer = new JsonTextWriter(output))
                {
                    writer.Indentation = 2;
                    writer.IndentChar  = ' ';
                    writer.Formatting  = Formatting.Indented;

                    writer.WriteStartObject();

                    var playerClassDefinitionClasses = engine.Objects
                                                       .Where(o => o.IsA(playerClassDefinitionClass) &&
                                                              o.GetName().StartsWith("Default__") ==
                                                              false)
                                                       .OrderBy(o => o.GetPath());
                    foreach (dynamic playerClassDefinition in playerClassDefinitionClasses)
                    {
                        writer.WritePropertyName(playerClassDefinition.GetPath());
                        writer.WriteStartObject();

                        var characterName = (CharacterNames)playerClassDefinition.CharacterName;

                        writer.WritePropertyName("name");
                        writer.WriteValue(characterName.ToString());

                        // TODO: PlayerSkillSet

                        writer.WriteEndObject();
                    }

                    writer.WriteEndObject();
                    writer.Flush();
                }
        }
        private static void Go(Engine engine)
        {
            var itemDefinitionClass                     = engine.GetClass("WillowGame.ItemDefinition");
            var weaponTypeDefinitionClass               = engine.GetClass("WillowGame.WeaponTypeDefinition");
            var inventoryBalanceDefinitionClass         = engine.GetClass("WillowGame.InventoryBalanceDefinition");
            var itemPartListCollectionDefinitionClass   = engine.GetClass("WillowGame.ItemPartListCollectionDefinition");
            var weaponPartListCollectionDefinitionClass = engine.GetClass("WillowGame.WeaponPartListCollectionDefinition");

            if (itemDefinitionClass == null ||
                weaponTypeDefinitionClass == null ||
                inventoryBalanceDefinitionClass == null ||
                weaponPartListCollectionDefinitionClass == null)
            {
                throw new InvalidOperationException();
            }

            var itemBalances       = new List <dynamic>();
            var weaponTypeBalances = new List <dynamic>();

            {
                var balanceDefinitions = engine.Objects
                                         .Where(o => o.IsA(inventoryBalanceDefinitionClass) &&
                                                o.GetName().StartsWith("Default__") == false)
                                         .OrderBy(o => o.GetPath());
                foreach (dynamic balanceDefinition in balanceDefinitions)
                {
                    dynamic associatedDefinition = null;

                    if (balanceDefinition.InventoryDefinition != null)
                    {
                        associatedDefinition = balanceDefinition.InventoryDefinition;
                        var partListCollection = balanceDefinition.PartListCollection;
                        if (partListCollection != null)
                        {
                            if (partListCollection.IsA(itemPartListCollectionDefinitionClass) == true)
                            {
                                if (partListCollection.AssociatedItem != associatedDefinition)
                                {
                                    throw new InvalidOperationException();
                                }
                            }
                            else if (partListCollection.IsA(weaponPartListCollectionDefinitionClass) == true)
                            {
                                if (partListCollection.AssociatedWeaponType != associatedDefinition)
                                {
                                    throw new InvalidOperationException();
                                }
                            }
                            else
                            {
                                Console.WriteLine($"Bad definition (part list mismatch): {balanceDefinition.GetPath()}");
                                continue;
                            }
                        }
                    }
                    else
                    {
                        var partListCollection = balanceDefinition.PartListCollection;
                        if (partListCollection != null)
                        {
                            if (partListCollection.IsA(itemPartListCollectionDefinitionClass) == true)
                            {
                                associatedDefinition = partListCollection.AssociatedItem;
                            }
                            else if (partListCollection.IsA(weaponPartListCollectionDefinitionClass) == true)
                            {
                                associatedDefinition = partListCollection.AssociatedWeaponType;
                            }
                            else
                            {
                                throw new NotSupportedException();
                            }
                        }
                        else
                        {
                            Console.WriteLine($"Bad definition (associated definition missing): {balanceDefinition.GetPath()}");
                            continue;
                        }
                    }

                    if (associatedDefinition.IsA(itemDefinitionClass) == true)
                    {
                        itemBalances.Add(associatedDefinition);
                    }
                    else if (associatedDefinition.IsA(weaponTypeDefinitionClass) == true)
                    {
                        weaponTypeBalances.Add(associatedDefinition);
                    }
                    else
                    {
                        throw new NotSupportedException();
                    }
                }
            }

            using (var output = Dataminer.NewDump("Weapon Types.json"))
                using (var writer = new JsonTextWriter(output))
                {
                    writer.Indentation = 2;
                    writer.IndentChar  = ' ';
                    writer.Formatting  = Formatting.Indented;

                    writer.WriteStartObject();
                    foreach (var weaponType in weaponTypeBalances.Distinct().OrderBy(wp => wp.GetPath()))
                    {
                        writer.WritePropertyName(weaponType.GetPath());
                        writer.WriteStartObject();

                        UnrealClass weaponPartClass = weaponType.GetClass();
                        if (weaponPartClass.Path != "WillowGame.WeaponTypeDefinition")
                        {
                            throw new InvalidOperationException();
                        }

                        var weaponBaseType = GetWeaponType(weaponType);

                        writer.WritePropertyName("type");
                        writer.WriteValue(((WeaponType)weaponBaseType).ToString());

                        writer.WritePropertyName("name");
                        writer.WriteValue(weaponType.TypeName);

                        if (weaponType.TitleList != null &&
                            weaponType.TitleList.Length > 0)
                        {
                            writer.WritePropertyName("titles");
                            writer.WriteStartArray();
                            IEnumerable <dynamic> titleList = weaponType.TitleList;
                            foreach (var title in titleList
                                     .Where(tp => tp != null)
                                     .OrderBy(tp => tp.GetPath()))
                            {
                                writer.WriteValue(title.GetPath());
                            }
                            writer.WriteEndArray();
                        }

                        if (weaponType.PrefixList != null &&
                            weaponType.PrefixList.Length > 0)
                        {
                            writer.WritePropertyName("prefixes");
                            writer.WriteStartArray();
                            IEnumerable <dynamic> prefixList = weaponType.PrefixList;
                            foreach (var prefix in prefixList
                                     .Where(pp => pp != null)
                                     .OrderBy(pp => pp.GetPath()))
                            {
                                writer.WriteValue(prefix.GetPath());
                            }
                            writer.WriteEndArray();
                        }

                        DumpCustomPartTypeData(writer, "body_parts", weaponType.BodyParts);
                        DumpCustomPartTypeData(writer, "grip_parts", weaponType.GripParts);
                        DumpCustomPartTypeData(writer, "magazine_parts", weaponType.MagazineParts);
                        DumpCustomPartTypeData(writer, "barrel_parts", weaponType.BarrelParts);
                        DumpCustomPartTypeData(writer, "sight_parts", weaponType.SightParts);
                        DumpCustomPartTypeData(writer, "stock_parts", weaponType.StockParts);
                        DumpCustomPartTypeData(writer, "action_parts", weaponType.ActionParts);
                        DumpCustomPartTypeData(writer, "accessory_parts", weaponType.AccessoryParts);
                        DumpCustomPartTypeData(writer, "material_parts", weaponType.MaterialParts);

                        writer.WriteEndObject();
                    }
                    writer.WriteEndObject();
                }

            using (var output = Dataminer.NewDump("Item Types.json"))
                using (var writer = new JsonTextWriter(output))
                {
                    writer.Indentation = 2;
                    writer.IndentChar  = ' ';
                    writer.Formatting  = Formatting.Indented;

                    writer.WriteStartObject();
                    foreach (var itemType in itemBalances.Distinct().OrderBy(wp => wp.GetPath()))
                    {
                        writer.WritePropertyName(itemType.GetPath());
                        writer.WriteStartObject();

                        UnrealClass itemPartClass = itemType.GetClass();
                        if (itemPartClass.Path != "WillowGame.ItemDefinition")
                        {
                            throw new InvalidOperationException();
                        }

                        if (string.IsNullOrEmpty((string)itemType.ItemName) == false)
                        {
                            writer.WritePropertyName("name");
                            writer.WriteValue(itemType.ItemName);
                        }

                        if ((bool)itemType.bItemNameIsFullName == true)
                        {
                            writer.WritePropertyName("has_full_name");
                            writer.WriteValue(true);
                        }

                        if ((bool)itemType.bMissionItem == true)
                        {
                            writer.WritePropertyName("is_mission_item");
                            writer.WriteValue(true);
                        }

                        var characterRequirement = (CharacterRequirement)itemType.RequiredCharacter;
                        if (characterRequirement != CharacterRequirement.None)
                        {
                            writer.WritePropertyName("character_required");
                            writer.WriteValue(characterRequirement.ToString());
                        }

                        if (itemType.TitleList != null &&
                            itemType.TitleList.Length > 0)
                        {
                            writer.WritePropertyName("titles");
                            writer.WriteStartArray();
                            IEnumerable <dynamic> titleList = itemType.TitleList;
                            foreach (var title in titleList
                                     .Where(tp => tp != null)
                                     .OrderBy(tp => tp.GetPath()))
                            {
                                writer.WriteValue(title.GetPath());
                            }
                            writer.WriteEndArray();
                        }

                        if (itemType.PrefixList != null &&
                            itemType.PrefixList.Length > 0)
                        {
                            writer.WritePropertyName("prefixes");
                            writer.WriteStartArray();
                            IEnumerable <dynamic> prefixList = itemType.PrefixList;
                            foreach (var prefix in prefixList
                                     .Where(pp => pp != null)
                                     .OrderBy(pp => pp.GetPath()))
                            {
                                writer.WriteValue(prefix.GetPath());
                            }
                            writer.WriteEndArray();
                        }

                        DumpCustomPartTypeData(writer, "body_parts", itemType.BodyParts);
                        DumpCustomPartTypeData(writer, "left_side_parts", itemType.LeftSideParts);
                        DumpCustomPartTypeData(writer, "right_side_parts", itemType.RightSideParts);
                        DumpCustomPartTypeData(writer, "material_parts", itemType.MaterialParts);

                        writer.WriteEndObject();
                    }
                    writer.WriteEndObject();
                }
        }
        private static void Go(Engine engine)
        {
            dynamic outpostLookupClass = engine.GetClass("WillowGame.EmergencyTeleportOutpostLookup");
            dynamic willowGlobalsClass = engine.GetClass("WillowGame.WillowGlobals");
            dynamic dlcPackageClass    = engine.GetClass("WillowGame.DLCPackageDefinition");

            if (outpostLookupClass == null ||
                willowGlobalsClass == null ||
                dlcPackageClass == null)
            {
                throw new InvalidOperationException();
            }

            var     sourceLookup  = new Dictionary <string, string>();
            dynamic willowGlobals = engine.Objects.Single(
                o => o.IsA(willowGlobalsClass) == true &&
                o.GetName().StartsWith("Default__") == false);

            dynamic masterLookup = willowGlobals.MasterRegistrationStationList;

            var outpostOrder = new List <string>();

            foreach (dynamic lookupObject in masterLookup.OutpostLookupList)
            {
                outpostOrder.Add(lookupObject.OutpostName);
            }

            foreach (dynamic dlcPackage in engine.Objects.Where(
                         o => o.IsA(dlcPackageClass) == true &&
                         o.GetName().StartsWith("Default__") == false))
            {
                sourceLookup.Add(dlcPackage.TeleportLookupObject.GetPath(), dlcPackage.GetPath());
            }

            using (var output = Dataminer.NewDump("Emergency Teleport Outposts.json"))
                using (var writer = new JsonTextWriter(output))
                {
                    writer.Indentation = 2;
                    writer.IndentChar  = ' ';
                    writer.Formatting  = Formatting.Indented;

                    var outpostNames = new List <string>();

                    writer.WriteStartObject();
                    foreach (dynamic lookup in engine.Objects
                             .Where(
                                 o => o.IsA(outpostLookupClass) &&
                                 o.GetName().StartsWith("Default__") == false)
                             .OrderBy(o => o.GetPath())
                             .Distinct())
                    {
                        // The master list has every single station, we don't want to write it too.
                        if (lookup == masterLookup)
                        {
                            continue;
                        }

                        writer.WritePropertyName(lookup.GetPath());
                        writer.WriteStartObject();

                        string source;
                        if (sourceLookup.TryGetValue((string)lookup.GetPath(), out source) == true &&
                            source != "None")
                        {
                            writer.WritePropertyName("dlc_package");
                            writer.WriteValue(source);
                        }

                        writer.WritePropertyName("outposts");
                        writer.WriteStartArray();
                        foreach (dynamic lookupObject in ((UnrealObject[])lookup.OutpostLookupList)
                                 .Cast <dynamic>()
                                 .OrderBy(l => outpostOrder.IndexOf(l.OutpostName)))
                        {
                            var outpostName = (string)lookupObject.OutpostName;
                            if (outpostNames.Contains(outpostName) == true)
                            {
                                Console.WriteLine($"Skipping duplicate outpost {outpostName}.");
                                continue;
                            }
                            outpostNames.Add(outpostName);

                            writer.WriteStartObject();

                            writer.WritePropertyName("name");
                            writer.WriteValue(outpostName);

                            writer.WritePropertyName("path");
                            writer.WriteValue(lookupObject.OutpostPathName);

                            var isInitiallyActive = (bool)lookupObject.bInitiallyActive;
                            if (isInitiallyActive == true)
                            {
                                writer.WritePropertyName("is_initially_active");
                                writer.WriteValue(true);
                            }

                            var isCheckpointOnly = (bool)lookupObject.bCheckpointOnly;
                            if (isCheckpointOnly == true)
                            {
                                writer.WritePropertyName("is_checkpoint_only");
                                writer.WriteValue(true);
                            }

                            var displayName = (string)lookupObject.OutpostDisplayName;
                            if (string.IsNullOrEmpty(displayName) == false)
                            {
                                writer.WritePropertyName("display_name");
                                writer.WriteValue(displayName);
                            }

                            var description = (string)lookupObject.OutpostDescription;
                            if (string.IsNullOrEmpty(description) == false &&
                                description != "No Description")
                            {
                                writer.WritePropertyName("description");
                                writer.WriteValue(description);
                            }

                            var previousOutpost = (string)lookupObject.PreviousOutpost;
                            if (string.IsNullOrEmpty(previousOutpost) == false &&
                                previousOutpost != "None")
                            {
                                writer.WritePropertyName("previous_outpost");
                                writer.WriteValue(previousOutpost);
                            }

                            if (lookupObject.MissionDependencies != null &&
                                lookupObject.MissionDependencies.Length > 0)
                            {
                                writer.WritePropertyName("mission_dependencies");
                                writer.WriteStartObject();
                                foreach (var missionDependency in lookupObject.MissionDependencies)
                                {
                                    writer.WritePropertyName(missionDependency.MissionDefinition.GetPath());
                                    writer.WriteValue(((MissionStatus)missionDependency.MissionStatus).ToString());
                                }
                                writer.WriteEndObject();
                            }

                            var outpostIndex = outpostOrder.IndexOf(outpostName);
                            if (outpostIndex < 0)
                            {
                                throw new InvalidOperationException();
                            }

                            writer.WritePropertyName("sort_order");
                            writer.WriteValue(outpostIndex);

                            writer.WriteEndObject();
                        }
                        writer.WriteEndArray();

                        writer.WriteEndObject();
                    }
                    writer.WriteEndObject();
                    writer.Flush();
                }
        }