diff --git a/Daybreak/Controls/SnowfallOverlay.xaml b/Daybreak/Controls/SnowfallOverlay.xaml index c2fd47d4..55ec298c 100644 --- a/Daybreak/Controls/SnowfallOverlay.xaml +++ b/Daybreak/Controls/SnowfallOverlay.xaml @@ -5,11 +5,19 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Daybreak.Controls" xmlns:effects="clr-namespace:System.Windows.Media.Extensions.Effects;assembly=WpfExtended" + xmlns:converters="clr-namespace:Daybreak.Converters" mc:Ignorable="d" Loaded="UserControl_Loaded" Unloaded="UserControl_Unloaded" x:Name="_this" d:DesignHeight="450" d:DesignWidth="800"> + + + + + + + @@ -39,7 +47,9 @@ - + @@ -54,7 +64,9 @@ - + @@ -69,7 +81,9 @@ - + @@ -84,7 +98,9 @@ - + @@ -99,7 +115,9 @@ - + diff --git a/Daybreak/Controls/SnowfallOverlay.xaml.cs b/Daybreak/Controls/SnowfallOverlay.xaml.cs index 42b84ca1..ab23c52d 100644 --- a/Daybreak/Controls/SnowfallOverlay.xaml.cs +++ b/Daybreak/Controls/SnowfallOverlay.xaml.cs @@ -27,6 +27,7 @@ public partial class SnowfallOverlay : UserControl private double flakeSize4; [GenerateDependencyProperty] private double flakeSize5; + [GenerateDependencyProperty] private double time; @@ -111,7 +112,7 @@ private double GetNoise(double source) { var f = Frequencies[i]; var a = Amplitudes[i]; - returnValue += a * Math.Sin(f * source * Math.PI * 2); + returnValue += a * Math.Sin(f * source * Math.PI); } return returnValue / Divisor; diff --git a/Daybreak/Converters/DoubleMultiplierConverter.cs b/Daybreak/Converters/DoubleMultiplierConverter.cs new file mode 100644 index 00000000..13fd8df8 --- /dev/null +++ b/Daybreak/Converters/DoubleMultiplierConverter.cs @@ -0,0 +1,24 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace Daybreak.Converters; +public sealed class DoubleMultiplierConverter : IValueConverter +{ + public double Multiplier { get; set; } + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is not double d) + { + return value; + } + + return d * this.Multiplier; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/Daybreak/Daybreak.csproj b/Daybreak/Daybreak.csproj index c1155fa3..2fe89760 100644 --- a/Daybreak/Daybreak.csproj +++ b/Daybreak/Daybreak.csproj @@ -13,7 +13,7 @@ preview Daybreak.ico true - 0.9.8.146 + 0.9.8.147 true cfb2a489-db80-448d-a969-80270f314c46 True diff --git a/Daybreak/Launch/MainWindow.xaml b/Daybreak/Launch/MainWindow.xaml index 46f7be71..f8071740 100644 --- a/Daybreak/Launch/MainWindow.xaml +++ b/Daybreak/Launch/MainWindow.xaml @@ -81,16 +81,16 @@ ? ValidLocations { get; init; } + + private Event() + { + } + + public static readonly Event Wintersday = new() + { + Name = "Wintersday", + ValidLocations = new List + { + Map.AscalonCityWintersdayOutpost, + Map.DroknarsForgeWintersdayOutpost, + Map.EyeOfTheNorthOutpostWintersdayOutpost, + Map.KamadanJewelOfIstanWintersdayOutpost, + Map.TheGreatSnowballFightoftheGodsFightinginaWinterWonderland, + Map.TravelersVale, + Map.BorlisPass, + Map.IronHorseMine, + Map.TheFrostGate, + Map.AnvilRock, + Map.IceToothCaveOutpost, + Map.DeldrimorBowl, + Map.BeaconsPerchOutpost, + Map.GriffonsMouth, + Map.DroknarsForgeOutpost, + Map.WitmansFolly, + Map.PortSledgeOutpost, + Map.TalusChute, + Map.IceCavesofSorrow, + Map.CampRankorOutpost, + Map.SnakeDance, + Map.DreadnoughtsDrift, + Map.LornarsPass, + Map.GrenthsFootprint, + Map.SpearheadPeak, + Map.TheGraniteCitadelOutpost, + Map.TascasDemise, + Map.MineralSprings, + Map.Icedome, + Map.CopperhammerMinesOutpost, + Map.FrozenForest, + Map.IronMinesofMoladune, + Map.IceFloe, + Map.MarhansGrottoOutpost, + Map.ThunderheadKeep, + Map.IceCliffChasms, + Map.GunnarsHoldOutpost, + Map.NorrhartDomains, + Map.OlafsteadOutpost, + Map.VarajarFells, + Map.SifhallaOutpost, + Map.DrakkarLake, + Map.JagaMoraine, + Map.BjoraMarches + } + }; +} diff --git a/Daybreak/Services/Screenshots/Models/Location.cs b/Daybreak/Services/Screenshots/Models/Location.cs index 5a819de7..7d7af1aa 100644 --- a/Daybreak/Services/Screenshots/Models/Location.cs +++ b/Daybreak/Services/Screenshots/Models/Location.cs @@ -1,4 +1,5 @@ using Daybreak.Models.Guildwars; +using LiveChartsCore.Geo; using System.Collections.Generic; namespace Daybreak.Services.Screenshots.Models; @@ -591,6 +592,15 @@ internal sealed class Location StartIndex = 1, Count = 21 }, + new Entry + { + Map = Map.SanctumCay, + Url = "https://i.imgur.com/9jrmIAM.jpeg", + Credit = "https://imgur.com/a/PzYch4c", + IdFormat = "D2", + StartIndex = 1, + Count = 1 + } }); public static readonly Location MaguumaJungle = new( Region.MaguumaJungle, @@ -740,6 +750,15 @@ internal sealed class Location StartIndex = 1, Count = 12 }, + new Entry + { + Map = Map.MamnoonLagoon, + Url = "https://i.imgur.com/d78EuZt.jpeg", + Credit = "https://imgur.com/a/PzYch4c", + IdFormat = "D2", + StartIndex = 1, + Count = 1 + } }); public static readonly Location CrystalDesert = new( Region.CrystalDesert, @@ -1129,6 +1148,15 @@ internal sealed class Location StartIndex = 1, Count = 57 }, + new Entry + { + Map = Map.MineralSprings, + Url = "https://i.imgur.com/CFP4AmT.jpeg", + Credit = "https://imgur.com/a/PzYch4c", + IdFormat = "D2", + StartIndex = 1, + Count = 1 + } }); public static readonly Location RingOfFireIslandChain = new( Region.RingOfFireIslands, @@ -1179,6 +1207,24 @@ internal sealed class Location StartIndex = 1, Count = 38 }, + new Entry + { + Map = Map.RingOfFire, + Url = "https://i.imgur.com/srejpIP.jpeg", + Credit = "https://imgur.com/a/PzYch4c", + IdFormat = "D2", + StartIndex = 1, + Count = 1 + }, + new Entry + { + Map = Map.RingOfFire, + Url = "https://i.imgur.com/70Oc160.jpeg", + Credit = "https://imgur.com/a/PzYch4c", + IdFormat = "D2", + StartIndex = 1, + Count = 1 + } }); public static readonly Location FarShiverpeaks = new( Region.FarShiverpeaks, @@ -1351,6 +1397,15 @@ internal sealed class Location StartIndex = 1, Count = 60 }, + new Entry + { + Map = Map.GrothmarWardowns, + Url = "https://i.imgur.com/aECgPky.jpeg", + Credit = "https://imgur.com/a/PzYch4c", + IdFormat = "D2", + StartIndex = 1, + Count = 1 + } }); public static readonly Location TarnishedCoast = new( Region.TarnishedCoast, @@ -3586,6 +3641,15 @@ internal sealed class Location StartIndex = 1, Count = 35 }, + new Entry + { + Map = Map.SunwardMarches, + Url = "https://i.imgur.com/8pSyjun.jpeg", + Credit = "https://imgur.com/a/PzYch4c", + IdFormat = "D2", + StartIndex = 1, + Count = 1 + } }); public static readonly Location Vabbi = new( Region.Vabbi, @@ -4096,6 +4160,15 @@ internal sealed class Location StartIndex = 1, Count = 4 }, + new Entry + { + Map = Map.NightfallenJahai, + Url = "https://i.imgur.com/2tKgyv4.jpeg", + Credit = "https://imgur.com/a/PzYch4c", + IdFormat = "D2", + StartIndex = 1, + Count = 1 + } }); public static readonly Location BattleIsles = new( Region.TheBattleIsles, diff --git a/Daybreak/Services/Screenshots/OnlinePictureClient.cs b/Daybreak/Services/Screenshots/OnlinePictureClient.cs index 4debb9ab..d0202060 100644 --- a/Daybreak/Services/Screenshots/OnlinePictureClient.cs +++ b/Daybreak/Services/Screenshots/OnlinePictureClient.cs @@ -5,6 +5,7 @@ using Daybreak.Services.Screenshots.Models; using Microsoft.Extensions.Logging; using System; +using System.Collections.Generic; using System.Configuration; using System.Core.Extensions; using System.Extensions; @@ -13,6 +14,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using System.Windows.Documents; using System.Windows.Media; namespace Daybreak.Services.Screenshots; @@ -21,7 +23,7 @@ public sealed class OnlinePictureClient : IOnlinePictureClient { private const string CloudFlareCookieValue = "fcfd523b2470336531e47baff3d2c2d6a0e2412a.1689426482.1"; private const string CloudFlareCookieKey = "wschkid"; - private const string CacheFolder = "Bloogum"; + private const string CacheFolder = "ImageCache"; private readonly IImageCache imageCache; private readonly IGuildwarsMemoryCache guildwarsMemoryCache; private readonly IHttpClient httpClient; @@ -47,7 +49,7 @@ public OnlinePictureClient( public async Task<(ImageSource? Source, string Credit)> GetImage(bool localized) { (var uri, var credit) = await this.GetImageUri(localized); - var localUri = Path.GetFullPath(Path.Combine(CacheFolder, uri)).Replace("http:\\", ""); + var localUri = Path.GetFullPath(Path.Combine(CacheFolder, uri)).Replace("https:\\", "").Replace("http:\\", ""); if (!File.Exists(localUri)) { var imageStream = await this.GetRemoteImage(uri); @@ -65,11 +67,54 @@ public OnlinePictureClient( private async Task<(string Uri, string CreditText)> GetImageUri(bool localized) { + if (this.themeOptions.Value.WintersdayMode) + { + return await this.GetWintersdayUri(localized); + } + if (!localized) { return GetRandomScreenShot(); } + return await this.GetLocalizedImageUri(); + } + + private async Task<(string Uri, string CreditText)> GetWintersdayUri(bool localized) + { + var validEntries = Location.Locations + .SelectMany(l => l.Entries) + .Where(e => Models.Event.Wintersday.ValidLocations!.Any(map => e.Map == map)); + if (localized) + { + WorldData? worldInfo = default; + try + { + worldInfo = await this.guildwarsMemoryCache.ReadWorldData(CancellationToken.None); + } + catch (Exception ex) when (ex is TimeoutException or TaskCanceledException or HttpRequestException) + { + this.logger.LogInformation("Could not retrieve world data. Returning random screenshot"); + } + + if (worldInfo?.Map is Map map) + { + var localizedEntries = validEntries.Where(e => e.Map == map).ToList(); + if (localizedEntries.Count > 0) + { + var selectedEntry = localizedEntries[Random.Shared.Next(0, localizedEntries.Count)]; + return GetScreenshotName(selectedEntry, selectedEntry.StartIndex ?? 0 + Random.Shared.Next(0, selectedEntry.Count ?? 0)); + } + } + } + + var finalEntries = validEntries.ToList(); + var selectedFinalEntry = finalEntries[Random.Shared.Next(0, finalEntries.Count)]; + return GetScreenshotName(selectedFinalEntry, selectedFinalEntry.StartIndex ?? 0 + Random.Shared.Next(0, selectedFinalEntry.Count ?? 0)); + } + + private async Task<(string Uri, string CreditText)> GetLocalizedImageUri() + { WorldData? worldInfo = default; try { @@ -85,8 +130,13 @@ public OnlinePictureClient( return GetRandomScreenShot(); } - var validLocations = Location.Locations.Where(l => l.Region == worldInfo!.Region).ToList(); - var validCategories = validLocations.SelectMany(l => l.Entries).Where(c => c.Map == worldInfo!.Map).ToList(); + var validLocations = Location.Locations + .Where(l => l.Region == worldInfo!.Region) + .ToList(); + var validCategories = validLocations + .SelectMany(l => l.Entries) + .Where(c => c.Map == worldInfo!.Map) + .ToList(); if (validCategories.None()) { if (validLocations.None()) @@ -98,6 +148,11 @@ public OnlinePictureClient( return GetRandomScreenShot(location); } + return this.GetImageUri(validLocations, validCategories); + } + + private (string Uri, string CreditText) GetImageUri(List validLocations, List validCategories) + { var selectedCategory = validCategories[Random.Shared.Next(0, validCategories.Count)]; if (selectedCategory.Count == 0) {