示例#1
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="name">The unique location name.</param>
        /// <param name="fromMapFile">The initial map file to load.</param>
        /// <param name="migrateLegacyNames">The fallback location names to migrate if no location is found matching <paramref name="name"/>.</param>
        /// <param name="contentPack">The content pack which added the location.</param>
        public CustomLocationData(string name, string fromMapFile, string[] migrateLegacyNames, IContentPack contentPack)
        {
            this.Name               = name;
            this.FromMapFile        = fromMapFile;
            this.MigrateLegacyNames = migrateLegacyNames;
            this.ContentPack        = contentPack;
            this.PublicMapPath      = PathUtilities.NormalizeAssetName($"Maps/{name}");
            this.IsEnabled          = true;

            this.HasLegacyNames = this.MigrateLegacyNames.Any();
        }
示例#2
0
        /// <summary>Load data from a version 1.1 manifest.</summary>
        /// <param name="contentPack">The content pack to load.</param>
        /// <param name="configPath">The content pack's relative config file path.</param>
        private LocationConfig ReadConfig_1_2(IContentPack contentPack, string configPath)
        {
            try
            {
                // read raw data
                LocationConfig config = contentPack.ReadJsonFile <LocationConfig>(configPath);

                // load child config
                if (config.Includes.Any())
                {
                    foreach (string include in config.Includes)
                    {
                        // parse file
                        string         childPath = $"{include}.json";
                        LocationConfig childConfig;
                        try
                        {
                            childConfig = contentPack.LoadAsset <LocationConfig>(childPath);
                        }
                        catch (Exception err)
                        {
                            this.Monitor.Log($"   Skipped child config '{childPath}' because it can't be parsed.", LogLevel.Error, err);
                            continue;
                        }

                        // add data to parent config
                        foreach (Conditional entry in childConfig.Conditionals)
                        {
                            config.Conditionals.Add(entry);
                        }
                        foreach (Location entry in childConfig.Locations)
                        {
                            config.Locations.Add(entry);
                        }
                        foreach (Override entry in childConfig.Overrides)
                        {
                            config.Overrides.Add(entry);
                        }
                        foreach (Property entry in childConfig.Properties)
                        {
                            config.Properties.Add(entry);
                        }
                        foreach (Redirect entry in childConfig.Redirects)
                        {
                            config.Redirects.Add(entry);
                        }
                        foreach (string entry in childConfig.Shops)
                        {
                            config.Shops.Add(entry);
                        }
                        foreach (TeleporterList entry in childConfig.Teleporters)
                        {
                            config.Teleporters.Add(entry);
                        }
                        foreach (Tile entry in childConfig.Tiles)
                        {
                            config.Tiles.Add(entry);
                        }
                        foreach (Tilesheet entry in childConfig.Tilesheets)
                        {
                            config.Tilesheets.Add(entry);
                        }
                        foreach (Warp entry in childConfig.Warps)
                        {
                            config.Warps.Add(entry);
                        }
                    }
                }

                return(config);
            }
            catch (Exception err)
            {
                this.Monitor.Log("   Skipped: can't parse config file (version 1.2).", LogLevel.Warn, err);
                return(null);
            }
        }
示例#3
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ContentPackAssetProvider"/> class.
 /// </summary>
 /// <param name="contentPack">The content pack to provide assets for.</param>
 public ContentPackAssetProvider(IContentPack contentPack)
 {
     this.contentPack = contentPack;
 }
