Exemplo n.º 1
0
        /*********
        ** Internal Methods
        *********/
        /// <summary>The prefix for the <see cref="AnimalHouse.resetSharedState"/> method.</summary>
        /// <param name="__instance">The current <see cref="AnimalHouse"/> instance being patched.</param>
        /// <returns><see langword="false"/>, meaning the original method will not get ran.</returns>
        /// <remarks>This reimplements the original method to add the custom incubator recipes.</remarks>
        internal static bool ResetSharedStatePrefix(AnimalHouse __instance)
        {
            __instance.resetPositionsOfAllAnimals();

            foreach (var @object in __instance.Objects.Values)
            {
                // ensure object is an incubator
                if ([email protected] || [email protected]("Incubator") || @object.heldObject.Value == null || @object.minutesUntilReady > 0)
                {
                    continue;
                }

                // ensure there is space in the animal house to hatch the animal
                if (!__instance.isFull())
                {
                    var whatHatched = "??";

                    string internalName = null;
                    if (@object.modData.TryGetValue($"{ModEntry.Instance.ModManifest.UniqueID}/recipeInternalAnimalName", out var recipeInternalAnimalNameString))
                    {
                        internalName = recipeInternalAnimalNameString;
                    }

                    if (internalName == null) // the only time the above property won't be present on an incubator is if it was populated before FAVR was installed
                    {
                        var incubatorType    = __instance.Name == "Incubator" ? IncubatorType.Regular : IncubatorType.Ostrich;
                        var incubatorRecipes = ModEntry.Instance.CustomIncubatorRecipes.Where(incubatorRecipe => incubatorRecipe.IncubatorType.HasFlag(incubatorType)).ToList();

                        // try to find the recipe that has the corresponding item
                        var recipes = incubatorRecipes.Where(IncubatorRecipe => IncubatorRecipe.InputId == @object.heldObject.Value.ParentSheetIndex);
                        if (recipes.Any())
                        {
                            var totalChance  = recipes.Select(recipe => recipe.Chance).Sum();
                            var randomChance = (float)(Game1.random.NextDouble() * totalChance);
                            foreach (var recipe in recipes)
                            {
                                randomChance -= recipe.Chance;
                                if (randomChance <= 0)
                                {
                                    internalName = recipe.InternalAnimalName;
                                    break;
                                }
                            }
                        }
                    }

                    var animalName = ModEntry.Instance.Api.GetAnimalByInternalName(internalName)?.Name
                                     ?? ModEntry.Instance.Api.GetAnimalSubtypeByInternalName(internalName)?.Name;

                    if (animalName != null)
                    {
                        whatHatched = $"A new baby {animalName.ToLower()} hatched!";
                    }

                    if (whatHatched == "??")
                    {
                        // if there was no valid recipe, then just remove the object from the incubator (this is the case when the player puts an egg in the incubator then removes the mod that adds that recipe)
                        @object.heldObject.Value = null;
                        return(false);
                    }

                    // run hatched event
                    __instance.currentEvent = new Event($"none/-1000 -1000/farmer 2 9 0/pause 250/message \"{whatHatched}\"/pause 500/animalNaming/pause 500/end");
                    break;
                }

                // tell player building is full if they haven't already been told
                if (!__instance.hasShownIncubatorBuildingFullMessage)
                {
                    __instance.hasShownIncubatorBuildingFullMessage = true;
                    Game1.showGlobalMessage(Game1.content.LoadString("Strings\\Locations:AnimalHouse_Incubator_HouseFull"));
                }
            }

            // call the base resetSharedState method
            // this approach isn't ideal but when using regular reflection and invoking the MethodInfo directly, it would call this patch (instead of the base method) resulting in a stack overflow
            // https://stackoverflow.com/questions/4357729/use-reflection-to-invoke-an-overridden-base-method/14415506#14415506
            var baseMethod      = typeof(GameLocation).GetMethod("resetSharedState", BindingFlags.NonPublic | BindingFlags.Instance);
            var functionPointer = baseMethod.MethodHandle.GetFunctionPointer();
            var function        = (System.Action)Activator.CreateInstance(typeof(System.Action), __instance, functionPointer);

            function.Invoke();
            return(false);
        }