diff --git a/UIInfoSuite2/Infrastructure/SoundHelper.cs b/UIInfoSuite2/Infrastructure/SoundHelper.cs new file mode 100644 index 00000000..272d85d7 --- /dev/null +++ b/UIInfoSuite2/Infrastructure/SoundHelper.cs @@ -0,0 +1,76 @@ +using System; +using System.IO; +using Microsoft.Xna.Framework.Audio; +using StardewModdingAPI; +using StardewValley; + +namespace UIInfoSuite2.Infrastructure; + +public enum Sounds +{ + LevelUp +} + +public class SoundHelper +{ + private static readonly Lazy _instance = new(() => new SoundHelper()); + + private bool _initialized; + + protected SoundHelper() { } + + public static SoundHelper Instance => _instance.Value; + + public void Initialize(IModHelper helper) + { + if (_initialized) + { + throw new InvalidOperationException("Cannot re-initialize sound helper"); + } + + RegisterSound(helper, Sounds.LevelUp, "LevelUp.wav"); + + _initialized = true; + } + + private static string GetQualifiedSoundName(Sounds sound) + { + return $"UIInfoSuite.sounds.{sound.ToString()}"; + } + + private static void RegisterSound( + IModHelper helper, + Sounds sound, + string fileName, + int instanceLimit = -1, + CueDefinition.LimitBehavior? limitBehavior = null + ) + { + CueDefinition newCueDefinition = new() { name = GetQualifiedSoundName(sound) }; + + if (instanceLimit > 0) + { + newCueDefinition.instanceLimit = instanceLimit; + newCueDefinition.limitBehavior = limitBehavior ?? CueDefinition.LimitBehavior.ReplaceOldest; + } else if (limitBehavior.HasValue) + { + newCueDefinition.limitBehavior = limitBehavior.Value; + } + + SoundEffect audio; + string filePath = Path.Combine(helper.DirectoryPath, "assets", fileName); + using (var stream = new FileStream(filePath, FileMode.Open)) + { + audio = SoundEffect.FromStream(stream); + } + + newCueDefinition.SetSound(audio, Game1.audioEngine.GetCategoryIndex("Sound")); + Game1.soundBank.AddCue(newCueDefinition); + ModEntry.MonitorObject.Log($"Registered Sound: {newCueDefinition.name}"); + } + + public static void Play(Sounds sound) + { + Game1.playSound(GetQualifiedSoundName(sound)); + } +} diff --git a/UIInfoSuite2/ModEntry.cs b/UIInfoSuite2/ModEntry.cs index 191fddc4..da9ff268 100644 --- a/UIInfoSuite2/ModEntry.cs +++ b/UIInfoSuite2/ModEntry.cs @@ -33,6 +33,8 @@ public override void Entry(IModHelper helper) #region Generic mod config menu private void OnGameLaunched(object sender, GameLaunchedEventArgs e) { + SoundHelper.Instance.Initialize(Helper); + // get Generic Mod Config Menu's API (if it's installed) ISemanticVersion? modVersion = Helper.ModRegistry.Get("spacechase0.GenericModConfigMenu")?.Manifest?.Version; var minModVersion = "1.6.0"; diff --git a/UIInfoSuite2/Options/ModOptionsPageHandler.cs b/UIInfoSuite2/Options/ModOptionsPageHandler.cs index 4fdfbd54..6f460065 100644 --- a/UIInfoSuite2/Options/ModOptionsPageHandler.cs +++ b/UIInfoSuite2/Options/ModOptionsPageHandler.cs @@ -99,7 +99,6 @@ public ModOptionsPageHandler(IModHelper helper, ModOptions options, bool showPer showWhenAnimalNeedsPet, showCalendarAndBillboardOnGameMenuButton, showCropAndBarrelTime, - experienceBar, showItemHoverInformation, showTravelingMerchant, showRainyDayIcon, diff --git a/UIInfoSuite2/UIElements/ExperienceBar.cs b/UIInfoSuite2/UIElements/ExperienceBar.cs index 61587c9a..eceb1c0a 100644 --- a/UIInfoSuite2/UIElements/ExperienceBar.cs +++ b/UIInfoSuite2/UIElements/ExperienceBar.cs @@ -1,9 +1,5 @@ -using System; using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Audio; using StardewModdingAPI; using StardewModdingAPI.Enums; using StardewModdingAPI.Events; @@ -17,7 +13,7 @@ namespace UIInfoSuite2.UIElements; -public partial class ExperienceBar : IDisposable +public partial class ExperienceBar { #region Properties private readonly PerScreen _previousItem = new(); @@ -47,7 +43,8 @@ public partial class ExperienceBar : IDisposable { SkillType.Fishing, new Rectangle(20, 428, 10, 10) }, { SkillType.Foraging, new Rectangle(60, 428, 10, 10) }, { SkillType.Mining, new Rectangle(30, 428, 10, 10) }, - { SkillType.Combat, new Rectangle(120, 428, 10, 10) } + { SkillType.Combat, new Rectangle(120, 428, 10, 10) }, + { SkillType.Luck, new Rectangle(50, 428, 10, 10) } }; private static readonly Dictionary ExperienceFillColor = new() @@ -56,7 +53,8 @@ public partial class ExperienceBar : IDisposable { SkillType.Fishing, new Color(17, 84, 252, 0.63f) }, { SkillType.Foraging, new Color(0, 234, 0, 0.63f) }, { SkillType.Mining, new Color(145, 104, 63, 0.63f) }, - { SkillType.Combat, new Color(204, 0, 3, 0.63f) } + { SkillType.Combat, new Color(204, 0, 3, 0.63f) }, + { SkillType.Luck, new Color(232, 223, 42, 0.63f) } }; private readonly PerScreen _experienceIconRectangle = new(() => SkillIconRectangles[SkillType.Farming]); @@ -64,8 +62,6 @@ public partial class ExperienceBar : IDisposable private readonly PerScreen _levelUpIconRectangle = new(() => SkillIconRectangles[SkillType.Farming]); private readonly PerScreen _experienceFillColor = new(() => ExperienceFillColor[SkillType.Farming]); - private SoundEffectInstance _soundEffect; - private bool ExperienceBarFadeoutEnabled { get; set; } = true; private bool ExperienceGainTextEnabled { get; set; } = true; private bool LevelUpAnimationEnabled { get; set; } = true; @@ -80,36 +76,12 @@ public ExperienceBar(IModHelper helper) { _helper = helper; - InitializeSound(); - if (_helper.ModRegistry.IsLoaded("DevinLematty.LevelExtender")) { _levelExtenderApi = _helper.ModRegistry.GetApi("DevinLematty.LevelExtender"); } } - private void InitializeSound() - { - var path = string.Empty; - try - { - path = Path.Combine(_helper.DirectoryPath, "assets", "LevelUp.wav"); - _soundEffect = SoundEffect.FromStream(new FileStream(path, FileMode.Open)).CreateInstance(); - } - catch (Exception ex) - { - ModEntry.MonitorObject.Log( - "Error loading sound file from " + path + ": " + ex.Message + Environment.NewLine + ex.StackTrace, - LogLevel.Error - ); - } - } - - public void Dispose() - { - _soundEffect.Dispose(); - } - public void ToggleOption( bool experienceBarEnabled, bool experienceBarFadeoutEnabled, @@ -189,7 +161,7 @@ private void OnLevelChanged(object sender, LevelChangedEventArgs e) _experienceBarVisibleTimer.Value = ExperienceBarVisibleTicks; - PlayLevelUpSoundEffect(); + SoundHelper.Play(Sounds.LevelUp); } } @@ -298,23 +270,6 @@ private void InitializeExperiencePoints() } } - private void PlayLevelUpSoundEffect() - { - if (_soundEffect == null) - { - return; - } - - _soundEffect.Volume = Game1.options.soundVolumeLevel; - Task.Factory.StartNew( - async () => - { - await Task.Delay(200); - _soundEffect?.Play(); - } - ); - } - private bool TryGetCurrentLevelIndexFromSkillChange(out int currentLevelIndex) { currentLevelIndex = -1; diff --git a/UIInfoSuite2/UIElements/ShowItemEffectRanges.cs b/UIInfoSuite2/UIElements/ShowItemEffectRanges.cs index eef0956d..14f72bf4 100644 --- a/UIInfoSuite2/UIElements/ShowItemEffectRanges.cs +++ b/UIInfoSuite2/UIElements/ShowItemEffectRanges.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; @@ -16,7 +17,7 @@ namespace UIInfoSuite2.UIElements; internal class ShowItemEffectRanges : IDisposable { #region Properties - private readonly PerScreen> _effectiveArea = new(() => new List()); + private readonly PerScreen> _effectiveArea = new(() => new HashSet()); private readonly Mutex _mutex = new(); @@ -185,7 +186,15 @@ private void UpdateEffectiveArea() * and now it's the tile that's being hovered over. * That new behavior might not be intended and might get rolled back. */ - AddTilesToHighlightedArea(currentItem.GetSprinklerTiles()); + + // Move tiles to 0, 0 and then offset by the correct tile. + IEnumerable unplacedSprinklerTiles = currentItem.GetSprinklerTiles(); + if (currentItem.TileLocation != validTile) + { + unplacedSprinklerTiles = unplacedSprinklerTiles.Select(tile => tile - currentItem.TileLocation + validTile); + } + + AddTilesToHighlightedArea(unplacedSprinklerTiles); similarObjects = GetSimilarObjectsInLocation("sprinkler"); foreach (Object next in similarObjects) diff --git a/UIInfoSuite2/UIElements/ShowItemHoverInformation.cs b/UIInfoSuite2/UIElements/ShowItemHoverInformation.cs index 8910f721..d6920595 100644 --- a/UIInfoSuite2/UIElements/ShowItemHoverInformation.cs +++ b/UIInfoSuite2/UIElements/ShowItemHoverInformation.cs @@ -277,7 +277,8 @@ private void DrawAdvancedTooltip(SpriteBatch spriteBatch) bool notShippedYet = hoveredObject != null && hoveredObject.countsForShippedCollection() && !Game1.player.basicShipped.ContainsKey(hoveredObject.ItemId) && - hoveredObject.Type != "Fish"; + hoveredObject.Type != "Fish" && + hoveredObject.Category != Object.skillBooksCategory; if (notShippedYet && hoveredObject != null && ModEntry.DGA.IsCustomObject(hoveredObject, out DynamicGameAssetsHelper? dgaHelper)) diff --git a/UIInfoSuite2/UIElements/ShowRainyDayIcon.cs b/UIInfoSuite2/UIElements/ShowRainyDayIcon.cs index 9a5007ef..4dd09e1f 100644 --- a/UIInfoSuite2/UIElements/ShowRainyDayIcon.cs +++ b/UIInfoSuite2/UIElements/ShowRainyDayIcon.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI; @@ -148,17 +149,20 @@ private void CreateTileSheet() // Setup Texture sheet as a copy, so as not to disturb existing sprites _iconSheet = new Texture2D(Game1.graphics.GraphicsDevice, WeatherSheetWidth, WeatherSheetHeight); _weatherIconColors = new Color[WeatherSheetWidth * WeatherSheetHeight]; + Texture2D weatherBorderTexture = Texture2D.FromFile(Game1.graphics.GraphicsDevice, Path.Combine(_helper.DirectoryPath, "assets", "weatherbox.png")); + var weatherBorderColors = new Color[15 * 15]; var cursorColors = new Color[Game1.mouseCursors.Width * Game1.mouseCursors.Height]; var cursorColors_1_6 = new Color[Game1.mouseCursors_1_6.Width * Game1.mouseCursors_1_6.Height]; var bounds = new Rectangle(0, 0, Game1.mouseCursors.Width, Game1.mouseCursors.Height); var bounds_1_6 = new Rectangle(0, 0, Game1.mouseCursors_1_6.Width, Game1.mouseCursors_1_6.Height); + weatherBorderTexture.GetData(weatherBorderColors); Game1.mouseCursors.GetData(cursorColors); Game1.mouseCursors_1_6.GetData(cursorColors_1_6); var subTextureColors = new Color[15 * 15]; // Copy over the bits we want // Border from TV screen - Tools.GetSubTexture(subTextureColors, cursorColors, bounds, new Rectangle(499, 307, 15, 15)); + Tools.GetSubTexture(subTextureColors, weatherBorderColors, new Rectangle(0, 0, 15, 15), new Rectangle(0, 0, 15, 15)); // Copy to each destination for (var i = 0; i < 4; i++) { diff --git a/UIInfoSuite2/UIInfoSuite2.csproj b/UIInfoSuite2/UIInfoSuite2.csproj index 997246df..636f17e1 100644 --- a/UIInfoSuite2/UIInfoSuite2.csproj +++ b/UIInfoSuite2/UIInfoSuite2.csproj @@ -1,6 +1,6 @@ - 2.3.1 + 2.3.2 net6.0 enable $(SolutionDir)\Releases diff --git a/UIInfoSuite2/assets/weatherbox.png b/UIInfoSuite2/assets/weatherbox.png new file mode 100644 index 00000000..90cec56f Binary files /dev/null and b/UIInfoSuite2/assets/weatherbox.png differ diff --git a/UIInfoSuite2/manifest.json b/UIInfoSuite2/manifest.json index 1084a120..0ee857b4 100644 --- a/UIInfoSuite2/manifest.json +++ b/UIInfoSuite2/manifest.json @@ -1,11 +1,11 @@ { "Name": "UI Info Suite 2", "Author": "Annosz", - "Version": "2.3.1", + "Version": "2.3.2", "Description": "Adds a useful information to the user interface. Based on Cdaragorn's excellent UI Info Suite.", "UniqueID": "Annosz.UiInfoSuite2", "EntryDll": "UIInfoSuite2.dll", - "MinimumApiVersion": "4.0.0", + "MinimumApiVersion": "4.0.5", "UpdateKeys": [ "GitHub:Annosz/UIInfoSuite2" ]