示例#4
0
        /*********
        ** Protected methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="path">The path to the patch from the root content file.</param>
        /// <param name="type">The patch type.</param>
        /// <param name="assetName">The normalized asset name to intercept.</param>
        /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
        /// <param name="updateRate">When the patch should be updated.</param>
        /// <param name="normalizeAssetName">Normalize an asset name.</param>
        /// <param name="contentPack">The content pack which requested the patch.</param>
        /// <param name="parentPatch">The parent <see cref="PatchType.Include"/> patch for which this patch was loaded, if any.</param>
        /// <param name="fromAsset">The normalized asset key from which to load the local asset (if applicable), including tokens.</param>
        protected Patch(LogPathBuilder path, PatchType type, IManagedTokenString assetName, IEnumerable <Condition> conditions, UpdateRate updateRate, IContentPack contentPack, IPatch parentPatch, Func <string, string> normalizeAssetName, IManagedTokenString fromAsset = null)
        {
            this.Path = path;
            this.Type = type;
            this.ManagedRawTargetAsset  = assetName;
            this.Conditions             = conditions.ToArray();
            this.UpdateRate             = updateRate;
            this.NormalizeAssetNameImpl = normalizeAssetName;
            this.PrivateContext         = new LocalContext(scope: contentPack.Manifest.UniqueID);
            this.ManagedRawFromAsset    = fromAsset;
            this.ContentPack            = contentPack;
            this.ParentPatch            = parentPatch;

            this.Contextuals
            .Add(this.Conditions)
            .Add(assetName)
            .Add(fromAsset);
            this.ManuallyUpdatedTokens.Add(assetName);
            this.ManuallyUpdatedTokens.Add(fromAsset);
        }
示例#5
0
        /// <summary>Validate the configuration for a content pack and remove invalid settings.</summary>
        /// <param name="contentPack">The content pack to load.</param>
        /// <param name="config">The config settings.</param>
        private ContentPackData ValidateData(IContentPack contentPack, LocationConfig config)
        {
            ContentPackData data = new ContentPackData {
                ContentPack = contentPack
            };

            string currentStep = "Entry";

            try
            {
                // validate locations
                if (config.Locations != null)
                {
                    currentStep = "Locations";
                    foreach (Location location in config.Locations)
                    {
                        if (!this.AssertFileExists(contentPack, "location", location.FileName))
                        {
                            continue;
                        }
                        if (!this.AffectedLocations.Add(location.MapName))
                        {
                            this.Monitor.Log($"   Skipped {location}: that map is already being modified.", LogLevel.Error);
                            continue;
                        }

                        if (!this.LocationTypes.Contains(location.Type))
                        {
                            this.Monitor.Log($"   Location {location} has unknown type, using 'Default' instead.", LogLevel.Warn);
                            location.Type = "Default";
                        }

                        data.Locations.Add(location);
                    }
                }

                // validate overrides
                if (config.Overrides != null)
                {
                    currentStep = "Overrides";
                    foreach (Override @override in config.Overrides)
                    {
                        if (!this.AssertFileExists(contentPack, "override", @override.FileName))
                        {
                            continue;
                        }
                        if (!this.AffectedLocations.Add(@override.MapName))
                        {
                            this.Monitor.Log($"   Skipped {@override}: that map is already being modified.", LogLevel.Error);
                            continue;
                        }

                        data.Overrides.Add(@override);
                    }
                }

                // validate redirects
                if (config.Redirects != null)
                {
                    currentStep = "Redirects";
                    foreach (Redirect redirect in config.Redirects)
                    {
                        if (!File.Exists(Path.Combine(Game1.content.RootDirectory, $"{redirect.FromFile}.xnb")))
                        {
                            this.Monitor.Log($"   Skipped {redirect}: file {redirect.FromFile}.xnb doesn't exist in the game's content folder.", LogLevel.Error);
                            continue;
                        }

                        if (!this.AssertFileExists(contentPack, "redirect", redirect.ToFile))
                        {
                            continue;
                        }

                        data.Redirects.Add(redirect);
                    }
                }

                // validate tilesheets
                if (config.Tilesheets != null)
                {
                    currentStep = "Tilesheets";
                    foreach (Tilesheet tilesheet in config.Tilesheets)
                    {
                        if (tilesheet.FileName != null)
                        {
                            if (tilesheet.Seasonal)
                            {
                                bool filesExist =
                                    this.AssertFileExists(contentPack, "tilesheet", $"{tilesheet.FileName}_spring") &&
                                    this.AssertFileExists(contentPack, "tilesheet", $"{tilesheet.FileName}_summer") &&
                                    this.AssertFileExists(contentPack, "tilesheet", $"{tilesheet.FileName}_fall") &&
                                    this.AssertFileExists(contentPack, "tilesheet", $"{tilesheet.FileName}_winter");
                                if (!filesExist)
                                {
                                    continue;
                                }
                            }
                            else if (!this.AssertFileExists(contentPack, "tilesheet", tilesheet.FileName))
                            {
                                continue;
                            }
                        }

                        data.Tilesheets.Add(tilesheet);
                    }
                }

                // validate tiles
                if (config.Tiles != null)
                {
                    currentStep = "Tiles";
                    foreach (Tile tile in config.Tiles)
                    {
                        if (!this.ValidLayers.Contains(tile.LayerId))
                        {
                            this.Monitor.Log($"   Skipped {tile}: unknown layer '{tile.LayerId}'.", LogLevel.Error);
                            continue;
                        }

                        data.Tiles.Add(tile);
                    }
                }

                // validate properties
                if (config.Properties != null)
                {
                    currentStep = "Properties";
                    foreach (Property property in config.Properties)
                    {
                        if (!this.ValidLayers.Contains(property.LayerId))
                        {
                            this.Monitor.Log($"   Skipped `{property}`: unknown layer '{property.LayerId}'.",
                                             LogLevel.Error);
                            continue;
                        }

                        data.Properties.Add(property);
                    }
                }

                // validate warps
                if (config.Warps != null)
                {
                    currentStep = "Warps";
                    foreach (Warp warp in config.Warps)
                    {
                        data.Warps.Add(warp);
                    }
                }

                // validate conditionals
                if (config.Conditionals != null)
                {
                    currentStep = "Conditionals";
                    foreach (Conditional condition in config.Conditionals)
                    {
                        if (condition.Item < -1)
                        {
                            this.Monitor.Log($"   Skipped {condition}, references null item.", LogLevel.Error);
                            continue;
                        }

                        if (condition.Amount < 1)
                        {
                            this.Monitor.Log($"   Skipped {condition}, item amount can't be less then 1.", LogLevel.Error);
                            continue;
                        }

                        if (!this.AddedConditionNames.Add(condition.Name))
                        {
                            this.Monitor.Log($"   Skipped {condition.Name}, another condition with this name already exists.", LogLevel.Error);
                            continue;
                        }

                        data.Conditionals.Add(condition);
                    }
                }

                // validate minecarts
                if (config.Teleporters != null)
                {
                    currentStep = "Teleporters";
                    foreach (TeleporterList list in config.Teleporters)
                    {
                        bool valid = true;
                        foreach (TeleporterList prevList in this.AddedTeleporters)
                        {
                            if (prevList.ListName == list.ListName)
                            {
                                valid = false;
                                foreach (TeleporterDestination dest in list.Destinations)
                                {
                                    if (prevList.Destinations.TrueForAll(a => !a.Equals(dest)))
                                    {
                                        prevList.Destinations.Add(dest);
                                    }
                                    else
                                    {
                                        this.Monitor.Log($"   Can't add teleporter destination for the `{list.ListName}` teleporter, the destination already exists: `{dest}`.", LogLevel.Error);
                                    }
                                }
                                this.Monitor.Log($"   Teleporter updated: {prevList}", LogLevel.Trace);
                                break;
                            }
                        }
                        if (valid)
                        {
                            this.AddedTeleporters.Add(list);
                            this.Monitor.Log($"   Teleporter created: {list}", LogLevel.Trace);
                        }
                    }
                }

                // validate shops
                if (config.Shops != null)
                {
                    currentStep = "Shops";
                    foreach (string shop in config.Shops)
                    {
                        try
                        {
                            ShopConfig shopConfig = contentPack.ReadJsonFile <ShopConfig>($"{shop}.json");
                            if (shopConfig == null)
                            {
                                this.Monitor.Log($"   Skipped shop '{shop}.json': file does not exist.", LogLevel.Error);
                                continue;
                            }
                            shopConfig.Name = shop;
                            if (!string.IsNullOrWhiteSpace(shopConfig.Portrait))
                            {
                                try
                                {
                                    shopConfig.PortraitTexture = contentPack.LoadAsset <Texture2D>(shopConfig.Portrait);
                                }
                                catch (Exception ex)
                                {
                                    this.Monitor.Log($"Can't load texture '{shopConfig.Portrait}' from content pack {contentPack.Manifest.Name}.", LogLevel.Error);
                                    this.Monitor.Log(ex.ToString());
                                }
                            }

                            data.Shops.Add(shopConfig);
                        }
                        catch (Exception ex)
                        {
                            this.Monitor.Log($"   Skipped shop '{shop}.json': unexpected error parsing file.", LogLevel.Error, ex);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this.Monitor.Log($"   Failed validating config (step: {currentStep}).", LogLevel.Error, ex);
            }

            return(data);
        }
示例#6
0
            /// <summary>Checks whether a config file should be used with the currently loaded farm.</summary>
            /// <param name="config">The FarmConfig to be checked.</param>
            /// <param name="pack">The content pack associated with this file, if any.</param>
            /// <returns>True if the file should be used with the current farm; false otherwise.</returns>
            public static bool CheckFileConditions(FarmConfig config, IContentPack pack)
            {
                Monitor.Log("Checking file conditions...", LogLevel.Trace);

                //check farm type
                if (config.File_Conditions.FarmTypes != null && config.File_Conditions.FarmTypes.Length > 0)
                {
                    Monitor.Log("Farm type condition(s) found. Checking...", LogLevel.Trace);

                    bool validType = false;

                    foreach (object obj in config.File_Conditions.FarmTypes) //for each listed farm type
                    {
                        int type = -1;

                        //parse the farm type object into an integer (int type)
                        if (obj is long || obj is int)                                                                                            //if the object is a readable integer
                        {
                            type = Convert.ToInt32(obj);                                                                                          //convert it to a 32-bit integer and use it
                        }
                        else if (obj is string name)                                                                                              //if the object is a string, cast it as one
                        {
                            string farmTypeID = Helper.Reflection.GetMethod(typeof(Game1), "GetFarmTypeID", false)?.Invoke <string>();            //get the farm type ID string (null in SDV 1.5.4 or older)

                            if (name.Equals("All", StringComparison.OrdinalIgnoreCase) || name.Equals("Any", StringComparison.OrdinalIgnoreCase)) //if this is "all" or "any"
                            {
                                validType = true;
                                break;                                                            //skip checking the rest of the farm types
                            }
                            else if (name.Equals(farmTypeID, StringComparison.OrdinalIgnoreCase)) //if this is the name of the player's SDV-supported custom farm type (SDV 1.5.5 or newer)
                            {
                                validType = true;
                                break;                                                  //skip checking the rest of the farm types
                            }
                            else if (Enum.TryParse(name, true, out FarmTypes farmType)) //if this name can be parsed into a FarmTypes enum
                            {
                                type = (int)farmType;                                   //use it as an integer
                            }
                            else //if this is a string, but not a recognized value
                            {
                                if (int.TryParse(name, out int parsed)) //if this string can be parsed as an integer
                                {
                                    type = parsed;                      //use the parsed value
                                }
                                else //if this string cannot be parsed
                                {
                                    Monitor.Log($"This setting in the Farm Types list could not be parsed: {name}", LogLevel.Debug);
                                    Monitor.Log($"The setting will be ignored. If it's intended to be a custom farm type, please use its ID number instead of its name.", LogLevel.Debug);
                                    continue; //skip to the next farm type condition
                                }
                            }
                        }

                        if (type == Game1.whichFarm) //if the parsed type matches the current farm type
                        {
                            validType = true;
                            break;                       //skip checking the rest of the farm types
                        }
                        else if (Game1.whichFarm == 200) //if this may be a MTN farm type, handle compatibility (based on MTN 2.1.0-beta8)
                        {
                            //if MTN is installed, use reflection to access its "whichFarm" equivalent

                            try
                            {
                                IModInfo modInfo = Helper.ModRegistry.Get("SgtPickles.MTN");                                                                                                          // get MTN info (null if it's not installed)
                                if (modInfo == null)                                                                                                                                                  //if MTN isn't installed
                                {
                                    continue;                                                                                                                                                         //skip to the next farm type check
                                }
                                object mod = modInfo.GetType().GetProperty("Mod", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)?.GetValue(modInfo);                           //get MTN's Mod instance
                                if (mod == null)                                                                                                                                                      //if it couldn't be accessed
                                {
                                    continue;                                                                                                                                                         //skip to the next farm type check
                                }
                                object customManager = mod.GetType().GetProperty("CustomManager", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)?.GetValue(mod);               //get CustomManager instance
                                if (customManager == null)                                                                                                                                            //if it couldn't be accessed
                                {
                                    continue;                                                                                                                                                         //skip to the next farm type check
                                }
                                object loadedFarm = customManager.GetType().GetProperty("LoadedFarm", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)?.GetValue(customManager); //get LoadedFarm instance
                                if (loadedFarm == null)                                                                                                                                               //if it couldn't be accessed
                                {
                                    continue;                                                                                                                                                         //skip to the next farm type check
                                }
                                int farmID = (int)loadedFarm.GetType().GetProperty("ID", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)?.GetValue(loadedFarm);                 //get the loaded farm's ID

                                if (type == farmID)                                                                                                                                                   //if MTN's custom farm ID matches the parsed type
                                {
                                    Monitor.VerboseLog($"Farm type matches the loaded MTN farm type's ID: {type}");
                                    validType = true;
                                    break; //skip checking the rest of the farm types
                                }
                            }
                            catch (Exception) //if any exception is thrown while accessing MTN
                            {
                                Monitor.Log($"Error encountered while trying to check MTN farm type. This check may be obsolete or require updates.", LogLevel.Trace);
                                continue; //skip to the next farm type check
                            }
                        }
                    }

                    if (validType) //if a valid farm type was listed
                    {
                        Monitor.Log("Farm type matched a setting. File allowed.", LogLevel.Trace);
                    }
                    else
                    {
                        Monitor.Log("Farm type did NOT match any settings. File disabled.", LogLevel.Trace);
                        return(false); //prevent config use
                    }
                }

                //check farmer name
                if (config.File_Conditions.FarmerNames != null && config.File_Conditions.FarmerNames.Length > 0)
                {
                    Monitor.Log("Farmer name condition(s) found. Checking...", LogLevel.Trace);

                    bool validName = false;

                    foreach (string name in config.File_Conditions.FarmerNames)                 //for each listed name
                    {
                        if (name.Equals(Game1.player.Name, StringComparison.OrdinalIgnoreCase)) //if the name matches the current player's
                        {
                            validName = true;
                            break; //skip the rest of these checks
                        }
                    }

                    if (validName) //if a valid farmer name was listed
                    {
                        Monitor.Log("Farmer name matched a setting. File allowed.", LogLevel.Trace);
                    }
                    else
                    {
                        Monitor.Log("Farmer name did NOT match any settings. File disabled.", LogLevel.Trace);
                        return(false); //prevent config use
                    }
                }

                //check save file names (technically the save folder name)
                if (config.File_Conditions.SaveFileNames != null && config.File_Conditions.SaveFileNames.Length > 0)
                {
                    Monitor.Log("Save file name condition(s) found. Checking...", LogLevel.Trace);

                    bool validSave = false;

                    foreach (string saveName in config.File_Conditions.SaveFileNames)                      //for each listed save name
                    {
                        if (saveName.Equals(Constants.SaveFolderName, StringComparison.OrdinalIgnoreCase)) //if the name matches the current player's save folder name
                        {
                            validSave = true;
                            break; //skip the rest of these checks
                        }
                    }

                    if (validSave) //if a valid save name was listed
                    {
                        Monitor.Log("Save file name matched a setting. File allowed.", LogLevel.Trace);
                    }
                    else
                    {
                        Monitor.Log("Save file name did NOT match any settings. File disabled.", LogLevel.Trace);
                        return(false); //prevent config use
                    }
                }

                //check whether other mods exist
                if (config.File_Conditions.OtherMods != null && config.File_Conditions.OtherMods.Count > 0)
                {
                    Monitor.Log("Other mod condition(s) found. Checking...", LogLevel.Trace);

                    bool validMods = true;                                                                       //whether all entries are accurate (true by default, unlike most other settings)

                    foreach (KeyValuePair <string, bool> entry in config.File_Conditions.OtherMods)              //for each mod entry in OtherMods
                    {
                        bool validEntry = !(entry.Value);                                                        //whether the current entry is accurate (starts false if the mod should exist; starts true if the mod should NOT exist)

                        foreach (IModInfo mod in Helper.ModRegistry.GetAll())                                    //for each mod currently loaded by SMAPI
                        {
                            if (entry.Value == true)                                                             //if the mod should exist
                            {
                                if (entry.Key.Equals(mod.Manifest.UniqueID, StringComparison.OrdinalIgnoreCase)) //if this mod's UniqueID matches the OtherMods entry
                                {
                                    validEntry = true;                                                           //this entry is valid
                                    break;                                                                       //skip the rest of these checks
                                }
                            }
                            else //if the mod should NOT exist
                            {
                                if (entry.Key.Equals(mod.Manifest.UniqueID, StringComparison.OrdinalIgnoreCase)) //if this mod's UniqueID matches the OtherMods entry
                                {
                                    validEntry = false; //this entry is invalid
                                    break;              //skip the rest of these checks
                                }
                            }
                        }

                        if (validEntry) //if the current mod entry is valid
                        {
                            Monitor.Log($"Mod check successful: \"{entry.Key}\" {(entry.Value ? "does exist" : "does not exist")}.", LogLevel.Trace);
                        }
                        else //if the current mod entry is NOT valid
                        {
                            Monitor.Log($"Mod check failed: \"{entry.Key}\" {(entry.Value ? "does not exist" : "does exist")}.", LogLevel.Trace);
                            validMods = false;
                            break; //skip the rest of these checks
                        }
                    }

                    if (validMods) //if all mod entries in the list are valid
                    {
                        Monitor.Log("The OtherMods list matches the player's mods. File allowed.", LogLevel.Trace);
                    }
                    else //if any entries were NOT valid
                    {
                        Monitor.Log("The OtherMods list does NOT match the player's mods. File disabled.", LogLevel.Trace);
                        return(false); //prevent config use
                    }
                }

                return(true); //all checks were successful; config should be used
            }
示例#7
0
        private void loadData(IContentPack contentPack)
        {
            Log.info($"\t{contentPack.Manifest.Name} {contentPack.Manifest.Version} by {contentPack.Manifest.Author} - {contentPack.Manifest.Description}");

            // load objects
            DirectoryInfo objectsDir = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "Objects"));

            if (objectsDir.Exists)
            {
                foreach (DirectoryInfo dir in objectsDir.EnumerateDirectories())
                {
                    string relativePath = $"Objects/{dir.Name}";

                    // load data
                    ObjectData obj = contentPack.ReadJsonFile <ObjectData>($"{relativePath}/object.json");
                    if (obj == null)
                    {
                        continue;
                    }

                    // save object
                    obj.texture = contentPack.LoadAsset <Texture2D>($"{relativePath}/object.png");
                    if (obj.IsColored)
                    {
                        obj.textureColor = contentPack.LoadAsset <Texture2D>($"{relativePath}/color.png");
                    }
                    this.objects.Add(obj);

                    // save ring
                    if (obj.Category == ObjectData.Category_.Ring)
                    {
                        this.myRings.Add(obj);
                    }
                }
            }

            // load crops
            DirectoryInfo cropsDir = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "Crops"));

            if (cropsDir.Exists)
            {
                foreach (DirectoryInfo dir in cropsDir.EnumerateDirectories())
                {
                    string relativePath = $"Crops/{dir.Name}";

                    // load data
                    CropData crop = contentPack.ReadJsonFile <CropData>($"{relativePath}/crop.json");
                    if (crop == null)
                    {
                        continue;
                    }

                    // save crop
                    crop.texture = contentPack.LoadAsset <Texture2D>($"{relativePath}/crop.png");
                    crops.Add(crop);

                    // save seeds
                    crop.seed = new ObjectData
                    {
                        texture                 = contentPack.LoadAsset <Texture2D>($"{relativePath}/seeds.png"),
                        Name                    = crop.SeedName,
                        Description             = crop.SeedDescription,
                        Category                = ObjectData.Category_.Seeds,
                        Price                   = crop.SeedPurchasePrice,
                        CanPurchase             = true,
                        PurchaseFrom            = crop.SeedPurchaseFrom,
                        PurchasePrice           = crop.SeedPurchasePrice,
                        PurchaseRequirements    = crop.SeedPurchaseRequirements ?? new List <string>(),
                        NameLocalization        = crop.SeedNameLocalization,
                        DescriptionLocalization = crop.SeedDescriptionLocalization
                    };

                    // TODO: Clean up this chunk
                    // I copy/pasted it from the unofficial update decompiled
                    string   str   = "";
                    string[] array = new[] { "spring", "summer", "fall", "winter" }
                    .Except(crop.Seasons)
                    .ToArray();
                    foreach (var season in array)
                    {
                        str += $"/z {season}";
                    }
                    string strtrimstart = str.TrimStart(new char[] { '/' });
                    if (crop.SeedPurchaseRequirements != null && crop.SeedPurchaseRequirements.Count > 0)
                    {
                        for (int index = 0; index < crop.SeedPurchaseRequirements.Count; index++)
                        {
                            if (SeasonLimiter.IsMatch(crop.SeedPurchaseRequirements[index]))
                            {
                                crop.SeedPurchaseRequirements[index] = strtrimstart;
                                Log.warn($"        Faulty season requirements for {crop.SeedName}!\n        Fixed season requirements: {crop.SeedPurchaseRequirements[index]}");
                            }
                        }
                        if (!crop.SeedPurchaseRequirements.Contains(str.TrimStart('/')))
                        {
                            Log.trace($"        Adding season requirements for {crop.SeedName}:\n        New season requirements: {strtrimstart}");
                            crop.seed.PurchaseRequirements.Add(strtrimstart);
                        }
                    }
                    else
                    {
                        Log.trace($"        Adding season requirements for {crop.SeedName}:\n        New season requirements: {strtrimstart}");
                        crop.seed.PurchaseRequirements.Add(strtrimstart);
                    }

                    objects.Add(crop.seed);
                }
            }

            // load fruit trees
            DirectoryInfo fruitTreesDir = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "FruitTrees"));

            if (fruitTreesDir.Exists)
            {
                foreach (DirectoryInfo dir in fruitTreesDir.EnumerateDirectories())
                {
                    string relativePath = $"FruitTrees/{dir.Name}";

                    // load data
                    FruitTreeData tree = contentPack.ReadJsonFile <FruitTreeData>($"{relativePath}/tree.json");
                    if (tree == null)
                    {
                        continue;
                    }

                    // save fruit tree
                    tree.texture = contentPack.LoadAsset <Texture2D>($"{relativePath}/tree.png");
                    fruitTrees.Add(tree);

                    // save seed
                    tree.sapling = new ObjectData
                    {
                        texture                 = contentPack.LoadAsset <Texture2D>($"{relativePath}/sapling.png"),
                        Name                    = tree.SaplingName,
                        Description             = tree.SaplingDescription,
                        Category                = ObjectData.Category_.Seeds,
                        Price                   = tree.SaplingPurchasePrice,
                        CanPurchase             = true,
                        PurchaseRequirements    = tree.SaplingPurchaseRequirements,
                        PurchaseFrom            = tree.SaplingPurchaseFrom,
                        PurchasePrice           = tree.SaplingPurchasePrice,
                        NameLocalization        = tree.SaplingNameLocalization,
                        DescriptionLocalization = tree.SaplingDescriptionLocalization
                    };
                    objects.Add(tree.sapling);
                }
            }

            // load big craftables
            DirectoryInfo bigCraftablesDir = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "BigCraftables"));

            if (bigCraftablesDir.Exists)
            {
                foreach (DirectoryInfo dir in bigCraftablesDir.EnumerateDirectories())
                {
                    string relativePath = $"BigCraftables/{dir.Name}";

                    // load data
                    BigCraftableData craftable = contentPack.ReadJsonFile <BigCraftableData>($"{relativePath}/big-craftable.json");
                    if (craftable == null)
                    {
                        continue;
                    }

                    // save craftable
                    craftable.texture = contentPack.LoadAsset <Texture2D>($"{relativePath}/big-craftable.png");
                    bigCraftables.Add(craftable);
                }
            }

            // load hats
            DirectoryInfo hatsDir = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "Hats"));

            if (hatsDir.Exists)
            {
                foreach (DirectoryInfo dir in hatsDir.EnumerateDirectories())
                {
                    string relativePath = $"Hats/{dir.Name}";

                    // load data
                    HatData hat = contentPack.ReadJsonFile <HatData>($"{relativePath}/hat.json");
                    if (hat == null)
                    {
                        continue;
                    }

                    // save object
                    hat.texture = contentPack.LoadAsset <Texture2D>($"{relativePath}/hat.png");
                    hats.Add(hat);
                }
            }

            // Load weapons
            // load objects
            DirectoryInfo weaponsDir = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "Weapons"));

            if (weaponsDir.Exists)
            {
                foreach (DirectoryInfo dir in weaponsDir.EnumerateDirectories())
                {
                    string relativePath = $"Weapons/{dir.Name}";

                    // load data
                    WeaponData weapon = contentPack.ReadJsonFile <WeaponData>($"{relativePath}/weapon.json");
                    if (weapon == null)
                    {
                        continue;
                    }

                    // save object
                    weapon.texture = contentPack.LoadAsset <Texture2D>($"{relativePath}/weapon.png");
                    weapons.Add(weapon);
                }
            }
        }
示例#8
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="path">The path to the patch from the root content file.</param>
        /// <param name="assetName">The normalized asset name to intercept.</param>
        /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
        /// <param name="fromFile">The normalized asset key from which to load entries (if applicable), including tokens.</param>
        /// <param name="records">The data records to edit.</param>
        /// <param name="fields">The data fields to edit.</param>
        /// <param name="moveRecords">The records to reorder, if the target is a list asset.</param>
        /// <param name="textOperations">The text operations to apply to existing values.</param>
        /// <param name="updateRate">When the patch should be updated.</param>
        /// <param name="contentPack">The content pack which requested the patch.</param>
        /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        /// <param name="normalizeAssetName">Normalize an asset name.</param>
        /// <param name="tryParseFields">Parse the data change fields for an <see cref="PatchType.EditData"/> patch.</param>
        public EditDataPatch(LogPathBuilder path, IManagedTokenString assetName, IEnumerable <Condition> conditions, IManagedTokenString fromFile, IEnumerable <EditDataPatchRecord> records, IEnumerable <EditDataPatchField> fields, IEnumerable <EditDataPatchMoveRecord> moveRecords, IEnumerable <TextOperation> textOperations, UpdateRate updateRate, IContentPack contentPack, IPatch parentPatch, IMonitor monitor, Func <string, string> normalizeAssetName, TryParseFieldsDelegate tryParseFields)
            : base(
                path: path,
                type: PatchType.EditData,
                assetName: assetName,
                conditions: conditions,
                updateRate: updateRate,
                contentPack: contentPack,
                parentPatch: parentPatch,
                normalizeAssetName: normalizeAssetName,
                fromAsset: fromFile
                )
        {
            // set fields
            this.Records        = records?.ToArray();
            this.Fields         = fields?.ToArray();
            this.MoveRecords    = moveRecords?.ToArray();
            this.TextOperations = textOperations?.ToArray() ?? new TextOperation[0];
            this.Monitor        = monitor;
            this.TryParseFields = tryParseFields;

            // track contextuals
            this.Contextuals
            .Add(this.Records)
            .Add(this.Fields)
            .Add(this.MoveRecords)
            .Add(this.TextOperations)
            .Add(this.Conditions);
        }
示例#9
0
        /// <summary>Get the local value providers with which to initialize a local context.</summary>
        /// <param name="contentPack">The content pack for which to get tokens.</param>
        private IEnumerable <IValueProvider> GetLocalValueProviders(IContentPack contentPack)
        {
            yield return(new HasFileValueProvider(contentPack.DirectoryPath));

            yield return(new RandomValueProvider()); // per-pack for more reproducible selection when troubleshooting
        }
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="indexPath">The path of indexes from the root <c>content.json</c> to this patch; see <see cref="IPatch.IndexPath"/>.</param>
        /// <param name="path">The path to the patch from the root content file.</param>
        /// <param name="assetName">The normalized asset name to intercept.</param>
        /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
        /// <param name="fromAsset">The asset key to load from the content pack instead.</param>
        /// <param name="fromArea">The sprite area from which to read an image.</param>
        /// <param name="toArea">The sprite area to overwrite.</param>
        /// <param name="patchMode">Indicates how the image should be patched.</param>
        /// <param name="updateRate">When the patch should be updated.</param>
        /// <param name="contentPack">The content pack which requested the patch.</param>
        /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        /// <param name="normalizeAssetName">Normalize an asset name.</param>
        public EditImagePatch(int[] indexPath, LogPathBuilder path, IManagedTokenString assetName, IEnumerable <Condition> conditions, IManagedTokenString fromAsset, TokenRectangle fromArea, TokenRectangle toArea, PatchImageMode patchMode, UpdateRate updateRate, IContentPack contentPack, IPatch parentPatch, IMonitor monitor, Func <string, string> normalizeAssetName)
            : base(
                indexPath: indexPath,
                path: path,
                type: PatchType.EditImage,
                assetName: assetName,
                conditions: conditions,
                normalizeAssetName: normalizeAssetName,
                fromAsset: fromAsset,
                updateRate: updateRate,
                contentPack: contentPack,
                parentPatch: parentPatch
                )
        {
            this.FromArea  = fromArea;
            this.ToArea    = toArea;
            this.PatchMode = patchMode;
            this.Monitor   = monitor;

            this.Contextuals
            .Add(fromArea)
            .Add(toArea);
        }
示例#11
0
        internal static void ApplyTilesheet(IContentHelper coreContentHelper, IContentPack contentPack, Tilesheet tilesheet)
        {
            int stage  = 0;
            int branch = 0;
            int skip   = 0;

            try
            {
                stage++; // 1
                GameLocation location = Game1.getLocationFromName(tilesheet.MapName);
                stage++; // 2
                if (tilesheet.FileName == null)
                {
                    skip = 1;
                }
                else
                {
                    skip = 2;
                    string fakepath = Path.Combine("AdvancedLocationLoader/FakePath_paths_objects", tilesheet.FileName);
                    if (tilesheet.Seasonal)
                    {
                        fakepath = fakepath.Replace("all_sheet_paths_objects", Path.Combine("all_sheet_paths_objects", Game1.currentSeason));
                    }
                    stage++; // 3
                    if (!_MappingCache.Contains(tilesheet.FileName))
                    {
                        string toAssetPath = contentPack.GetRelativePath(
                            fromAbsolutePath: ModEntry.SHelper.DirectoryPath,
                            toLocalPath: tilesheet.Seasonal ? ($"{tilesheet.FileName}_{Game1.currentSeason}") : tilesheet.FileName
                            );
                        coreContentHelper.RegisterXnbReplacement(fakepath, toAssetPath);
                        _MappingCache.Add(tilesheet.FileName);
                    }
                    stage++; // 4
                    if (location.map.GetTileSheet(tilesheet.SheetId) != null)
                    {
                        branch = 1;
                        location.map.GetTileSheet(tilesheet.SheetId).ImageSource = fakepath;
                    }
                    else
                    {
                        branch = 2;
                        Texture2D sheet = Game1.content.Load <Texture2D>(fakepath);
                        location.map.AddTileSheet(new xTile.Tiles.TileSheet(tilesheet.SheetId, location.map, fakepath, new xTile.Dimensions.Size((int)Math.Ceiling(sheet.Width / 16.0), (int)Math.Ceiling(sheet.Height / 16.0)), new xTile.Dimensions.Size(16, 16)));
                    }
                }
                stage++; // 5 (skip 3)
                if (tilesheet.Properties.Count > 0)
                {
                    xTile.Tiles.TileSheet sheet = location.map.GetTileSheet(tilesheet.SheetId);
                    foreach (string prop in tilesheet.Properties.Keys)
                    {
                        sheet.Properties[prop] = tilesheet.Properties[prop];
                    }
                }
            }
            catch (Exception err)
            {
                ModEntry.Logger.ExitGameImmediately($"Unable to patch tilesheet, a unexpected error occured at stage {stage}{(skip > 0 ? ("-" + skip) : "")}{(branch > 0 ? ("-" + branch) : "")}: {tilesheet}", err);
            }
        }
示例#12
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="indexPath">The path of indexes from the root <c>content.json</c> to this patch; see <see cref="IPatch.IndexPath"/>.</param>
        /// <param name="path">The path to the patch from the root content file.</param>
        /// <param name="assetName">The normalized asset name to intercept.</param>
        /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
        /// <param name="fromAsset">The asset key to load from the content pack instead.</param>
        /// <param name="fromArea">The map area from which to read tiles.</param>
        /// <param name="patchMode">Indicates how the map should be patched.</param>
        /// <param name="toArea">The map area to overwrite.</param>
        /// <param name="mapProperties">The map properties to change when editing a map, if any.</param>
        /// <param name="mapTiles">The map tiles to change when editing a map.</param>
        /// <param name="addWarps">The warps to add to the location.</param>
        /// <param name="textOperations">The text operations to apply to existing values.</param>
        /// <param name="updateRate">When the patch should be updated.</param>
        /// <param name="contentPack">The content pack which requested the patch.</param>
        /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        /// <param name="reflection">Simplifies access to private code.</param>
        /// <param name="normalizeAssetName">Normalize an asset name.</param>
        public EditMapPatch(int[] indexPath, LogPathBuilder path, IManagedTokenString assetName, IEnumerable <Condition> conditions, IManagedTokenString fromAsset, TokenRectangle fromArea, TokenRectangle toArea, PatchMapMode patchMode, IEnumerable <EditMapPatchProperty> mapProperties, IEnumerable <EditMapPatchTile> mapTiles, IEnumerable <IManagedTokenString> addWarps, IEnumerable <TextOperation> textOperations, UpdateRate updateRate, IContentPack contentPack, IPatch parentPatch, IMonitor monitor, IReflectionHelper reflection, Func <string, string> normalizeAssetName)
            : base(
                indexPath: indexPath,
                path: path,
                type: PatchType.EditMap,
                assetName: assetName,
                fromAsset: fromAsset,
                conditions: conditions,
                updateRate: updateRate,
                contentPack: contentPack,
                parentPatch: parentPatch,
                normalizeAssetName: normalizeAssetName
                )
        {
            this.FromArea       = fromArea;
            this.ToArea         = toArea;
            this.PatchMode      = patchMode;
            this.MapProperties  = mapProperties?.ToArray() ?? new EditMapPatchProperty[0];
            this.MapTiles       = mapTiles?.ToArray() ?? new EditMapPatchTile[0];
            this.AddWarps       = addWarps?.Reverse().ToArray() ?? new IManagedTokenString[0]; // reversing the warps allows later ones to 'overwrite' earlier ones, since the game checks them in the listed order
            this.TextOperations = textOperations?.ToArray() ?? new TextOperation[0];
            this.Monitor        = monitor;
            this.Reflection     = reflection;

            this.Contextuals
            .Add(this.FromArea)
            .Add(this.ToArea)
            .Add(this.MapProperties)
            .Add(this.MapTiles)
            .Add(this.AddWarps)
            .Add(this.TextOperations);
        }
示例#13
0
            /// <summary>Checks whether a config file should be used with the currently loaded farm.</summary>
            /// <param name="config">The FarmConfig to be checked.</param>
            /// <param name="pack">The content pack associated with this file, if any.</param>
            /// <returns>True if the file should be used with the current farm; false otherwise.</returns>
            public static bool CheckFileConditions(FarmConfig config, IContentPack pack)
            {
                Monitor.Log("Checking file conditions...", LogLevel.Trace);

                //check "reset main data folder" flag
                //NOTE: it's preferable to do this as the first step; it's intended to be a one-off cleaning process, rather than a conditional effect
                if (config.File_Conditions.ResetMainDataFolder && MConfig.EnableContentPackFileChanges) //if "reset" is true and file changes are enabled
                {
                    if (pack != null)                                                                   //if this is part of a content pack
                    {
                        //attempt to load the content pack's global save data
                        ContentPackSaveData packSave = null;
                        try
                        {
                            packSave = pack.ReadJsonFile <ContentPackSaveData>(Path.Combine("data", "ContentPackSaveData.save")); //load the content pack's global save data (null if it doesn't exist)
                        }
                        catch (Exception ex)
                        {
                            Monitor.Log($"Warning: This content pack's save data could not be parsed correctly: {pack.Manifest.Name}", LogLevel.Warn);
                            Monitor.Log($"Affected file: data/ContentPackSaveData.save", LogLevel.Warn);
                            Monitor.Log($"Please delete the file and/or contact the mod's developer.", LogLevel.Warn);
                            Monitor.Log($"The content pack will be skipped until this issue is fixed. The auto-generated error message is displayed below:", LogLevel.Warn);
                            Monitor.Log($"----------", LogLevel.Warn);
                            Monitor.Log($"{ex.Message}", LogLevel.Warn);
                            return(false); //disable this content pack's config, since it may require this process to function
                        }

                        if (packSave == null) //no global save data exists for this content pack
                        {
                            packSave = new ContentPackSaveData();
                        }

                        if (!packSave.MainDataFolderReset) //if this content pack has NOT reset the main data folder yet
                        {
                            Monitor.Log($"ResetMainDataFolder requested by content pack: {pack.Manifest.Name}", LogLevel.Debug);
                            string        dataPath   = Path.Combine(Helper.DirectoryPath, "data"); //the path to this mod's data folder
                            DirectoryInfo dataFolder = new DirectoryInfo(dataPath);                //an object representing this mod's data directory

                            if (dataFolder.Exists)                                                 //the data folder exists
                            {
                                Monitor.Log("Attempting to archive data folder...", LogLevel.Trace);
                                try
                                {
                                    string        archivePath   = Path.Combine(Helper.DirectoryPath, "data", "archive", DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss"));
                                    DirectoryInfo archiveFolder = Directory.CreateDirectory(archivePath); //create a timestamped archive folder
                                    foreach (FileInfo file in dataFolder.GetFiles())                      //for each file in dataFolder
                                    {
                                        file.MoveTo(Path.Combine(archiveFolder.FullName, file.Name));     //move each file to archiveFolder
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Monitor.Log($"Warning: This content pack attempted to archive Farm Type Manager's data folder but failed: {pack.Manifest.Name}", LogLevel.Warn);
                                    Monitor.Log($"Please report this issue to Farm Type Manager's developer. This might also be fixed by manually removing your FarmTypeManager/data/ files.", LogLevel.Warn);
                                    Monitor.Log($"The content pack will be skipped until this issue is fixed. The auto-generated error message is displayed below:", LogLevel.Warn);
                                    Monitor.Log($"----------", LogLevel.Warn);
                                    Monitor.Log($"{ex.Message}", LogLevel.Warn);
                                    return(false); //disable this content pack's config, since it may require this process to function
                                }
                            }
                            else //the data folder doesn't exist
                            {
                                Monitor.Log("Data folder not found; assuming it was deleted or not yet generated.", LogLevel.Trace);
                            }

                            packSave.MainDataFolderReset = true; //update save data
                        }

                        pack.WriteJsonFile(Path.Combine("data", "ContentPackSaveData.save"), packSave); //update the content pack's global save data file
                        Monitor.Log("Data folder archive successful.", LogLevel.Trace);
                    }
                    else //if this is NOT part of a content pack
                    {
                        Monitor.Log("This farm's config file has ResetMainDataFolder = true, but this setting only works for content packs.", LogLevel.Info);
                    }
                }

                //check farm type
                if (config.File_Conditions.FarmTypes != null && config.File_Conditions.FarmTypes.Length > 0)
                {
                    Monitor.Log("Farm type condition(s) found. Checking...", LogLevel.Trace);

                    bool validType = false;

                    foreach (object obj in config.File_Conditions.FarmTypes) //for each listed farm type
                    {
                        int type = -1;

                        //parse the farm type object into an integer (int type)
                        if (obj is long || obj is int)                                                                                            //if the object is a readable integer
                        {
                            type = Convert.ToInt32(obj);                                                                                          //convert it to a 32-bit integer and use it
                        }
                        else if (obj is string name)                                                                                              //if the object is a string, cast it as one
                        {
                            if (name.Equals("All", StringComparison.OrdinalIgnoreCase) || name.Equals("Any", StringComparison.OrdinalIgnoreCase)) //if this is "all" or "any"
                            {
                                validType = true;
                                break;                                                  //skip checking the rest of the farm types
                            }
                            else if (Enum.TryParse(name, true, out FarmTypes farmType)) //if this name can be parsed into a FarmTypes enum
                            {
                                type = (int)farmType;                                   //use it as an integer
                            }
                            else //if this is a string, but not a recognized value
                            {
                                if (int.TryParse(name, out int parsed)) //if this string can be parsed as an integer
                                {
                                    type = parsed;                      //use the parsed value
                                }
                                else //if this string cannot be parsed
                                {
                                    Monitor.Log($"This setting in the Farm Types list could not be parsed: {name}", LogLevel.Debug);
                                    Monitor.Log($"The setting will be ignored. If it's intended to be a custom farm type, please use its ID number instead of its name.", LogLevel.Debug);
                                    continue; //skip to the next farm type condition
                                }
                            }
                        }

                        if (type == Game1.whichFarm) //if the parsed type matches the current farm type
                        {
                            validType = true;
                            break;                       //skip checking the rest of the farm types
                        }
                        else if (Game1.whichFarm == 200) //if this may be a MTN farm type, handle compatibility (based on MTN 2.1.0-beta8)
                        {
                            //if MTN is installed, use reflection to access its "whichFarm" equivalent

                            try
                            {
                                IModInfo modInfo = Helper.ModRegistry.Get("SgtPickles.MTN");                                                                                                          // get MTN info (null if it's not installed)
                                if (modInfo == null)                                                                                                                                                  //if MTN isn't installed
                                {
                                    continue;                                                                                                                                                         //skip to the next farm type check
                                }
                                object mod = modInfo.GetType().GetProperty("Mod", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)?.GetValue(modInfo);                           //get MTN's Mod instance
                                if (mod == null)                                                                                                                                                      //if it couldn't be accessed
                                {
                                    continue;                                                                                                                                                         //skip to the next farm type check
                                }
                                object customManager = mod.GetType().GetProperty("CustomManager", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)?.GetValue(mod);               //get CustomManager instance
                                if (customManager == null)                                                                                                                                            //if it couldn't be accessed
                                {
                                    continue;                                                                                                                                                         //skip to the next farm type check
                                }
                                object loadedFarm = customManager.GetType().GetProperty("LoadedFarm", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)?.GetValue(customManager); //get LoadedFarm instance
                                if (loadedFarm == null)                                                                                                                                               //if it couldn't be accessed
                                {
                                    continue;                                                                                                                                                         //skip to the next farm type check
                                }
                                int farmID = (int)loadedFarm.GetType().GetProperty("ID", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)?.GetValue(loadedFarm);                 //get the loaded farm's ID

                                if (type == farmID)                                                                                                                                                   //if MTN's custom farm ID matches the parsed type
                                {
                                    Monitor.VerboseLog($"Farm type matches the loaded MTN farm type's ID: {type}");
                                    validType = true;
                                    break; //skip checking the rest of the farm types
                                }
                            }
                            catch (Exception) //if any exception is thrown while accessing MTN
                            {
                                Monitor.Log($"Error encountered while trying to check MTN farm type. This check may be obsolete or require updates.", LogLevel.Trace);
                                continue; //skip to the next farm type check
                            }
                        }
                    }

                    if (validType) //if a valid farm type was listed
                    {
                        Monitor.Log("Farm type matched a setting. File allowed.", LogLevel.Trace);
                    }
                    else
                    {
                        Monitor.Log("Farm type did NOT match any settings. File disabled.", LogLevel.Trace);
                        return(false); //prevent config use
                    }
                }

                //check farmer name
                if (config.File_Conditions.FarmerNames != null && config.File_Conditions.FarmerNames.Length > 0)
                {
                    Monitor.Log("Farmer name condition(s) found. Checking...", LogLevel.Trace);

                    bool validName = false;

                    foreach (string name in config.File_Conditions.FarmerNames)                 //for each listed name
                    {
                        if (name.Equals(Game1.player.Name, StringComparison.OrdinalIgnoreCase)) //if the name matches the current player's
                        {
                            validName = true;
                            break; //skip the rest of these checks
                        }
                    }

                    if (validName) //if a valid farmer name was listed
                    {
                        Monitor.Log("Farmer name matched a setting. File allowed.", LogLevel.Trace);
                    }
                    else
                    {
                        Monitor.Log("Farmer name did NOT match any settings. File disabled.", LogLevel.Trace);
                        return(false); //prevent config use
                    }
                }

                //check save file names (technically the save folder name)
                if (config.File_Conditions.SaveFileNames != null && config.File_Conditions.SaveFileNames.Length > 0)
                {
                    Monitor.Log("Save file name condition(s) found. Checking...", LogLevel.Trace);

                    bool validSave = false;

                    foreach (string saveName in config.File_Conditions.SaveFileNames)                      //for each listed save name
                    {
                        if (saveName.Equals(Constants.SaveFolderName, StringComparison.OrdinalIgnoreCase)) //if the name matches the current player's save folder name
                        {
                            validSave = true;
                            break; //skip the rest of these checks
                        }
                    }

                    if (validSave) //if a valid save name was listed
                    {
                        Monitor.Log("Save file name matched a setting. File allowed.", LogLevel.Trace);
                    }
                    else
                    {
                        Monitor.Log("Save file name did NOT match any settings. File disabled.", LogLevel.Trace);
                        return(false); //prevent config use
                    }
                }

                //check whether other mods exist
                if (config.File_Conditions.OtherMods != null && config.File_Conditions.OtherMods.Count > 0)
                {
                    Monitor.Log("Other mod condition(s) found. Checking...", LogLevel.Trace);

                    bool validMods = true;                                                                       //whether all entries are accurate (true by default, unlike most other settings)

                    foreach (KeyValuePair <string, bool> entry in config.File_Conditions.OtherMods)              //for each mod entry in OtherMods
                    {
                        bool validEntry = !(entry.Value);                                                        //whether the current entry is accurate (starts false if the mod should exist; starts true if the mod should NOT exist)

                        foreach (IModInfo mod in Helper.ModRegistry.GetAll())                                    //for each mod currently loaded by SMAPI
                        {
                            if (entry.Value == true)                                                             //if the mod should exist
                            {
                                if (entry.Key.Equals(mod.Manifest.UniqueID, StringComparison.OrdinalIgnoreCase)) //if this mod's UniqueID matches the OtherMods entry
                                {
                                    validEntry = true;                                                           //this entry is valid
                                    break;                                                                       //skip the rest of these checks
                                }
                            }
                            else //if the mod should NOT exist
                            {
                                if (entry.Key.Equals(mod.Manifest.UniqueID, StringComparison.OrdinalIgnoreCase)) //if this mod's UniqueID matches the OtherMods entry
                                {
                                    validEntry = false; //this entry is invalid
                                    break;              //skip the rest of these checks
                                }
                            }
                        }

                        if (validEntry) //if the current mod entry is valid
                        {
                            Monitor.Log($"Mod check successful: \"{entry.Key}\" {(entry.Value ? "does exist" : "does not exist")}.", LogLevel.Trace);
                        }
                        else //if the current mod entry is NOT valid
                        {
                            Monitor.Log($"Mod check failed: \"{entry.Key}\" {(entry.Value ? "does not exist" : "does exist")}.", LogLevel.Trace);
                            validMods = false;
                            break; //skip the rest of these checks
                        }
                    }

                    if (validMods) //if all mod entries in the list are valid
                    {
                        Monitor.Log("The OtherMods list matches the player's mods. File allowed.", LogLevel.Trace);
                    }
                    else //if any entries were NOT valid
                    {
                        Monitor.Log("The OtherMods list does NOT match the player's mods. File disabled.", LogLevel.Trace);
                        return(false); //prevent config use
                    }
                }

                return(true); //all checks were successful; config should be used
            }
示例#14
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="contentPack">The underlying content pack.</param>
 public MusicPack(IContentPack contentPack)
 {
     this.ContentPack   = contentPack;
     this.playingSounds = new Dictionary <string, List <SoundEffectInstance> >();
     this.LoadMusicFiles();
 }
示例#15
0
        /*********
        ** Private methods
        *********/

        /// <summary>Queues an error.</summary>
        /// <param name="contentPack">The content pack the error is for.</param>
        /// <param name="id">An id to use when displaying the error.</param>
        /// <param name="info">The error info.</param>
        /// <param name="forIndividualDoor">Whether the error is for one door or an entire image.</param>
        private void QueueError(IContentPack contentPack, string id, string info, bool forIndividualDoor)
        {
            this.errorQueue.AddError($"{contentPack.Manifest.Name} ({contentPack.Manifest.UniqueID}) - {id} - Found an error. Info: {info}. {(forIndividualDoor ? "This door" : "Every door in this image")} won't be loaded.");
        }
