/// <summary>Get the data to display for this subject.</summary> /// <param name="metadata">Provides metadata that's not available from the game data directly.</param> public override IEnumerable <ICustomField> GetData(Metadata metadata) { // get data Item item = this.Target; Object obj = item as Object; bool isCrop = this.FromCrop != null; bool isSeed = this.SeedForCrop != null; bool isDeadCrop = this.FromCrop?.dead == true; // get overrides bool showInventoryFields = true; { ObjectData objData = metadata.GetObject(item, this.Context); if (objData != null) { this.Name = objData.Name ?? this.Name; this.Description = objData.Description ?? this.Description; this.Type = objData.Type ?? this.Type; showInventoryFields = objData.ShowInventoryFields ?? true; } } // don't show data for dead crop if (isDeadCrop) { yield return(new GenericField("Crop", "This crop is dead.")); yield break; } // crop fields if (isCrop || isSeed) { // get crop Crop crop = this.FromCrop ?? this.SeedForCrop; // get harvest schedule int harvestablePhase = crop.phaseDays.Count - 1; bool canHarvestNow = (crop.currentPhase >= harvestablePhase) && (!crop.fullyGrown || crop.dayOfCurrentPhase <= 0); int daysToFirstHarvest = crop.phaseDays.Take(crop.phaseDays.Count - 1).Sum(); // ignore harvestable phase // add next-harvest field if (isCrop) { // calculate next harvest int daysToNextHarvest = 0; Tuple <string, int> dayOfNextHarvest = null; if (!canHarvestNow) { // calculate days until next harvest int daysUntilLastPhase = daysToFirstHarvest - crop.dayOfCurrentPhase - crop.phaseDays.Take(crop.currentPhase).Sum(); { // growing: days until next harvest if (!crop.fullyGrown) { daysToNextHarvest = daysUntilLastPhase; } // regrowable crop harvested today else if (crop.dayOfCurrentPhase >= crop.regrowAfterHarvest) { daysToNextHarvest = crop.regrowAfterHarvest; } // regrowable crop else { daysToNextHarvest = crop.dayOfCurrentPhase; // dayOfCurrentPhase decreases to 0 when fully grown, where <=0 is harvestable } } dayOfNextHarvest = GameHelper.GetDayOffset(daysToNextHarvest, metadata.Constants.DaysInSeason); } // generate field string summary; if (canHarvestNow) { summary = "now"; } else if (Game1.currentLocation.Name != Constant.LocationNames.Greenhouse && !crop.seasonsToGrowIn.Contains(dayOfNextHarvest.Item1)) { summary = $"too late in the season for the next harvest (would be on {dayOfNextHarvest.Item1} {dayOfNextHarvest.Item2})"; } else { summary = $"{dayOfNextHarvest.Item1} {dayOfNextHarvest.Item2} ({GameHelper.Pluralise(daysToNextHarvest, "tomorrow", $"in {daysToNextHarvest} days")})"; } yield return(new GenericField("Harvest", summary)); } // crop summary { List <string> summary = new List <string>(); // harvest summary.Add($"-harvest after {daysToFirstHarvest} {GameHelper.Pluralise(daysToFirstHarvest, "day")}" + (crop.regrowAfterHarvest != -1 ? $", then every {GameHelper.Pluralise(crop.regrowAfterHarvest, "day", $"{crop.regrowAfterHarvest} days")}" : "")); // seasons summary.Add($"-grows in {string.Join(", ", crop.seasonsToGrowIn)}"); // drops if (crop.minHarvest != crop.maxHarvest && crop.chanceForExtraCrops > 0) { summary.Add($"-drops {crop.minHarvest} to {crop.maxHarvest} ({Math.Round(crop.chanceForExtraCrops * 100, 2)}% chance of extra crops)"); } else if (crop.minHarvest > 1) { summary.Add($"-drops {crop.minHarvest}"); } // crop sale price Item drop = GameHelper.GetObjectBySpriteIndex(crop.indexOfHarvest); summary.Add($"-sells for {SaleValueField.GetSummary(this.GetSaleValue(drop, false), 1)}"); // generate field yield return(new GenericField("Crop", string.Join(Environment.NewLine, summary))); } } // crafting if (obj?.heldObject != null) { if (obj is Cask) { // get cask data Cask cask = (Cask)obj; Object agingObj = cask.heldObject; ItemQuality currentQuality = (ItemQuality)agingObj.quality; // calculate aging schedule float effectiveAge = metadata.Constants.CaskAgeSchedule.Values.Max() - cask.daysToMature; var schedule = ( from entry in metadata.Constants.CaskAgeSchedule let quality = entry.Key let baseDays = entry.Value where baseDays > effectiveAge orderby baseDays ascending let daysLeft = (int)Math.Ceiling((baseDays - effectiveAge) / cask.agingRate) select new { Quality = quality, DaysLeft = daysLeft, HarvestDate = GameHelper.GetDayOffset(daysLeft, metadata.Constants.DaysInSeason) } ) .ToArray(); // display fields yield return(new ItemIconField("Contents", obj.heldObject)); if (cask.minutesUntilReady <= 0 || !schedule.Any()) { yield return(new GenericField("Aging", $"{currentQuality.GetName()} quality ready")); } else { string scheduleStr = string.Join(Environment.NewLine, (from entry in schedule select $"-{entry.Quality.GetName()} {GameHelper.Pluralise(entry.DaysLeft, "tomorrow", $"in {entry.DaysLeft} days")} ({entry.HarvestDate.Item1} {entry.HarvestDate.Item2})")); yield return(new GenericField("Aging", $"-{currentQuality.GetName()} now (use pickaxe to stop aging){Environment.NewLine}" + scheduleStr)); } } else { yield return(new ItemIconField("Contents", obj.heldObject, $"{obj.heldObject.Name} " + (obj.minutesUntilReady > 0 ? "in " + GenericField.GetString(TimeSpan.FromMinutes(obj.minutesUntilReady)) : "ready"))); } } // item if (showInventoryFields) { var giftTastes = this.GetGiftTastes(item); if (!isCrop) { yield return(new SaleValueField("Sells for", this.GetSaleValue(item, this.KnownQuality), item.Stack)); } yield return(new ItemGiftTastesField("Loves this", giftTastes, GiftTaste.Love)); yield return(new ItemGiftTastesField("Likes this", giftTastes, GiftTaste.Like)); } // fence if (item is Fence) { Fence fence = (Fence)item; // health if (Game1.getFarm().isBuildingConstructed(Constant.BuildingNames.GoldClock)) { yield return(new GenericField("Health", "no decay with Gold Clock")); } else { float maxHealth = fence.isGate ? fence.maxHealth * 2 : fence.maxHealth; float health = fence.health / maxHealth; float daysLeft = fence.health * metadata.Constants.FenceDecayRate / 60 / 24; yield return(new PercentageBarField("Health", (int)fence.health, (int)maxHealth, Color.Green, Color.Red, $"{Math.Round(health * 100)}% (roughly {Math.Round(daysLeft)} days left)")); } } // recipes if (obj != null && obj.bigCraftable != true) { RecipeModel[] recipes = GameHelper.GetRecipesForIngredient(this.DisplayItem).ToArray(); if (recipes.Any()) { yield return(new RecipesForIngredientField("Recipes", item, recipes)); } } }
/// <summary>Get the data to display for this subject.</summary> /// <param name="metadata">Provides metadata that's not available from the game data directly.</param> public override IEnumerable <ICustomField> GetData(Metadata metadata) { NPC npc = this.Target; switch (this.TargetType) { case TargetType.Villager: if (!metadata.Constants.AsocialVillagers.Contains(npc.getName())) { var giftTastes = this.GetGiftTastes(npc); yield return(new GenericField("Birthday", $"{Utility.capitalizeFirstLetter(npc.birthday_Season)} {npc.birthday_Day}")); // friendship if (Game1.player.friendships.ContainsKey(npc.name)) { FriendshipModel friendship = DataParser.GetFriendshipForVillager(Game1.player, npc, metadata); yield return(new GenericField("Can romance", friendship.IsSpouse ? "you're married! <" : GenericField.GetString(npc.datable))); yield return(new CharacterFriendshipField("Friendship", friendship)); yield return(new GenericField("Talked today", Game1.player.friendships[npc.name][2] == 1)); yield return(new GenericField("Gifted today", Game1.player.friendships[npc.name][3] > 0)); if (!friendship.IsSpouse) { yield return(new GenericField("Gifted this week", $"{Game1.player.friendships[npc.name][1]} of {NPC.maxGiftsPerWeek}")); } } else { yield return(new GenericField("Friendship", "You haven't met them yet.")); } yield return(new CharacterGiftTastesField("Loves gifts", giftTastes, GiftTaste.Love)); yield return(new CharacterGiftTastesField("Likes gifts", giftTastes, GiftTaste.Like)); } break; case TargetType.Pet: Pet pet = (Pet)npc; yield return(new CharacterFriendshipField("Love", DataParser.GetFriendshipForPet(Game1.player, pet))); yield return(new GenericField("Petted today", GameHelper.GetPrivateField <bool>(pet, "wasPetToday"))); break; case TargetType.Monster: // basic info Monster monster = (Monster)npc; yield return(new GenericField("Invincible", $"For {GameHelper.GetPrivateField<int>(monster, "invincibleCountdown")} seconds", hasValue: monster.isInvincible())); yield return(new PercentageBarField("Health", monster.health, monster.maxHealth, Color.Green, Color.Gray, $"{Math.Round((monster.health / (monster.maxHealth * 1f) * 100))}% ({monster.health} of {monster.maxHealth})")); yield return(new ItemDropListField("Drops", this.GetMonsterDrops(monster), defaultText: "nothing")); yield return(new GenericField("XP", monster.experienceGained)); yield return(new GenericField("Defence", monster.resilience)); yield return(new GenericField("Attack", monster.damageToFarmer)); // Adventure Guild quest AdventureGuildQuestData adventureGuildQuest = metadata.GetAdventurerGuildQuest(monster.name); if (adventureGuildQuest != null) { int kills = adventureGuildQuest.Targets.Select(p => Game1.stats.getMonstersKilled(p)).Sum(); yield return(new GenericField("Adventure Guild", $"{(kills >= adventureGuildQuest.RequiredKills ? "complete" : "in progress")} (killed {kills} of {adventureGuildQuest.RequiredKills})")); } break; } }