public bool ShouldDieOfOldAge(FarmAnimal animal)
        {
            int age = animal.GetDaysOwned();

            var ages = GetMinAndMaxAnimalAgeInYears(animal);

            // convert years to days
            int minAge = ages.Item1 * 28 * 4;
            int maxAge = ages.Item2 * 28 * 4;

            if (age >= minAge)
            {
                double ran = Game1.random.NextDouble();

                double mappedValue = Map(age, minAge, maxAge, 0, 1);

                if (ran < mappedValue)
                {
                    return(true);
                }
            }

            return(false);
        }
 public override void draw(SpriteBatch b)
 {
     if (!movingAnimal && !Game1.globalFade)
     {
         b.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * 0.75f);
         Game1.drawDialogueBox(xPositionOnScreen, yPositionOnScreen + 128, width, height - 128, speaker: false, drawOnlyBox: true);
         if ((byte)animal.harvestType != 2)
         {
             textBox.Draw(b);
         }
         int    age     = (animal.GetDaysOwned() + 1) / 28 + 1;
         string ageText = (age <= 1) ? Game1.content.LoadString("Strings\\UI:AnimalQuery_Age1") : Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeN", age);
         if ((int)animal.age < (byte)animal.ageWhenMature)
         {
             ageText += Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeBaby");
         }
         Utility.drawTextWithShadow(b, ageText, Game1.smallFont, new Vector2(xPositionOnScreen + IClickableMenu.spaceToClearSideBorder + 32, yPositionOnScreen + IClickableMenu.spaceToClearTopBorder + 16 + 128), Game1.textColor);
         int yOffset = 0;
         if (parentName != null)
         {
             yOffset = 21;
             Utility.drawTextWithShadow(b, Game1.content.LoadString("Strings\\UI:AnimalQuery_Parent", parentName), Game1.smallFont, new Vector2(xPositionOnScreen + IClickableMenu.spaceToClearSideBorder + 32, 32 + yPositionOnScreen + IClickableMenu.spaceToClearTopBorder + 16 + 128), Game1.textColor);
         }
         int halfHeart = (int)((loveLevel * 1000.0 % 200.0 >= 100.0) ? (loveLevel * 1000.0 / 200.0) : (-100.0));
         for (int i = 0; i < 5; i++)
         {
             b.Draw(Game1.mouseCursors, new Vector2(xPositionOnScreen + 96 + 32 * i, yOffset + yPositionOnScreen - 32 + 320), new Microsoft.Xna.Framework.Rectangle(211 + ((loveLevel * 1000.0 <= (double)((i + 1) * 195)) ? 7 : 0), 428, 7, 6), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 0.89f);
             if (halfHeart == i)
             {
                 b.Draw(Game1.mouseCursors, new Vector2(xPositionOnScreen + 96 + 32 * i, yOffset + yPositionOnScreen - 32 + 320), new Microsoft.Xna.Framework.Rectangle(211, 428, 4, 6), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 0.891f);
             }
         }
         Utility.drawTextWithShadow(b, Game1.parseText(animal.getMoodMessage(), Game1.smallFont, width - IClickableMenu.spaceToClearSideBorder * 2 - 64), Game1.smallFont, new Vector2(xPositionOnScreen + IClickableMenu.spaceToClearSideBorder + 32, yOffset + yPositionOnScreen + 384 - 64 + 4), Game1.textColor);
         okButton.draw(b);
         sellButton.draw(b);
         moveHomeButton.draw(b);
         if (allowReproductionButton != null)
         {
             allowReproductionButton.draw(b);
         }
         if (confirmingSell)
         {
             b.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * 0.75f);
             Game1.drawDialogueBox(Game1.uiViewport.Width / 2 - 160, Game1.uiViewport.Height / 2 - 192, 320, 256, speaker: false, drawOnlyBox: true);
             string confirmText = Game1.content.LoadString("Strings\\UI:AnimalQuery_ConfirmSell");
             b.DrawString(Game1.dialogueFont, confirmText, new Vector2((float)(Game1.uiViewport.Width / 2) - Game1.dialogueFont.MeasureString(confirmText).X / 2f, Game1.uiViewport.Height / 2 - 96 + 8), Game1.textColor);
             yesButton.draw(b);
             noButton.draw(b);
         }
         else if (hoverText != null && hoverText.Length > 0)
         {
             IClickableMenu.drawHoverText(b, hoverText, Game1.smallFont);
         }
     }
     else if (!Game1.globalFade)
     {
         string s = Game1.content.LoadString("Strings\\UI:AnimalQuery_ChooseBuilding", animal.displayHouse, animal.displayType);
         Game1.drawDialogueBox(32, -64, (int)Game1.dialogueFont.MeasureString(s).X + IClickableMenu.borderWidth * 2 + 16, 128 + IClickableMenu.borderWidth * 2, speaker: false, drawOnlyBox: true);
         b.DrawString(Game1.dialogueFont, s, new Vector2(32 + IClickableMenu.spaceToClearSideBorder * 2 + 8, 44f), Game1.textColor);
         okButton.draw(b);
     }
     drawMouse(b);
 }
        /// <summary>Invoked when the menu should get drawn.</summary>
        /// <param name="b">The sprite batch to draw the menu to.</param>
        public override void draw(SpriteBatch b)
        {
            if (CurrentStage == AnimalQueryMenuStage.Main || CurrentStage == AnimalQueryMenuStage.ConfirmingSell) // when the animal is being sold, the main menu should be drawn underneath
            {
                // dark background
                b.Draw(
                    texture: Game1.fadeToBlackRect,
                    destinationRectangle: Game1.graphics.GraphicsDevice.Viewport.Bounds,
                    color: Color.Black * .75f
                    );

                // menu background
                Game1.drawDialogueBox(
                    x: this.xPositionOnScreen,
                    y: this.yPositionOnScreen + 128,
                    width: DefaultWidth,
                    height: DefaultHeight - 128,
                    speaker: false,
                    drawOnlyBox: true
                    );

                // name box
                NameTextBox.Draw(b);

                // age
                var ageInMonths = (Animal.GetDaysOwned() + 1) / 28 + 1;
                var ageText     = (ageInMonths <= 1)
                    ? Game1.content.LoadString("Strings\\UI:AnimalQuery_Age1")
                    : Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeN", ageInMonths);
                if (Animal.age < Animal.ageWhenMature)
                {
                    ageText += Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeBaby"); // append '(Baby)' on the end
                }
                Utility.drawTextWithShadow(
                    b: b,
                    text: ageText,
                    font: Game1.smallFont,
                    position: new Vector2(this.xPositionOnScreen + IClickableMenu.spaceToClearSideBorder + 32, this.yPositionOnScreen + IClickableMenu.spaceToClearTopBorder + 16 + 128),
                    color: Game1.textColor
                    );

                // parent
                var yOffset = 0;
                if (ParentName != null)
                {
                    yOffset = 21;
                    Utility.drawTextWithShadow(
                        b: b,
                        text: Game1.content.LoadString("Strings\\UI:AnimalQuery_Parent", ParentName),
                        font: Game1.smallFont,
                        position: new Vector2(this.xPositionOnScreen + IClickableMenu.spaceToClearSideBorder + 32, 32 + this.yPositionOnScreen + IClickableMenu.spaceToClearTopBorder + 16 + 128),
                        color: Game1.textColor
                        );
                }

                // hearts
                var halfHeart = (LoveLevel * 1000 % 200 >= 100) // half heart refers to which heart should be displayed as a half heart (if any)
                    ? LoveLevel * 1000 / 200
                    : -100;
                for (int i = 0; i < 5; i++)
                {
                    var sourceRectangle = new Rectangle(211 + ((LoveLevel * 1000 <= ((i + 1) * 195)) ? 7 : 0), 428, 7, 6);
                    var layerDepth      = .89f;
                    if (halfHeart == 1)
                    {
                        sourceRectangle = new Rectangle(211, 428, 4, 6);
                        layerDepth      = .891f;
                    }

                    b.Draw(
                        texture: Game1.mouseCursors,
                        position: new Vector2(this.xPositionOnScreen + 96 + 32 * i, yOffset + this.yPositionOnScreen - 32 + 320),
                        sourceRectangle: sourceRectangle,
                        color: Color.White,
                        rotation: 0,
                        origin: Vector2.Zero,
                        scale: 4,
                        effects: SpriteEffects.None,
                        layerDepth: layerDepth
                        );
                }

                // mood message
                Utility.drawTextWithShadow(
                    b: b,
                    text: Game1.parseText(Animal.getMoodMessage(), Game1.smallFont, AnimalQueryMenu.width - IClickableMenu.spaceToClearSideBorder * 2 - 64),
                    font: Game1.smallFont,
                    position: new Vector2(base.xPositionOnScreen + IClickableMenu.spaceToClearSideBorder + 32, yOffset + base.yPositionOnScreen + 384 - 64 + 4),
                    color: Game1.textColor
                    );

                // buttons
                MoveHomeButton.draw(b);
                SellButton.draw(b);
                OkButton.draw(b);
                if (AllowReproductionButton != null) // this is null when the animal can't reproduce
                {
                    AllowReproductionButton.draw(b);
                }

                // hover text
                if (!string.IsNullOrEmpty(HoverText))
                {
                    IClickableMenu.drawHoverText(b, HoverText, Game1.smallFont);
                }

                // draw the overlayed sell menu if needed
                if (CurrentStage == AnimalQueryMenuStage.ConfirmingSell)
                {
                    // dark background
                    b.Draw(
                        texture: Game1.fadeToBlackRect,
                        destinationRectangle: Game1.graphics.GraphicsDevice.Viewport.Bounds,
                        color: Color.Black * .75f
                        );

                    // menu background
                    Game1.drawDialogueBox(
                        x: Game1.uiViewport.Width / 2 - 160,
                        y: Game1.uiViewport.Height / 2 - 192,
                        width: 320,
                        height: 256,
                        speaker: false,
                        drawOnlyBox: true
                        );

                    // 'Are you sure?' label
                    var confirmText = Game1.content.LoadString("Strings\\UI:AnimalQuery_ConfirmSell");
                    b.DrawString(
                        spriteFont: Game1.dialogueFont,
                        text: confirmText,
                        position: new Vector2(Game1.uiViewport.Width / 2 - Game1.dialogueFont.MeasureString(confirmText).X / 2, Game1.uiViewport.Height / 2 - 96 + 8),
                        color: Game1.textColor
                        );

                    // buttons
                    ConfirmSellButton.draw(b);
                    DenySellButton.draw(b);
                }
            }
            else if (CurrentStage == AnimalQueryMenuStage.Rehoming)
            {
                // back button
                BackButton.draw(b);

                // housing string
                var buildingsString = "";
                var animal          = ModEntry.Instance.Api.GetAnimalByInternalSubtypeName(Animal.type);
                if (animal != null)
                {
                    buildingsString = Utilities.ConstructBuildingString(animal.Buildings);
                }
                var housingString = $"Choose a {buildingsString} for your {animal.Name}";

                SpriteText.drawStringWithScrollBackground(
                    b: b,
                    s: housingString,
                    x: Game1.uiViewport.Width / 2 - SpriteText.getWidthOfString(housingString) / 2,
                    y: 16
                    );
            }

            this.drawMouse(b);
        }
        private void CalculateDeathMessage(FarmAnimal animal, string cause)
        {
            // animal.age is the value that doesn't increase if you haven't fed the animal
            int    age         = animal.GetDaysOwned() + 1;
            string causeString = Helper.Translation.Get($"Cause.{cause}");

            string ageString;

            if (age >= 28 * 4)
            {
                int yearCount = (age / (28 * 4)) + 1;
                ageString = Helper.Translation.Get(yearCount == 1 ? "Age.year" : "Age.years", new { yearCount });

                int restAge = age - (yearCount * 28 * 4);
                if (restAge >= 28)
                {
                    int monthCount = (restAge / 28) + 1;

                    if (yearCount == 1)
                    {
                        ageString = Helper.Translation.Get(monthCount == 1 ? "Age.yearAndMonth" : "Age.yearAndMonths", new { yearCount, monthCount });
                    }
                    else
                    {
                        ageString = Helper.Translation.Get(monthCount == 1 ? "Age.yearsAndMonth" : "Age.yearsAndMonths", new { yearCount, monthCount });
                    }
                }
            }
            else
            {
                if (age >= 28)
                {
                    int monthCount = (age / 28) + 1;
                    ageString = Helper.Translation.Get(monthCount == 1 ? "Age.month" : "Age.months", new { monthCount });
                }
                else
                {
                    ageString = Helper.Translation.Get(age == 1 ? "Age.day" : "Age.days", new { dayCount = age });
                }
            }

            string happiness;

            if (animal.happiness < 30)
            {
                happiness = Helper.Translation.Get("Happiness.sad");
            }
            else if (animal.happiness < 200)
            {
                happiness = Helper.Translation.Get("Happiness.fine");
            }
            else
            {
                happiness = Helper.Translation.Get("Happiness.happy");
            }

            double hearts = animal.friendshipTowardFarmer < 1000 ? animal.friendshipTowardFarmer / 200.0 : 5;

            double withHalfHearts = ((int)(hearts * 2.0)) / 2.0;
            string loveString     = Helper.Translation.Get(withHalfHearts == 1 ? "Love.heart" : "Love.hearts", new { heartCount = withHalfHearts });

            // if the locale is the default locale, keep the english animal type
            string animalType = string.IsNullOrWhiteSpace(Helper.Translation.Locale) ? animal.type.Value.ToLower() : animal.displayType;

            string message = Helper.Translation.Get("AnimalDeathMessage", new { animalType, animalName = animal.name, cause = causeString, entireAgeText = ageString, happinessText = happiness, lovestring = loveString });

            PostMessage(message);
        }
        internal static void AnimalQueryMenuPatch(AnimalQueryMenu __instance, bool ___confirmingSell, FarmAnimal ___animal, TextBox ___textBox, string ___parentName)
        {
            try
            {
                int    x = Game1.getMouseX(true), y = Game1.getMouseY(true);                                               // Mouse x and y position
                bool   isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed();                                         // For narrating animal details
                bool   isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box
                string toSpeak = " ", details = " ";

                if (___textBox.Selected)
                {
                    toSpeak = ___textBox.Text;

                    if (isEscPressed)
                    {
                        ___textBox.Selected = false;
                    }
                }
                else
                {
                    if (isCPressed & !isNarratingAnimalInfo)
                    {
                        string name    = ___animal.displayName;
                        string type    = ___animal.displayType;
                        int    age     = (___animal.GetDaysOwned() + 1) / 28 + 1;
                        string ageText = (age <= 1) ? Game1.content.LoadString("Strings\\UI:AnimalQuery_Age1") : Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeN", age);
                        string parent  = "";
                        if ((int)___animal.age.Value < (byte)___animal.ageWhenMature.Value)
                        {
                            ageText += Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeBaby");
                        }
                        if (___parentName != null)
                        {
                            parent = Game1.content.LoadString("Strings\\UI:AnimalQuery_Parent", ___parentName);
                        }

                        details = $"Name: {name} Type: {type} \n\t Age: {ageText} {parent}";
                        animalQueryMenuQuery = " ";

                        isNarratingAnimalInfo = true;
                        Task.Delay(200).ContinueWith(_ => { isNarratingAnimalInfo = false; });
                    }

                    if (__instance.okButton != null && __instance.okButton.containsPoint(x, y))
                    {
                        toSpeak = "OK button";
                    }
                    else if (__instance.sellButton != null && __instance.sellButton.containsPoint(x, y))
                    {
                        toSpeak = $"Sell for {___animal.getSellPrice()}g button";
                    }
                    else if (___confirmingSell && __instance.yesButton != null && __instance.yesButton.containsPoint(x, y))
                    {
                        toSpeak = "Confirm selling animal";
                    }
                    else if (___confirmingSell && __instance.noButton != null && __instance.noButton.containsPoint(x, y))
                    {
                        toSpeak = "Cancel selling animal";
                    }
                    else if (__instance.moveHomeButton != null && __instance.moveHomeButton.containsPoint(x, y))
                    {
                        toSpeak = "Change home building button";
                    }
                    else if (__instance.allowReproductionButton != null && __instance.allowReproductionButton.containsPoint(x, y))
                    {
                        toSpeak = ((___animal.allowReproduction.Value) ? "Enabled" : "Disabled") + " allow reproduction button";
                    }
                    else if (__instance.textBoxCC != null && __instance.textBoxCC.containsPoint(x, y))
                    {
                        toSpeak = "Animal name text box";
                    }
                }

                if (animalQueryMenuQuery != toSpeak)
                {
                    animalQueryMenuQuery = toSpeak;
                    MainClass.ScreenReader.Say($"{details} {toSpeak}", true);
                }
            }
            catch (System.Exception e)
            {
                MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
            }
        }