示例#16
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="indexPath">The path of indexes from the root <c>content.json</c> to this patch; see <see cref="IPatch.IndexPath"/>.</param>
        /// <param name="path">The path to the patch from the root content file.</param>
        /// <param name="assetName">The normalized asset name to intercept.</param>
        /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
        /// <param name="fromAsset">The asset key to load from the content pack instead.</param>
        /// <param name="fromArea">The map area from which to read tiles.</param>
        /// <param name="patchMode">Indicates how the map should be patched.</param>
        /// <param name="toArea">The map area to overwrite.</param>
        /// <param name="mapProperties">The map properties to change when editing a map, if any.</param>
        /// <param name="mapTiles">The map tiles to change when editing a map.</param>
        /// <param name="addWarps">The warps to add to the location.</param>
        /// <param name="textOperations">The text operations to apply to existing values.</param>
        /// <param name="updateRate">When the patch should be updated.</param>
        /// <param name="contentPack">The content pack which requested the patch.</param>
        /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        /// <param name="parseAssetName">Parse an asset name.</param>
        public EditMapPatch(int[] indexPath, LogPathBuilder path, IManagedTokenString assetName, IEnumerable <Condition> conditions, IManagedTokenString?fromAsset, TokenRectangle?fromArea, TokenRectangle?toArea, PatchMapMode patchMode, IEnumerable <EditMapPatchProperty>?mapProperties, IEnumerable <EditMapPatchTile>?mapTiles, IEnumerable <IManagedTokenString>?addWarps, IEnumerable <TextOperation>?textOperations, UpdateRate updateRate, IContentPack contentPack, IPatch?parentPatch, IMonitor monitor, Func <string, IAssetName> parseAssetName)
            : base(
                indexPath: indexPath,
                path: path,
                type: PatchType.EditMap,
                assetName: assetName,
                fromAsset: fromAsset,
                conditions: conditions,
                updateRate: updateRate,
                contentPack: contentPack,
                parentPatch: parentPatch,
                parseAssetName: parseAssetName
                )
        {
            this.FromArea       = fromArea;
            this.ToArea         = toArea;
            this.PatchMode      = patchMode;
            this.MapProperties  = mapProperties?.ToArray() ?? Array.Empty <EditMapPatchProperty>();
            this.MapTiles       = mapTiles?.ToArray() ?? Array.Empty <EditMapPatchTile>();
            this.AddWarps       = addWarps?.Reverse().ToArray() ?? Array.Empty <IManagedTokenString>(); // reversing the warps allows later ones to 'overwrite' earlier ones, since the game checks them in the listed order
            this.TextOperations = textOperations?.ToArray() ?? Array.Empty <TextOperation>();
            this.Monitor        = monitor;

            this.Contextuals
            .Add(this.FromArea)
            .Add(this.ToArea)
            .Add(this.MapProperties)
            .Add(this.MapTiles)
            .Add(this.AddWarps)
            .Add(this.TextOperations);
        }
