private void Display_RenderedWorld(object sender, StardewModdingAPI.Events.RenderedWorldEventArgs e) { if (Game1.activeClickableMenu == null && PlacedAugmentorsManager.Instance != null) { GameLocation CurrentLocation = Game1.player.currentLocation; bool IsHoveringPlacedObject = CurrentLocation.Objects.TryGetValue(HoveredTile, out Object HoveredObject); if (IsHoveringPlacedObject) { Dictionary <AugmentorType, int> AttachedAugmentors = PlacedAugmentorsManager.Instance.GetAugmentorQuantities(CurrentLocation.NameOrUniqueName, HoveredTile); bool HasAttachedAugmentors = AttachedAugmentors.Any(x => x.Value > 0); if (MachineInfo.TryGetMachineInfo(HoveredObject, out MachineInfo MI)) { SpriteFont DefaultFont = Game1.dialogueFont; // Draw a tooltip showing what effects the held item will have when attached to this machine if (Game1.player.CurrentItem is Augmentor HeldAugmentor && HeldAugmentor.IsAugmentable(HoveredObject)) { AttachedAugmentors.TryGetValue(HeldAugmentor.AugmentorType, out int CurrentAttachedQuantity); double CurrentEffect = CurrentAttachedQuantity <= 0 ? Augmentor.GetDefaultEffect(HeldAugmentor.AugmentorType) : Augmentor.ComputeEffect(HeldAugmentor.AugmentorType, CurrentAttachedQuantity, MI.RequiresInput); double NewEffectSingle = Augmentor.ComputeEffect(HeldAugmentor.AugmentorType, CurrentAttachedQuantity + 1, MI.RequiresInput); double NewEffectAll = Augmentor.ComputeEffect(HeldAugmentor.AugmentorType, CurrentAttachedQuantity + HeldAugmentor.Stack, MI.RequiresInput); // Example of desired tooltip: // ------------------------------------------- // | [Icon] Time to process: | // |-----------------------------------------| // | Current Effect: 95.5% | // | New Effect: 92.2% (1) / 81.1% (4) | // ------------------------------------------- int Padding = 28; int MarginAfterIcon = 10; float LabelTextScale = 0.75f; float ValueTextScale = 1.0f; // Compute sizes of each row so we know how big the tooltip is, and can horizontally center the header and other rows // Compute size of header int HeaderHorizontalPadding = 4; Augmentor.TryGetIconDetails(HeldAugmentor.AugmentorType, out Texture2D IconTexture, out Rectangle IconSourcePosition, out float IconScale, out SpriteEffects IconEffects); Vector2 IconSize = new Vector2(IconSourcePosition.Width * IconScale, IconSourcePosition.Height * IconScale); string HeaderText = string.Format("{0}:", HeldAugmentor.GetEffectDescription()); Vector2 HeaderTextSize = DefaultFont.MeasureString(HeaderText) * LabelTextScale; float HeaderRowWidth = IconSize.X + MarginAfterIcon + HeaderTextSize.X + HeaderHorizontalPadding * 2; float HeaderRowHeight = Math.Max(IconSize.Y, HeaderTextSize.Y); // Compute size of horizontal separator int HorizontalSeparatorHeight = 6; int HorizontalSeparatorMargin = 8; // Compute size of the labels before the effect values int MarginAfterLabel = 8; string CurrentEffectLabel = string.Format("{0}:", Translate("CurrentEffectLabel")); Vector2 CurrentEffectLabelSize = DefaultFont.MeasureString(CurrentEffectLabel) * LabelTextScale; string NewEffectLabel = string.Format("{0}:", Translate("NewEffectLabel")); Vector2 NewEffectLabelSize = DefaultFont.MeasureString(NewEffectLabel) * LabelTextScale; float EffectLabelWidth = Math.Max(CurrentEffectLabelSize.X, NewEffectLabelSize.X); Vector2 EffectLabelSize = new Vector2(EffectLabelWidth, CurrentEffectLabelSize.Y + NewEffectLabelSize.Y); // Compute size of the effect values string CurrentEffectValue = string.Format("{0}% ({1})", (CurrentEffect * 100.0).ToString("0.##"), CurrentAttachedQuantity); Vector2 CurrentEffectValueSize = DrawHelpers.MeasureStringWithSpecialNumbers(CurrentEffectValue, ValueTextScale, 0.0f); string NewEffectValue; if (HeldAugmentor.Stack > 1) { NewEffectValue = string.Format("{0}% ({1}) / {2}% ({3})", (NewEffectSingle * 100.0).ToString("0.##"), (CurrentAttachedQuantity + 1), (NewEffectAll * 100.0).ToString("0.##"), (CurrentAttachedQuantity + HeldAugmentor.Stack)); } else { NewEffectValue = string.Format("{0}% ({1})", (NewEffectSingle * 100.0).ToString("0.##"), (CurrentAttachedQuantity + 1)); } Vector2 NewEffectValueSize = DrawHelpers.MeasureStringWithSpecialNumbers(NewEffectValue, ValueTextScale, 0.0f); Vector2 EffectContentSize = new Vector2(EffectLabelWidth + MarginAfterLabel + Math.Max(CurrentEffectValueSize.X, NewEffectValueSize.X), Math.Max(CurrentEffectLabelSize.Y, CurrentEffectValueSize.Y) + Math.Max(NewEffectLabelSize.Y, NewEffectValueSize.Y)); // Compute total size of tooltip, draw the background Vector2 ToolTipSize = new Vector2(Padding * 2 + Math.Max(HeaderRowWidth, EffectContentSize.X), Padding + HeaderRowHeight + HorizontalSeparatorMargin + HorizontalSeparatorHeight + HorizontalSeparatorMargin + EffectContentSize.Y + Padding); Point ToolTipTopleft = DrawHelpers.GetTopleftPosition(new Point((int)ToolTipSize.X, (int)ToolTipSize.Y), MouseScreenPosition, 100); DrawHelpers.DrawBox(e.SpriteBatch, new Rectangle(ToolTipTopleft.X, ToolTipTopleft.Y, (int)ToolTipSize.X, (int)ToolTipSize.Y)); float CurrentY = ToolTipTopleft.Y + Padding; // Draw the header float HeaderStartX = ToolTipTopleft.X + (ToolTipSize.X - HeaderRowWidth) / 2.0f; Vector2 IconPosition = new Vector2(HeaderStartX, CurrentY + (HeaderRowHeight - IconSize.Y) / 2.0f); e.SpriteBatch.Draw(IconTexture, IconPosition, IconSourcePosition, Color.White, 0f, Vector2.Zero, IconScale, IconEffects, 1f); Vector2 HeaderTextPosition = new Vector2(HeaderStartX + IconSize.X + MarginAfterIcon, CurrentY + (HeaderRowHeight - HeaderTextSize.Y) / 2.0f); e.SpriteBatch.DrawString(DefaultFont, HeaderText, HeaderTextPosition, Color.Black, 0.0f, Vector2.Zero, LabelTextScale, SpriteEffects.None, 1.0f); CurrentY += HeaderRowHeight + HorizontalSeparatorMargin; // Draw the horizontal separator DrawHelpers.DrawHorizontalSeparator(e.SpriteBatch, ToolTipTopleft.X + Padding, (int)CurrentY, (int)(ToolTipSize.X - 2 * Padding), HorizontalSeparatorHeight); CurrentY += HorizontalSeparatorHeight + HorizontalSeparatorMargin; // Draw the current effect Vector2 CurrentEffectLabelPosition = new Vector2(ToolTipTopleft.X + Padding + (EffectLabelWidth - CurrentEffectLabelSize.X), CurrentY); Vector2 CurrentEffectValuePosition = new Vector2(ToolTipTopleft.X + Padding + EffectLabelWidth + MarginAfterLabel, CurrentY); e.SpriteBatch.DrawString(DefaultFont, CurrentEffectLabel, CurrentEffectLabelPosition, Color.Black, 0.0f, Vector2.Zero, LabelTextScale, SpriteEffects.None, 1.0f); DrawHelpers.DrawStringWithSpecialNumbers(e.SpriteBatch, CurrentEffectValuePosition, CurrentEffectValue, ValueTextScale, Color.White); CurrentY += Math.Max(CurrentEffectLabelSize.Y, CurrentEffectValueSize.Y); // Draw the new effect Vector2 NewEffectLabelPosition = new Vector2(ToolTipTopleft.X + Padding + (EffectLabelWidth - NewEffectLabelSize.X), CurrentY); Vector2 NewEffectValuePosition = new Vector2(ToolTipTopleft.X + Padding + EffectLabelWidth + MarginAfterLabel, CurrentY); e.SpriteBatch.DrawString(DefaultFont, NewEffectLabel, NewEffectLabelPosition, Color.Black, 0.0f, Vector2.Zero, LabelTextScale, SpriteEffects.None, 1.0f); DrawHelpers.DrawStringWithSpecialNumbers(e.SpriteBatch, NewEffectValuePosition, NewEffectValue, ValueTextScale, Color.White); } // Draw a tooltip showing what effects are currently applied to this machine else if (HasAttachedAugmentors) { int Padding = 28; int MarginAfterIcon = 10; // Compute the size of each icon Dictionary <AugmentorType, Vector2> IconSizes = new Dictionary <AugmentorType, Vector2>(); foreach (KeyValuePair <AugmentorType, int> KVP in AttachedAugmentors.Where(x => x.Value > 0)) { Augmentor.TryGetIconDetails(KVP.Key, out Texture2D IconTexture, out Rectangle IconSourcePosition, out float IconScale, out SpriteEffects IconEffects); Vector2 IconSize = new Vector2(IconSourcePosition.Width * IconScale, IconSourcePosition.Height * IconScale); IconSizes.Add(KVP.Key, IconSize); } float IconColumnWidth = IconSizes.Values.Max(x => x.Y); // Compute the size of each row (each row shows the effect of a type of augmentor that has been applied to this machine) Dictionary <AugmentorType, Vector2> RowSizes = new Dictionary <AugmentorType, Vector2>(); foreach (KeyValuePair <AugmentorType, int> KVP in AttachedAugmentors.Where(x => x.Value > 0)) { double CurrentEffect = Augmentor.ComputeEffect(KVP.Key, KVP.Value, MI.RequiresInput); string Text = string.Format("{0}% ({1})", (CurrentEffect * 100.0).ToString("0.#"), KVP.Value); Vector2 TextSize = DrawHelpers.MeasureStringWithSpecialNumbers(Text, 1.0f, 4.0f); float RowWidth = IconColumnWidth + MarginAfterIcon + TextSize.X; float RowHeight = Math.Max(IconSizes[KVP.Key].Y, TextSize.Y); RowSizes.Add(KVP.Key, new Vector2(RowWidth, RowHeight)); } // Compute total size of tooltip, draw the background Vector2 ToolTipSize = new Vector2(Padding * 2 + RowSizes.Values.Max(x => x.X), Padding * 2 + RowSizes.Values.Sum(x => x.Y)); Point ToolTipTopleft = DrawHelpers.GetTopleftPosition(new Point((int)ToolTipSize.X, (int)ToolTipSize.Y), MouseScreenPosition, 100); DrawHelpers.DrawBox(e.SpriteBatch, new Rectangle(ToolTipTopleft.X, ToolTipTopleft.Y, (int)ToolTipSize.X, (int)ToolTipSize.Y)); float CurrentY = ToolTipTopleft.Y + Padding; // Draw each row float RowStartX = ToolTipTopleft.X + Padding; foreach (KeyValuePair <AugmentorType, int> KVP in AttachedAugmentors.Where(x => x.Value > 0)) { float CurrentX = RowStartX; float RowHeight = RowSizes[KVP.Key].Y; // Draw the icon Augmentor.TryGetIconDetails(KVP.Key, out Texture2D IconTexture, out Rectangle IconSourcePosition, out float IconScale, out SpriteEffects IconEffects); Vector2 IconSize = IconSizes[KVP.Key]; Vector2 IconPosition = new Vector2(CurrentX + (IconColumnWidth - IconSize.X) / 2.0f, CurrentY + (RowHeight - IconSize.Y) / 2.0f); e.SpriteBatch.Draw(IconTexture, IconPosition, IconSourcePosition, Color.White, 0f, Vector2.Zero, IconScale, IconEffects, 1f); CurrentX += IconColumnWidth + MarginAfterIcon; // Draw the value double CurrentEffect = Augmentor.ComputeEffect(KVP.Key, KVP.Value, MI.RequiresInput); string Text = string.Format("{0}% ({1})", (CurrentEffect * 100.0).ToString("0.#"), KVP.Value); Vector2 TextSize = DrawHelpers.MeasureStringWithSpecialNumbers(Text, 1.0f, 0.0f); Vector2 TextPosition = new Vector2(CurrentX, CurrentY + (RowHeight - TextSize.Y) / 2.0f); DrawHelpers.DrawStringWithSpecialNumbers(e.SpriteBatch, TextPosition, Text, 1.0f, Color.White); CurrentY += RowHeight; } //Maybe also show MinutesUntilReady if it's not ReadyForHarvest? } } } }
private void Display_RenderedWorld(object sender, RenderedWorldEventArgs e) { if (Game1.activeClickableMenu == null) { GameLocation CurrentLocation = Game1.player.currentLocation; bool IsHoveringPlacedObject = CurrentLocation.Objects.TryGetValue(HoveredTile, out SObject HoveredObject); if (IsHoveringPlacedObject) { if (UserConfig.DrawToolTip) { // Draw a tooltip that shows how many of the machine were combined, and its total combined processing power // Such as: "Quantity: 5\nPower: 465%" if (HoveredObject.TryGetCombinedQuantity(out int CombinedQuantity)) { float UIScaleFactor = Game1.options.zoomLevel / Game1.options.uiScale; SpriteFont DefaultFont = Game1.dialogueFont; int Padding = 25; int MarginBetweenColumns = 10; int MarginBetweenRows = 5; float LabelTextScale = 0.75f; float ValueTextScale = 1.0f; List <string> RowHeaders = new List <string>() { Helper.Translation.Get("ToolTipQuantityLabel"), Helper.Translation.Get("ToolTipPowerLabel") }; List <Vector2> RowHeaderSizes = RowHeaders.Select(x => DefaultFont.MeasureString(x) * LabelTextScale).ToList(); double ProcessingPower = UserConfig.ComputeProcessingPower(CombinedQuantity) * 100.0; string FormattedProcessingPower = string.Format("{0}%", ProcessingPower.ToString("#.#")); List <string> RowValues = new List <string>() { CombinedQuantity.ToString(), FormattedProcessingPower }; List <Vector2> RowValueSizes = RowValues.Select(x => DrawHelpers.MeasureStringWithSpecialNumbers(x, ValueTextScale, 0.0f)).ToList(); // Measure the tooltip List <int> RowHeights = new List <int>(); for (int i = 0; i < RowHeaders.Count; i++) { RowHeights.Add((int)Math.Max(RowHeaderSizes[i].Y, RowValueSizes[i].Y)); } List <int> ColumnWidths = new List <int> { (int)RowHeaderSizes.Max(x => x.X), (int)RowValueSizes.Max(x => x.X) }; int ToolTipTopWidth = Padding + ColumnWidths.Sum() + (ColumnWidths.Count - 1) * MarginBetweenColumns + Padding; int ToolTipHeight = Padding + RowHeights.Sum() + (RowHeights.Count - 1) * MarginBetweenRows + Padding; Point ToolTipTopleft = DrawHelpers.GetTopleftPosition(new Point(ToolTipTopWidth, ToolTipHeight), new Point((int)((MouseScreenPosition.X + UserConfig.ToolTipOffset.X) / UIScaleFactor), (int)((MouseScreenPosition.Y + UserConfig.ToolTipOffset.Y) / UIScaleFactor)), 100); // Draw tooltip background DrawHelpers.DrawBox(e.SpriteBatch, new Rectangle(ToolTipTopleft.X, ToolTipTopleft.Y, ToolTipTopWidth, ToolTipHeight)); // Draw each row's header and value int CurrentY = ToolTipTopleft.Y + Padding; for (int i = 0; i < RowHeights.Count; i++) { int CurrentRowHeight = RowHeights[i]; // Draw the row header Vector2 RowHeaderPosition = new Vector2( ToolTipTopleft.X + Padding + ColumnWidths[0] - RowHeaderSizes[i].X, CurrentY + (RowHeights[i] - RowHeaderSizes[i].Y) / 2.0f ); e.SpriteBatch.DrawString(DefaultFont, RowHeaders[i], RowHeaderPosition, Color.Black, 0.0f, Vector2.Zero, LabelTextScale, SpriteEffects.None, 1.0f); // Draw the row value Vector2 RowValuePosition = new Vector2( ToolTipTopleft.X + Padding + ColumnWidths[0] + MarginBetweenColumns, CurrentY + (RowHeights[i] - RowValueSizes[i].Y) / 2.0f ); DrawHelpers.DrawStringWithSpecialNumbers(e.SpriteBatch, RowValuePosition, RowValues[i], ValueTextScale, Color.White); CurrentY += CurrentRowHeight + MarginBetweenRows; } } } } } }
private void Display_RenderedWorld(object sender, RenderedWorldEventArgs e) { if (Game1.activeClickableMenu == null) { GameLocation CurrentLocation = Game1.player.currentLocation; bool IsHoveringPlacedObject = CurrentLocation.Objects.TryGetValue(HoveredTile, out SObject HoveredObject); if (IsHoveringPlacedObject) { if (UserConfig.DrawToolTip) { // Draw a tooltip that shows how many of the machine were combined, and its total combined processing power // Such as: "Quantity: 5\nPower: 465%" if (HoveredObject.TryGetCombinedQuantity(out int CombinedQuantity)) { Cask Cask = HoveredObject as Cask; bool IsCask = Cask != null; CrabPot CrabPot = HoveredObject as CrabPot; bool IsCrabPot = CrabPot != null; bool IsScarecrow = HoveredObject.IsScarecrow(); bool HasHeldObject = HoveredObject.heldObject?.Value != null; float UIScaleFactor = Game1.options.zoomLevel / Game1.options.uiScale; SpriteFont DefaultFont = Game1.dialogueFont; int Padding = 25; int MarginBetweenColumns = 10; int MarginBetweenRows = 5; float LabelTextScale = 0.75f; float ValueTextScale = 1.0f; bool ShowDurationInfo = UserConfig.ToolTipShowDuration && UserConfig.ShouldModifyProcessingSpeed(HoveredObject); bool ShowQuantityInfo = UserConfig.ToolTipShowQuantity && UserConfig.ShouldModifyInputsAndOutputs(HoveredObject); // Compute row headers List <string> RowHeaders = new List <string>() { Helper.Translation.Get("ToolTipQuantityLabel"), Helper.Translation.Get("ToolTipPowerLabel") }; if (ShowDurationInfo) { if (IsCask) { //RowHeaders.Add(Helper.Translation.Get("ToolTipCaskAgingRateLabel")); if (HasHeldObject) { RowHeaders.Add(Helper.Translation.Get("ToolTipCaskDaysUntilIridiumLabel")); } } else if (IsCrabPot) { RowHeaders.Add(Helper.Translation.Get("ToolTipCrabPotProcessingIntervalLabel")); } else { if (HasHeldObject) { RowHeaders.Add(Helper.Translation.Get("ToolTipMinutesRemainingLabel")); } } } if (ShowQuantityInfo) { if (HasHeldObject && HoveredObject.HasModifiedOutput()) { RowHeaders.Add(Helper.Translation.Get("ToolTipProducedQuantityLabel")); } } if (IsScarecrow) { RowHeaders.Add(Helper.Translation.Get("ToolTipScarecrowRadiusLabel")); } List <Vector2> RowHeaderSizes = RowHeaders.Select(x => DefaultFont.MeasureString(x) * LabelTextScale).ToList(); double ProcessingPower = UserConfig.ComputeProcessingPower(CombinedQuantity) * 100.0; string FormattedProcessingPower = string.Format("{0}%", ProcessingPower.ToString("0.#")); // Compute row values List <string> RowValues = new List <string>() { CombinedQuantity.ToString(), FormattedProcessingPower }; if (ShowDurationInfo) { if (IsCask) { //RowValues.Add(Cask.agingRate.Value.ToString("0.##")); if (HasHeldObject) { RowValues.Add(Math.Ceiling(Cask.daysToMature.Value / Cask.agingRate.Value).ToString("0.##")); } } else if (IsCrabPot) { CrabPot.TryGetProcessingInterval(out double Power, out double IntervalHours, out int IntervalMinutes); RowValues.Add(IntervalMinutes.ToString()); } else { if (HasHeldObject) { RowValues.Add(HoveredObject.MinutesUntilReady.ToString("0.##")); } } } if (ShowQuantityInfo) { if (HasHeldObject && HoveredObject.HasModifiedOutput()) { RowValues.Add(HoveredObject.heldObject.Value.Stack.ToString()); } } if (IsScarecrow) { // Subtract 1 because the game internally counts the scarecrow's occupied tile as part of its radius, but users usually would be confused by that // So a typical user expects radius=8 for a regular scarecrow, even though the game does its calculations with radius=9 int OriginalRadius = HoveredObject.GetScarecrowBaseRadius() - 1; int AlteredRadius = HoveredObject.GetScarecrowRadius() - 1; //RowValues.Add(string.Format("{0}-->{1}", OriginalRadius, AlteredRadius)); RowValues.Add(AlteredRadius.ToString()); } List <Vector2> RowValueSizes = RowValues.Select(x => DrawHelpers.MeasureStringWithSpecialNumbers(x, ValueTextScale, 0.0f)).ToList(); // Measure the tooltip List <int> RowHeights = new List <int>(); for (int i = 0; i < RowHeaders.Count; i++) { RowHeights.Add((int)Math.Max(RowHeaderSizes[i].Y, RowValueSizes[i].Y)); } List <int> ColumnWidths = new List <int> { (int)RowHeaderSizes.Max(x => x.X), (int)RowValueSizes.Max(x => x.X) }; int ToolTipTopWidth = Padding + ColumnWidths.Sum() + (ColumnWidths.Count - 1) * MarginBetweenColumns + Padding; int ToolTipHeight = Padding + RowHeights.Sum() + (RowHeights.Count - 1) * MarginBetweenRows + Padding; Point ToolTipTopleft = DrawHelpers.GetTopleftPosition(new Point(ToolTipTopWidth, ToolTipHeight), new Point((int)((MouseScreenPosition.X + UserConfig.ToolTipOffset.X) / UIScaleFactor), (int)((MouseScreenPosition.Y + UserConfig.ToolTipOffset.Y) / UIScaleFactor)), 100); // Draw tooltip background DrawHelpers.DrawBox(e.SpriteBatch, new Rectangle(ToolTipTopleft.X, ToolTipTopleft.Y, ToolTipTopWidth, ToolTipHeight)); // Draw each row's header and value int CurrentY = ToolTipTopleft.Y + Padding; for (int i = 0; i < RowHeights.Count; i++) { int CurrentRowHeight = RowHeights[i]; // Draw the row header Vector2 RowHeaderPosition = new Vector2( ToolTipTopleft.X + Padding + ColumnWidths[0] - RowHeaderSizes[i].X, CurrentY + (RowHeights[i] - RowHeaderSizes[i].Y) / 2.0f ); e.SpriteBatch.DrawString(DefaultFont, RowHeaders[i], RowHeaderPosition, Color.Black, 0.0f, Vector2.Zero, LabelTextScale, SpriteEffects.None, 1.0f); // Draw the row value Vector2 RowValuePosition = new Vector2( ToolTipTopleft.X + Padding + ColumnWidths[0] + MarginBetweenColumns, CurrentY + (RowHeights[i] - RowValueSizes[i].Y) / 2.0f ); DrawHelpers.DrawStringWithSpecialNumbers(e.SpriteBatch, RowValuePosition, RowValues[i], ValueTextScale, Color.White); CurrentY += CurrentRowHeight + MarginBetweenRows; } } } } } }