示例#17
0
 /// <summary>Construct an instance.</summary>
 /// <param name="pack">The content pack to manage.</param>
 public ManagedContentPack(IContentPack pack, IMonitor monitor, bool paranoid = false)
 {
     this.Pack               = pack;
     this.Monitor            = monitor;
     this.legacyDataProvider = new LegacyDataProvider(this, paranoid);
 }
示例#18
0
        private IEnumerable <IContentPack> GetAllContentPacks()
        {
            // read SMAPI content packs
            foreach (IContentPack contentPack in this.Helper.ContentPacks.GetOwned())
            {
                yield return(contentPack);
            }

            // read legacy content packs
            string baseDir = Path.Combine(this.Helper.DirectoryPath, "locations");

            Directory.CreateDirectory(baseDir);
            foreach (string dir in Directory.EnumerateDirectories(baseDir))
            {
                IContentPack contentPack = null;
                try
                {
                    // skip SMAPI content pack (shouldn't be installed here)
                    if (File.Exists(Path.Combine(dir, "locations.json")))
                    {
                        ModEntry.Logger.Log($"The folder at path '{dir}' looks like a SMAPI content pack. Those should be installed directly in your Mods folder. This content pack won't be loaded.", LogLevel.Warn);
                        continue;
                    }

                    // read manifest
                    string file = Path.Combine(dir, "manifest.json");
                    if (!File.Exists(file))
                    {
                        ModEntry.Logger.Log($"Can't find a manifest.json in the '{dir}' folder. This content pack won't be loaded.", LogLevel.Warn);
                        continue;
                    }

                    JObject config;
                    try
                    {
                        config = (JObject)JsonConvert.DeserializeObject(File.ReadAllText(file));
                    }
                    catch (Exception ex)
                    {
                        ModEntry.Logger.Log($"Can't read manifest.json in the '{dir}' folder. This content pack won't be loaded.", LogLevel.Error, ex);
                        continue;
                    }

                    // get 'about' field
                    JObject about = config.GetValue("About", StringComparison.InvariantCultureIgnoreCase) as JObject;
                    if (about == null)
                    {
                        ModEntry.Logger.Log($"Can't read content pack 'about' info from the manifest.json in the '{dir}' folder. This content pack won't be loaded.", LogLevel.Error);
                        continue;
                    }

                    // prepare basic data
                    string id          = about.GetValue("ModID", StringComparison.InvariantCultureIgnoreCase)?.Value <string>() ?? Guid.NewGuid().ToString("N");
                    string name        = about.GetValue("ModName", StringComparison.InvariantCultureIgnoreCase)?.Value <string>() ?? Path.GetDirectoryName(dir);
                    string author      = about.GetValue("Author", StringComparison.InvariantCultureIgnoreCase)?.Value <string>();
                    string description = about.GetValue("Description", StringComparison.InvariantCultureIgnoreCase)?.Value <string>();
                    string version     = about.GetValue("Version", StringComparison.InvariantCultureIgnoreCase)?.Value <string>() ?? "1.0.0";

                    // create content pack
                    contentPack = this.Helper.ContentPacks.CreateTemporary(dir, id, name, description, author, new SemanticVersion(version));
                }
                catch (Exception ex)
                {
                    ModEntry.Logger.Log($"Could not parse location mod at path '{dir}'. This content pack won't be loaded.", LogLevel.Error, ex);
                }

                if (contentPack != null)
                {
                    yield return(contentPack);
                }
            }
        }
示例#19
0
        /// <summary>
        /// Saves each shop as long as its has a unique name
        /// </summary>
        /// <param name="data"></param>
        /// <param name="contentPack"></param>
        public static void RegisterShops(ContentPack data, IContentPack contentPack)
        {
            ItemsUtil.RegisterPacksToRemove(data.RemovePacksFromVanilla, data.RemovePackRecipesFromVanilla, data.RemoveItemsFromVanilla);

            if (data.Shops != null)
            {
                foreach (ItemShop shopPack in data.Shops)
                {
                    if (ItemShops.ContainsKey(shopPack.ShopName))
                    {
                        ModEntry.monitor.Log($"{contentPack.Manifest.Name} is trying to add a Shop \"{shopPack.ShopName}\"," +
                                             $" but a shop of this name has already been added. " +
                                             $"It will not be added.", LogLevel.Warn);
                        continue;
                    }
                    shopPack.ContentPack = contentPack;
                    ItemShops.Add(shopPack.ShopName, shopPack);
                }
            }

            if (data.AnimalShops != null)
            {
                foreach (AnimalShop animalShopPack in data.AnimalShops)
                {
                    if (AnimalShops.ContainsKey(animalShopPack.ShopName))
                    {
                        ModEntry.monitor.Log($"{contentPack.Manifest.Name} is trying to add an AnimalShop \"{animalShopPack.ShopName}\"," +
                                             $" but a shop of this name has already been added. " +
                                             $"It will not be added.", LogLevel.Warn);
                        continue;
                    }
                    AnimalShops.Add(animalShopPack.ShopName, animalShopPack);
                }
            }

            if (data.VanillaShops != null)
            {
                foreach (var vanillaShopPack in data.VanillaShops)
                {
                    if (!VanillaShopNames.Contains(vanillaShopPack.ShopName))
                    {
                        ModEntry.monitor.Log($"{contentPack.Manifest.Name}" +
                                             $" is trying to edit nonexistent vanilla store" +
                                             $" \"{vanillaShopPack.ShopName}\"", LogLevel.Warn);
                        continue;
                    }

                    if (VanillaShops.ContainsKey(vanillaShopPack.ShopName))
                    {
                        VanillaShops[vanillaShopPack.ShopName].StockManagers.Add(new ItemPriceAndStockManager(vanillaShopPack));

                        if (vanillaShopPack.ReplaceInsteadOfAdd)
                        {
                            VanillaShops[vanillaShopPack.ShopName].ReplaceInsteadOfAdd = true;
                        }

                        if (vanillaShopPack.AddStockAboveVanilla)
                        {
                            VanillaShops[vanillaShopPack.ShopName].AddStockAboveVanilla = true;
                        }
                    }
                    else
                    {
                        vanillaShopPack.Initialize();
                        vanillaShopPack.StockManagers.Add(new ItemPriceAndStockManager(vanillaShopPack));
                        VanillaShops.Add(vanillaShopPack.ShopName, vanillaShopPack);
                    }
                }
            }
        }
示例#20
0
 /// <summary>Constructs an instance.</summary>
 /// <param name="internalAnimalName">The internal name of the animal the sprite sheet is for.</param>
 /// <param name="internalAnimalSubtypeName">The internal name of the subtype the sprite sheet is for.</param>
 /// <param name="isBaby">Whether the sprite sheet is for the baby version of the animal.</param>
 /// <param name="isHarvested">Whether the sprite sheet is for the harvested version of the animal.</param>
 /// <param name="season">The season the sprite sheet is for of the animal.</param>
 /// <param name="relativeTexturePath">The path to the sprite sheet relative to <paramref name="contentPackAssetOwner"/>.</param>
 /// <param name="contentPackAssetOwner">The content pack that owns the asset.</param>
 public ManagedAsset(string internalAnimalName, string internalAnimalSubtypeName, bool isBaby, bool isHarvested, string season, string relativeTexturePath, IContentPack contentPackAssetOwner = null)
 {
     InternalAnimalName        = internalAnimalName;
     InternalAnimalSubtypeName = internalAnimalSubtypeName;
     IsBaby                = isBaby;
     IsHarvested           = isHarvested;
     Season                = season;
     RelativeTexturePath   = relativeTexturePath;
     ContentPackAssetOwner = contentPackAssetOwner;
     IsGameContent         = ContentPackAssetOwner == null;
 }
示例#21
0
 /*********
 ** Public methods
 *********/
 /// <summary>Get whether a file exists in the content pack.</summary>
 /// <param name="contentPack">The content pack.</param>
 /// <param name="key">The asset key.</param>
 public bool FileExists(IContentPack contentPack, string key)
 {
     return(this.GetRealPath(contentPack, key) != null);
 }
 public static Map Load(string path, IModHelper helper, IContentPack contentPack = null)
 {
     return(Load(path, helper, false, contentPack));
 }
示例#23
0
 public BuildableEdit(string id, string indoorsMap, string iconFile, int price, Texture2D icon, string mapName, string location, IContentPack pack)
 {
     this.id          = id;
     this.indoorsFile = indoorsMap;
     this.iconFile    = iconFile;
     this.price       = price;
     this._icon       = icon;
     this._mapName    = mapName;
     this._location   = location;
 }
        public static Map Load(string path, IModHelper helper, bool syncTexturesToClients, IContentPack contentPack)
        {
            Dictionary <TileSheet, Texture2D> tilesheets = Helper.Reflection.GetField <Dictionary <TileSheet, Texture2D> >(Game1.mapDisplayDevice, "m_tileSheetTextures").GetValue();
            Map    map      = tmx2map(Path.Combine(contentPack != null ? contentPack.DirectoryPath : helper.DirectoryPath, path));
            string fileName = new FileInfo(path).Name;

            foreach (TileSheet t in map.TileSheets)
            {
                string[] seasons       = new string[] { "summer_", "fall_", "winter_" };
                string   tileSheetPath = path.Replace(fileName, t.ImageSource + ".png");

                FileInfo tileSheetFile        = new FileInfo(Path.Combine(contentPack != null ? contentPack.DirectoryPath : helper.DirectoryPath, tileSheetPath));
                FileInfo tileSheetFileVanilla = new FileInfo(Path.Combine(PyUtils.getContentFolder(), "Content", t.ImageSource + ".xnb"));
                if (tileSheetFile.Exists && !tileSheetFileVanilla.Exists && tilesheets.Find(k => k.Key.ImageSource == t.ImageSource).Key == null)
                {
                    Texture2D tilesheet = contentPack != null?contentPack.LoadAsset <Texture2D>(tileSheetPath) : helper.Content.Load <Texture2D>(tileSheetPath);

                    tilesheet.inject(t.ImageSource);

                    if (syncTexturesToClients && Game1.IsMultiplayer && Game1.IsServer)
                    {
                        foreach (Farmer farmhand in Game1.otherFarmers.Values)
                        {
                            PyNet.sendGameContent(t.ImageSource, tilesheet, farmhand, (b) => Monitor.Log("Syncing " + t.ImageSource + " to " + farmhand.Name + ": " + (b ? "successful" : "failed"), b ? LogLevel.Info : LogLevel.Warn));
                        }
                    }

                    if (t.ImageSource.Contains("spring_"))
                    {
                        foreach (string season in seasons)
                        {
                            string   seasonPath = path.Replace(fileName, t.ImageSource.Replace("spring_", season));
                            FileInfo seasonFile = new FileInfo(Path.Combine(contentPack != null ? contentPack.DirectoryPath : helper.DirectoryPath, seasonPath + ".png"));
                            if (seasonFile.Exists && tilesheets.Find(k => k.Key.ImageSource == t.ImageSource.Replace("spring_", season)).Key == null)
                            {
                                Texture2D seasonTilesheet = contentPack != null?contentPack.LoadAsset <Texture2D>(seasonPath + ".png") : helper.Content.Load <Texture2D>(seasonPath + ".png");

                                string seasonTextureName = t.ImageSource.Replace("spring_", season);
                                seasonTilesheet.inject(seasonTextureName);
                                seasonTilesheet.inject("Maps/" + seasonTextureName);

                                if (syncTexturesToClients && Game1.IsMultiplayer && Game1.IsServer)
                                {
                                    foreach (Farmer farmhand in Game1.otherFarmers.Values)
                                    {
                                        PyNet.sendGameContent(new string[] { seasonTextureName, "Maps/" + seasonTextureName }, seasonTilesheet, farmhand, (b) => Monitor.Log("Syncing " + seasonTextureName + " to " + farmhand.Name + ": " + (b ? "successful" : "failed"), b ? LogLevel.Info : LogLevel.Warn));
                                    }
                                }
                            }
                        }
                    }
                }
            }
            map.LoadTileSheets(Game1.mapDisplayDevice);
            return(map);
        }
示例#25
0
        /// <summary>Load data from a version 1.1 manifest.</summary>
        /// <param name="contentPack">The content pack to load.</param>
        /// <param name="configPath">The content pack's relative config file path.</param>
        private LocationConfig ReadConfig_1_1(IContentPack contentPack, string configPath)
        {
            // read raw data
            LocationConfig_1_1 raw;

            try
            {
                raw = contentPack.ReadJsonFile <LocationConfig_1_1>(configPath);
            }
            catch (Exception err)
            {
                this.Monitor.Log("   Skipped: can't parse config file (version 1.1).", LogLevel.Warn, err);
                return(null);
            }

            // parse
            LocationConfig config = new LocationConfig();

            {
                // convert locations
                if (raw.Locations != null)
                {
                    foreach (IDictionary <string, string> location in raw.Locations)
                    {
                        config.Locations.Add(new Location
                        {
                            Farmable = location.ContainsKey("farmable") && Convert.ToBoolean(location["farmable"]),
                            Outdoor  = location.ContainsKey("outdoor") && Convert.ToBoolean(location["outdoor"]),
                            FileName = location["file"],
                            MapName  = location["name"],
                            Type     = "Default"
                        });
                    }
                }

                // convert overrides
                if (raw.Overrides != null)
                {
                    foreach (IDictionary <string, string> @override in raw.Overrides)
                    {
                        config.Overrides.Add(new Override
                        {
                            FileName = @override["file"],
                            MapName  = @override["name"]
                        });
                    }
                }

                // convert tilesheets
                if (raw.Tilesheets != null)
                {
                    foreach (IDictionary <string, string> sheet in raw.Tilesheets)
                    {
                        config.Tilesheets.Add(new Tilesheet
                        {
                            FileName = sheet["file"],
                            MapName  = sheet["map"],
                            SheetId  = sheet["sheet"],
                            Seasonal = sheet.ContainsKey("seasonal") && Convert.ToBoolean(sheet["seasonal"])
                        });
                    }
                }

                // convert tiles
                if (raw.Tiles != null)
                {
                    foreach (IDictionary <string, string> tile in raw.Tiles)
                    {
                        Tile newTile = new Tile
                        {
                            TileX   = Convert.ToInt32(tile["x"]),
                            TileY   = Convert.ToInt32(tile["y"]),
                            MapName = tile["map"],
                            LayerId = tile["layer"],
                            SheetId = tile.ContainsKey("sheet") ? tile["sheet"] : null
                        };

                        if (tile.ContainsKey("interval"))
                        {
                            newTile.Interval    = Convert.ToInt32(tile["interval"]);
                            newTile.TileIndexes = tile["tileIndex"].Split(',').Select(p => Convert.ToInt32(p)).ToArray();
                        }
                        else
                        {
                            newTile.TileIndex = Convert.ToInt32(tile["tileIndex"]);
                        }

                        newTile.Conditions = tile.ContainsKey("conditions") ? tile["conditions"] : null;
                        config.Tiles.Add(newTile);
                    }
                }

                // convert properties
                if (raw.Properties != null)
                {
                    foreach (IList <string> property in raw.Properties)
                    {
                        config.Properties.Add(new Property
                        {
                            MapName = property[0],
                            LayerId = property[1],
                            TileX   = Convert.ToInt32(property[2]),
                            TileY   = Convert.ToInt32(property[3]),
                            Key     = property[4],
                            Value   = property[5]
                        });
                    }
                }

                // convert warps
                if (raw.Warps != null)
                {
                    foreach (IList <string> warp in raw.Warps)
                    {
                        config.Warps.Add(new Warp
                        {
                            MapName    = warp[0],
                            TileX      = Convert.ToInt32(warp[1]),
                            TileY      = Convert.ToInt32(warp[2]),
                            TargetName = warp[3],
                            TargetX    = Convert.ToInt32(warp[4]),
                            TargetY    = Convert.ToInt32(warp[5])
                        });
                    }
                }

                // convert conditions
                if (raw.Conditions != null)
                {
                    foreach (KeyValuePair <string, IDictionary <string, string> > condition in raw.Conditions)
                    {
                        config.Conditionals.Add(new Conditional
                        {
                            Name     = condition.Key,
                            Item     = Convert.ToInt32(condition.Value["item"]),
                            Amount   = Convert.ToInt32(condition.Value["amount"]),
                            Question = condition.Value["question"]
                        });
                    }
                }

                // convert minecarts
                if (raw.Minecarts != null)
                {
                    foreach (KeyValuePair <string, IDictionary <string, IList <string> > > set in raw.Minecarts)
                    {
                        TeleporterList newSet = new TeleporterList
                        {
                            ListName = set.Key
                        };
                        foreach (KeyValuePair <string, IList <string> > destination in set.Value)
                        {
                            newSet.Destinations.Add(new TeleporterDestination
                            {
                                ItemText  = destination.Key,
                                MapName   = destination.Value[0],
                                TileX     = Convert.ToInt32(destination.Value[1]),
                                TileY     = Convert.ToInt32(destination.Value[2]),
                                Direction = Convert.ToInt32(destination.Value[3])
                            });
                        }

                        config.Teleporters.Add(newSet);
                    }
                }

                // convert shops
                config.Shops = raw.Shops;
            }

            return(config);
        }
示例#26
0
        /*********
        ** Public methods
        *********/
        /// <summary>The mod entry point, called after the mod is first loaded.</summary>
        /// <param name="helper">Provides simplified APIs for writing mods.</param>
        public override void Entry(IModHelper helper)
        {
            helper.Events.GameLoop.DayStarted   += this.OnDayStarted;
            helper.Events.GameLoop.UpdateTicked += this.UpdateTicked;

            // collect data models
            IList <ThingsToForget> models = new List <ThingsToForget>();

            foreach (IModInfo mod in this.Helper.ModRegistry.GetAll())
            {
                // make sure it's a Content Patcher pack
                if (!mod.IsContentPack || mod.Manifest.ContentPackFor?.UniqueID.Trim().Equals("Pathoschild.ContentPatcher", StringComparison.InvariantCultureIgnoreCase) != true)
                {
                    continue;
                }

                // get the directory path containing the manifest.json
                // HACK: IModInfo is implemented by ModMetadata, an internal SMAPI class which
                // contains much more non-public information. Caveats:
                //   - This isn't part of the public API so it may break in future versions.
                //   - Since the class is internal, we need reflection to access the values.
                //   - SMAPI's reflection API doesn't let us reflect into SMAPI, so we need manual
                //     reflection instead.
                //   - SMAPI's data API doesn't let us access an absolute path, so we need to parse
                //     the model ourselves.
                string directoryPath = (string)mod.GetType().GetProperty("DirectoryPath")?.GetValue(mod);
                if (directoryPath == null)
                {
                    throw new InvalidOperationException($"Couldn't fetch the DirectoryPath property from the mod info for {mod.Manifest.Name}.");
                }

                // read the JSON file
                IContentPack contentPack = this.Helper.ContentPacks.CreateFake(directoryPath);
                models.Add(contentPack.ReadJsonFile <ThingsToForget>("content.json"));
                // extract event IDs
                foreach (ThingsToForget model in models)
                {
                    if (model?.RepeatEvents == null)
                    {
                        continue;
                    }

                    foreach (int eventID in model.RepeatEvents)
                    {
                        this.EventsToForget.Add(eventID);
                    }
                }
                foreach (ThingsToForget model in models)
                {
                    if (model?.RepeatMail == null)
                    {
                        continue;
                    }

                    foreach (string mailID in model.RepeatMail)
                    {
                        this.MailToForget.Add(mailID);
                    }
                }
                foreach (ThingsToForget model in models)
                {
                    if (model?.RepeatResponse == null)
                    {
                        continue;
                    }

                    foreach (int ResponseID in model.RepeatResponse)
                    {
                        this.ResponseToForget.Add(ResponseID);
                    }
                }
            }
            helper.ConsoleCommands.Add("eventforget", "'usage: eventforget <id>", ForgetManualCommand);
            helper.ConsoleCommands.Add("showevents", "'usage: Lists all completed events", ShowEventsCommand);
            helper.ConsoleCommands.Add("showmail", "'usage: Lists all seen mail", ShowMailCommand);
            helper.ConsoleCommands.Add("mailforget", "'usage: mailforget <id>", ForgetMailCommand);
            helper.ConsoleCommands.Add("sendme", "'usage: sendme <id>", SendMailCommand);
            helper.ConsoleCommands.Add("showresponse", "'usage: Lists Response IDs.  For ADVANCED USERS!!", ShowResponseCommand);
            helper.ConsoleCommands.Add("responseforget", "'usage: responseforget <id>'", ForgetResponseCommand);
            //helper.ConsoleCommands.Add("responseadd", "'usage: responseadd <id>'  Inject a question response.", ResponseAddCommand);
            helper.ConsoleCommands.Add("repeateradd", "'usage: repeateradd <id(optional)>' Create a repeatable event.  If no id is given, the last seen will be repeated.  Works on Next Day", ManualRepeater);
            helper.ConsoleCommands.Add("repeatersave", "'usage: repeatersave <filename>' Creates a textfile with all events you set to repeat manually.", SaveManualCommand);
            helper.ConsoleCommands.Add("repeaterload", "'usage: repeaterload <filename>' Loads the file you designate.", LoadCommand);
            helper.ConsoleCommands.Add("inject", "'usage: inject <event, mail, response> <ID>' Example: 'inject event 1324329'  Inject IDs into the game.", injectCommand);
        }
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="pack">The content pack to manage.</param>
 public ManagedContentPack(IContentPack pack)
 {
     this.Pack = pack;
 }
示例#28
0
            /// <summary>Validates a single instance of farm data, correcting obsolete/invalid settings automatically.</summary>
            /// <param name="config">The contents of a single config file to be validated.</param>
            /// <param name="pack">The content pack associated with this config data; null if the file was from this mod's own data folder.</param>
            public static void ValidateFarmData(FarmConfig config, IContentPack pack)
            {
                if (pack != null)
                {
                    Monitor.Log($"Validating data from content pack: {pack.Manifest.Name}", LogLevel.Trace);
                }
                else
                {
                    Monitor.Log("Validating data from FarmTypeManager/data", LogLevel.Trace);
                }

                List <SpawnArea[]> allAreas = new List <SpawnArea[]>(); //a unified list of each "Areas" array in this config file

                //add each group of spawn areas to the list (unless its config section is null)
                if (config.Forage_Spawn_Settings != null)
                {
                    allAreas.Add(config.Forage_Spawn_Settings.Areas);
                }
                if (config.Large_Object_Spawn_Settings != null)
                {
                    allAreas.Add(config.Large_Object_Spawn_Settings.Areas);
                }
                if (config.Ore_Spawn_Settings != null)
                {
                    allAreas.Add(config.Ore_Spawn_Settings.Areas);
                }
                if (config.Monster_Spawn_Settings != null)
                {
                    allAreas.Add(config.Monster_Spawn_Settings.Areas);
                }

                Monitor.Log("Checking for duplicate UniqueAreaIDs...", LogLevel.Trace);
                HashSet <string> IDs = new HashSet <string>(); //a record of all unique IDs encountered during this process

                //erase any duplicate IDs and record the others in the "IDs" hashset
                foreach (SpawnArea[] areas in allAreas)                                                            //for each "Areas" array in allAreas
                {
                    foreach (SpawnArea area in areas)                                                              //for each area in the current array
                    {
                        if (String.IsNullOrWhiteSpace(area.UniqueAreaID) || area.UniqueAreaID.ToLower() == "null") //if the area ID is null, blank, or the string "null" (to account for user confusion)
                        {
                            continue;                                                                              //this name will be replaced later, so ignore it for now
                        }

                        if (IDs.Contains(area.UniqueAreaID)) //if this area's ID was already encountered
                        {
                            Monitor.Log($"Duplicate UniqueAreaID found: \"{area.UniqueAreaID}\" will be renamed.", LogLevel.Debug);
                            if (pack != null) //if this config is from a content pack
                            {
                                Monitor.Log($"Content pack: {pack.Manifest.Name}", LogLevel.Info);
                                Monitor.Log($"If this happened after updating another mod, it might cause certain conditions (such as one-time-only spawns) to reset in that area.", LogLevel.Debug);
                            }

                            area.UniqueAreaID = ""; //erase this area's ID, marking it for replacement
                        }
                        else //if this ID is unique so far
                        {
                            IDs.Add(area.UniqueAreaID); //add the area to the ID set
                        }
                    }
                }

                Monitor.Log("Assigning new UniqueAreaIDs to any blanks or duplicates...", LogLevel.Trace);
                string newName;   //temp storage for a new ID while it's created/tested
                int    newNumber; //temp storage for the numeric part of a new ID

                //create new IDs for any empty ones
                foreach (SpawnArea[] areas in allAreas)                                                            //for each "Areas" array in allAreas
                {
                    foreach (SpawnArea area in areas)                                                              //for each area in the current array
                    {
                        if (String.IsNullOrWhiteSpace(area.UniqueAreaID) || area.UniqueAreaID.ToLower() == "null") //if the area ID is null, blank, or the string "null" (to account for user confusion)
                        {
                            //create a new name, based on which type of area this is
                            newName = area.MapName;
                            if (area is ForageSpawnArea)
                            {
                                newName += " forage area ";
                            }
                            else if (area is LargeObjectSpawnArea)
                            {
                                newName += " large object area ";
                            }
                            else if (area is OreSpawnArea)
                            {
                                newName += " ore area ";
                            }
                            else if (area is MonsterSpawnArea)
                            {
                                newName += " monster area ";
                            }
                            else
                            {
                                newName += " area ";
                            }

                            newNumber = 1;

                            while (IDs.Contains(newName + newNumber)) //if this ID wouldn't be unique
                            {
                                newNumber++;                          //increment and try again
                            }

                            area.UniqueAreaID = newName + newNumber; //apply the new unique ID
                            Monitor.Log($"New UniqueAreaID assigned: {area.UniqueAreaID}", LogLevel.Trace);
                        }

                        IDs.Add(area.UniqueAreaID); //the ID is finalized, so add it to the set of encountered IDs
                    }
                }

                //confirm that any paired min/max settings are in the correct order
                foreach (SpawnArea[] areas in allAreas)                          //for each "Areas" array in allAreas
                {
                    foreach (SpawnArea area in areas)                            //for each area in the current array
                    {
                        if (area.MinimumSpawnsPerDay > area.MaximumSpawnsPerDay) //if the min and max are in the wrong order
                        {
                            //swap min and max
                            int temp = area.MinimumSpawnsPerDay;
                            area.MinimumSpawnsPerDay = area.MaximumSpawnsPerDay;
                            area.MaximumSpawnsPerDay = temp;
                            Monitor.Log($"Swapping minimum and maximum spawns per day for this area: {area.UniqueAreaID}", LogLevel.Trace);
                        }

                        if (area.SpawnTiming.StartTime > area.SpawnTiming.EndTime) //if start and end are in the wrong order
                        {
                            //swap start and end
                            StardewTime temp = area.SpawnTiming.StartTime;
                            area.SpawnTiming.StartTime = area.SpawnTiming.EndTime;
                            area.SpawnTiming.EndTime   = temp;
                            Monitor.Log($"Swapping StartTime and EndTime in the SpawnTiming settings for this area: {area.UniqueAreaID}", LogLevel.Trace);
                        }
                    }
                }

                //detect invalid sound names and warn the user
                //NOTE: this will not remove the invalid name, in case the problem is related to custom sound loading
                foreach (SpawnArea[] areas in allAreas)                                                      //for each "Areas" array in allAreas
                {
                    foreach (SpawnArea area in areas)                                                        //for each area in the current array
                    {
                        if (area.SpawnTiming.SpawnSound != null && area.SpawnTiming.SpawnSound.Trim() != "") //if a SpawnSound has been provided for this area
                        {
                            try
                            {
                                Game1.soundBank.GetCue(area.SpawnTiming.SpawnSound); //test whether this sound exists by retrieving it from the game's soundbank
                            }
                            catch                                                    //if an exception is thrown while retrieving the sound
                            {
                                Monitor.Log($"This spawn sound could not be found: {area.SpawnTiming.SpawnSound}", LogLevel.Debug);
                                Monitor.Log($"Please make sure the sound's name is spelled and capitalized correctly. Sound names are case-sensitive.", LogLevel.Debug);
                                Monitor.Log($"Area: {area.UniqueAreaID}", LogLevel.Debug);
                                if (pack != null) //if this file is from a content pack
                                {
                                    Monitor.Log($"Content pack: {pack.Manifest.Name}", LogLevel.Debug);
                                }
                                else //if this file is from FarmTypeManager/data
                                {
                                    Monitor.Log($"File: FarmTypeManager/data/{Constants.SaveFolderName}.json", LogLevel.Debug);
                                }
                            }
                        }
                    }
                }

                if (pack != null)
                {
                    Monitor.Log($"Validation complete for content pack: {pack.Manifest.Name}", LogLevel.Trace);
                }
                else
                {
                    Monitor.Log("Validation complete for data from FarmTypeManager/data", LogLevel.Trace);
                }
                return;
            }
示例#29
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="indexPath">The path of indexes from the root <c>content.json</c> to this patch; see <see cref="IPatch.IndexPath"/>.</param>
 /// <param name="path">The path to the patch from the root content file.</param>
 /// <param name="assetName">The normalized asset name to intercept.</param>
 /// <param name="localAsset">The asset key to load from the content pack instead.</param>
 /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
 /// <param name="updateRate">When the patch should be updated.</param>
 /// <param name="contentPack">The content pack which requested the patch.</param>
 /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
 /// <param name="parseAssetName">Parse an asset name.</param>
 public LoadPatch(int[] indexPath, LogPathBuilder path, IManagedTokenString assetName, IManagedTokenString localAsset, IEnumerable <Condition> conditions, UpdateRate updateRate, IContentPack contentPack, IPatch?parentPatch, Func <string, IAssetName> parseAssetName)
     : base(
         indexPath: indexPath,
         path: path,
         type: PatchType.Load,
         assetName: assetName,
         conditions: conditions,
         updateRate: updateRate,
         contentPack: contentPack,
         parentPatch: parentPatch,
         parseAssetName: parseAssetName,
         fromAsset: localAsset
         )
 {
 }
示例#30
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="logName">A unique name for this patch shown in log messages.</param>
 /// <param name="assetLoader">Handles loading assets from content packs.</param>
 /// <param name="contentPack">The content pack which requested the patch.</param>
 /// <param name="assetName">The normalised asset name to intercept.</param>
 /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
 /// <param name="localAsset">The asset key to load from the content pack instead.</param>
 /// <param name="normaliseAssetName">Normalise an asset name.</param>
 public LoadPatch(string logName, AssetLoader assetLoader, IContentPack contentPack, TokenString assetName, ConditionDictionary conditions, TokenString localAsset, Func <string, string> normaliseAssetName)
     : base(logName, PatchType.Load, assetLoader, contentPack, assetName, conditions, normaliseAssetName)
 {
     this.LocalAsset = localAsset;
 }