diff --git a/CHANGELOG.md b/CHANGELOG.md index bb1423e8..2e8d5b4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ +## [0.41.0](https://github.com/DorielRivalet/MHFZ_Overlay/compare/v0.40.0...v0.41.0) (2024-10-27) + + +### Features + +* add hotkeys window ([d25af92](https://github.com/DorielRivalet/MHFZ_Overlay/commit/d25af927216c2561efe8ff352f26d4234d720eb7)), closes [#375](https://github.com/DorielRivalet/MHFZ_Overlay/issues/375) +* **database:** add party size dictionary ([20f34ab](https://github.com/DorielRivalet/MHFZ_Overlay/commit/20f34abd3d9f8d7d8b939a91a950d3170f198aa1)), closes [#388](https://github.com/DorielRivalet/MHFZ_Overlay/issues/388) +* **database:** reduce database size ([e30aacd](https://github.com/DorielRivalet/MHFZ_Overlay/commit/e30aacdcf5d9d4df46c78ab98dee54ba33cb3614)), closes [#389](https://github.com/DorielRivalet/MHFZ_Overlay/issues/389) +* update monster images ([ed67b0e](https://github.com/DorielRivalet/MHFZ_Overlay/commit/ed67b0ea0146d468e02f9372671524e95ff3a33f)) + + +### Bug Fixes + +* **achievement:** fix ds achievements ([eb66abe](https://github.com/DorielRivalet/MHFZ_Overlay/commit/eb66abe45ae18ce93a1572fbe2bb2efdf89742d6)), closes [#390](https://github.com/DorielRivalet/MHFZ_Overlay/issues/390) + + +### For Developers + +* bump version ([5c670a2](https://github.com/DorielRivalet/MHFZ_Overlay/commit/5c670a2f211656d8038200d26161034213be09ee)) +* **deps:** bump braces from 3.0.2 to 3.0.3 ([1c1bf79](https://github.com/DorielRivalet/MHFZ_Overlay/commit/1c1bf7994fafd172d208fb0525c8318f0e143fc3)) +* update README.md ([31b1db2](https://github.com/DorielRivalet/MHFZ_Overlay/commit/31b1db2299781a523c7ecac56866a6ef1a0afa2b)) + ## [0.40.0](https://github.com/DorielRivalet/MHFZ_Overlay/compare/v0.39.1...v0.40.0) (2024-06-19) diff --git a/MHFZ_Overlay/App.config b/MHFZ_Overlay/App.config index 51b382b6..76e0a0c0 100644 --- a/MHFZ_Overlay/App.config +++ b/MHFZ_Overlay/App.config @@ -1044,6 +1044,15 @@ True + + Shift + F1 + + + Shift + F5 + + + Shift + F6 + diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/azure_rathalos.png b/MHFZ_Overlay/Assets/Icons/png/monster/azure_rathalos.png index bf4293f2..6d402a51 100644 Binary files a/MHFZ_Overlay/Assets/Icons/png/monster/azure_rathalos.png and b/MHFZ_Overlay/Assets/Icons/png/monster/azure_rathalos.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/azure_rathalos.webp b/MHFZ_Overlay/Assets/Icons/png/monster/azure_rathalos.webp new file mode 100644 index 00000000..f4764c5f Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/azure_rathalos.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/conquest_shantien.png b/MHFZ_Overlay/Assets/Icons/png/monster/conquest_shantien.png index 842e8ea5..f8d65500 100644 Binary files a/MHFZ_Overlay/Assets/Icons/png/monster/conquest_shantien.png and b/MHFZ_Overlay/Assets/Icons/png/monster/conquest_shantien.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/conquest_shantien.webp b/MHFZ_Overlay/Assets/Icons/png/monster/conquest_shantien.webp new file mode 100644 index 00000000..13840efd Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/conquest_shantien.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/disufiroa.png b/MHFZ_Overlay/Assets/Icons/png/monster/disufiroa.png index 8b9fb4f1..a3c9f6c8 100644 Binary files a/MHFZ_Overlay/Assets/Icons/png/monster/disufiroa.png and b/MHFZ_Overlay/Assets/Icons/png/monster/disufiroa.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/disufiroa.webp b/MHFZ_Overlay/Assets/Icons/png/monster/disufiroa.webp new file mode 100644 index 00000000..87bbe3d3 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/disufiroa.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/gold_rathian.png b/MHFZ_Overlay/Assets/Icons/png/monster/gold_rathian.png index c0e9a9e4..3a295066 100644 Binary files a/MHFZ_Overlay/Assets/Icons/png/monster/gold_rathian.png and b/MHFZ_Overlay/Assets/Icons/png/monster/gold_rathian.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/gold_rathian.webp b/MHFZ_Overlay/Assets/Icons/png/monster/gold_rathian.webp new file mode 100644 index 00000000..87e67912 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/gold_rathian.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/pink_rathian.png b/MHFZ_Overlay/Assets/Icons/png/monster/pink_rathian.png index d6943ebe..2c6ca688 100644 Binary files a/MHFZ_Overlay/Assets/Icons/png/monster/pink_rathian.png and b/MHFZ_Overlay/Assets/Icons/png/monster/pink_rathian.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/pink_rathian.webp b/MHFZ_Overlay/Assets/Icons/png/monster/pink_rathian.webp new file mode 100644 index 00000000..082ef167 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/pink_rathian.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/rathalos.png b/MHFZ_Overlay/Assets/Icons/png/monster/rathalos.png index e3b1bb02..baa4cf39 100644 Binary files a/MHFZ_Overlay/Assets/Icons/png/monster/rathalos.png and b/MHFZ_Overlay/Assets/Icons/png/monster/rathalos.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/rathalos.webp b/MHFZ_Overlay/Assets/Icons/png/monster/rathalos.webp new file mode 100644 index 00000000..9fc2a520 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/rathalos.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/rathian.png b/MHFZ_Overlay/Assets/Icons/png/monster/rathian.png index 3abb2cf6..1922e43e 100644 Binary files a/MHFZ_Overlay/Assets/Icons/png/monster/rathian.png and b/MHFZ_Overlay/Assets/Icons/png/monster/rathian.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/rathian.webp b/MHFZ_Overlay/Assets/Icons/png/monster/rathian.webp new file mode 100644 index 00000000..644c56e9 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/rathian.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shantien.png b/MHFZ_Overlay/Assets/Icons/png/monster/shantien.png index 70504cfd..57e9b072 100644 Binary files a/MHFZ_Overlay/Assets/Icons/png/monster/shantien.png and b/MHFZ_Overlay/Assets/Icons/png/monster/shantien.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shantien.webp b/MHFZ_Overlay/Assets/Icons/png/monster/shantien.webp new file mode 100644 index 00000000..afcfbc55 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shantien.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_disufiroa.png b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_disufiroa.png index bc8650a7..9f68a205 100644 Binary files a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_disufiroa.png and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_disufiroa.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_disufiroa.webp b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_disufiroa.webp new file mode 100644 index 00000000..19c055f5 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_disufiroa.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown.gif b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown.gif new file mode 100644 index 00000000..482eb985 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown.gif differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown.png b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown.png index 2ab2da42..7c919ef3 100644 Binary files a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown.png and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown.webp b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown.webp new file mode 100644 index 00000000..e4c140ef Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase2.png b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase2.png new file mode 100644 index 00000000..0594fd6f Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase2.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase2.webp b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase2.webp new file mode 100644 index 00000000..d33df936 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase2.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase3.png b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase3.png new file mode 100644 index 00000000..730abe64 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase3.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase3.webp b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase3.webp new file mode 100644 index 00000000..17c23a06 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase3.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase4.png b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase4.png new file mode 100644 index 00000000..1eb716b0 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase4.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase4.webp b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase4.webp new file mode 100644 index 00000000..6ccdf93c Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase4.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase5.png b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase5.png new file mode 100644 index 00000000..da26ddd5 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase5.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase5.webp b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase5.webp new file mode 100644 index 00000000..f89d07e9 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase5.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase6.png b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase6.png new file mode 100644 index 00000000..471c846f Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase6.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase6.webp b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase6.webp new file mode 100644 index 00000000..3a47bbd6 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase6.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase7.png b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase7.png new file mode 100644 index 00000000..59409905 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase7.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase7.webp b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase7.webp new file mode 100644 index 00000000..f3662259 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase7.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase8.png b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase8.png new file mode 100644 index 00000000..21442b3f Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase8.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase8.webp b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase8.webp new file mode 100644 index 00000000..f7bd6f5d Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/shiten_unknown_phase8.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/silver_rathalos.png b/MHFZ_Overlay/Assets/Icons/png/monster/silver_rathalos.png index a43afd33..c096936e 100644 Binary files a/MHFZ_Overlay/Assets/Icons/png/monster/silver_rathalos.png and b/MHFZ_Overlay/Assets/Icons/png/monster/silver_rathalos.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/silver_rathalos.webp b/MHFZ_Overlay/Assets/Icons/png/monster/silver_rathalos.webp new file mode 100644 index 00000000..545b7aaf Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/silver_rathalos.webp differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/unknown.png b/MHFZ_Overlay/Assets/Icons/png/monster/unknown.png index 76267bfc..0467c9a3 100644 Binary files a/MHFZ_Overlay/Assets/Icons/png/monster/unknown.png and b/MHFZ_Overlay/Assets/Icons/png/monster/unknown.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/monster/unknown.webp b/MHFZ_Overlay/Assets/Icons/png/monster/unknown.webp new file mode 100644 index 00000000..99ab3763 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/monster/unknown.webp differ diff --git a/MHFZ_Overlay/MHFZ_Overlay.csproj b/MHFZ_Overlay/MHFZ_Overlay.csproj index 3fb4ff83..a759e1dc 100644 --- a/MHFZ_Overlay/MHFZ_Overlay.csproj +++ b/MHFZ_Overlay/MHFZ_Overlay.csproj @@ -21,7 +21,7 @@ MHFZ Overlay Doriel Rivalet Doriel Rivalet - 0.40.0 + 0.41.0 https://github.com/DorielRivalet/mhfz-overlay https://github.com/DorielRivalet/mhfz-overlay.git git @@ -2001,44 +2001,44 @@ - + - + - + - - - + + + - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + + + + + + - + diff --git a/MHFZ_Overlay/Models/Collections/Achievements.cs b/MHFZ_Overlay/Models/Collections/Achievements.cs index 3fe09973..09d7baa3 100644 --- a/MHFZ_Overlay/Models/Collections/Achievements.cs +++ b/MHFZ_Overlay/Models/Collections/Achievements.cs @@ -6260,7 +6260,7 @@ [] [U] [] [] [] Description = string.Empty, Rank = AchievementRank.Platinum, Image = @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/weapon/Dual_Blades_Icon_White.png", - Objective = "Defeat Lv9999 Conquest Shantien solo with Dual Swords without dropping the combo (maximum sharpening buff, Speedrun mode).", + Objective = "Defeat Lv9999 Conquest Shantien solo with Dual Swords without dropping the combo more than 3 times (maximum sharpening buff, Speedrun mode).", IsSecret = false, Hint = string.Empty, } @@ -6286,7 +6286,7 @@ [] [U] [] [] [] Description = string.Empty, Rank = AchievementRank.Platinum, Image = @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/weapon/Dual_Blades_Icon_White.png", - Objective = "Defeat Upper Shiten Disufiroa solo with Dual Swords without dropping the combo (maximum sharpening buff, Speedrun mode).", + Objective = "Defeat Upper Shiten Disufiroa solo with Dual Swords without dropping the combo more than 3 times (maximum sharpening buff, Speedrun mode).", IsSecret = false, Hint = string.Empty, } diff --git a/MHFZ_Overlay/Models/Quest.cs b/MHFZ_Overlay/Models/Quest.cs index 5cca939f..c993c315 100644 --- a/MHFZ_Overlay/Models/Quest.cs +++ b/MHFZ_Overlay/Models/Quest.cs @@ -5,6 +5,7 @@ namespace MHFZ_Overlay.Models; using System; +using System.Collections.Generic; // TODO: ORM // get the graphs from here @@ -86,4 +87,6 @@ public sealed class Quest public long? PartySize { get; set; } = 0; + public string? PartySizeDictionary { get; set; } = string.Empty; + } diff --git a/MHFZ_Overlay/Services/AchievementService.cs b/MHFZ_Overlay/Services/AchievementService.cs index 2dcb8c76..e26538ec 100644 --- a/MHFZ_Overlay/Services/AchievementService.cs +++ b/MHFZ_Overlay/Services/AchievementService.cs @@ -3111,7 +3111,7 @@ join styleRankSkills in databaseManagerInstance.AllStyleRankSkills on quest.RunI return false; } - if (databaseManagerInstance.AllQuestsWeaponBuffs.Any(q => q.RunID == foundConquestShantienQuest.First().RunID && q.DualSwordsSharpensDictionary.Count == 4 && q.DualSwordsSharpensDictionary.Count((e) => e.Value == 0) == 0)) + if (databaseManagerInstance.AllQuestsWeaponBuffs.Any(q => q.RunID == foundConquestShantienQuest.First().RunID && q.DualSwordsSharpensDictionary.Count >= 4 && q.DualSwordsSharpensDictionary.Count((e) => e.Value == 0) <= 3)) { return true; } @@ -3151,7 +3151,7 @@ join styleRankSkills in databaseManagerInstance.AllStyleRankSkills on quest.RunI return false; } - if (databaseManagerInstance.AllQuestsWeaponBuffs.Any(q => q.RunID == foundUpperShitenDisufiroaQuest.First().RunID && q.DualSwordsSharpensDictionary.Count == 4 && q.DualSwordsSharpensDictionary.Count((e) => e.Value == 0) == 0)) + if (databaseManagerInstance.AllQuestsWeaponBuffs.Any(q => q.RunID == foundUpperShitenDisufiroaQuest.First().RunID && q.DualSwordsSharpensDictionary.Count >= 4 && q.DualSwordsSharpensDictionary.Count((e) => e.Value == 0) <= 3)) { return true; } diff --git a/MHFZ_Overlay/Services/DatabaseService.cs b/MHFZ_Overlay/Services/DatabaseService.cs index 60b4ddaa..9bcad479 100644 --- a/MHFZ_Overlay/Services/DatabaseService.cs +++ b/MHFZ_Overlay/Services/DatabaseService.cs @@ -148,9 +148,9 @@ public void CheckIfUserSetDatabasePath() { var s = (Settings)System.Windows.Application.Current.TryFindResource("Settings"); - if (string.IsNullOrEmpty(s.DatabaseFilePath) || !Directory.Exists(Path.GetDirectoryName(s.DatabaseFilePath))) + if (string.IsNullOrEmpty(s.DatabaseFilePath) || !Directory.Exists(System.IO.Path.GetDirectoryName(s.DatabaseFilePath))) { - this.connectionString = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHFZ_Overlay\\MHFZ_Overlay.sqlite"); + this.connectionString = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHFZ_Overlay\\MHFZ_Overlay.sqlite"); // Show warning to user that they should set a custom database path to prevent data loss on update Logger.Warn(CultureInfo.InvariantCulture, "The database file is being saved to the overlay default location"); @@ -325,6 +325,7 @@ public bool SetupLocalDatabase(DataLoader dataLoader) this.CreateDatabaseIndexes(conn); this.CreateDatabaseTriggers(conn); this.CheckDatabaseVersion(conn, dataLoader); + this.VacuumDatabaseFile(conn); // TODO: avoid using version files. find alternatives to IO. WriteNewVersionToFile(); @@ -334,7 +335,7 @@ public bool SetupLocalDatabase(DataLoader dataLoader) { conn.Open(); - var referenceSchemaFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHFZ_Overlay\\reference_schema.json"); + var referenceSchemaFilePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHFZ_Overlay\\reference_schema.json"); var doesReferenceSchemaFileExist = FileService.CheckIfFileExists(referenceSchemaFilePath, "Checking reference schema"); @@ -518,7 +519,7 @@ public void InsertPersonalBest(DataLoader dataLoader, long currentPersonalBest, var createdAt = DateTime.UtcNow; var createdBy = ViewModels.Windows.AddressModel.GetFullCurrentProgramVersion(); var playerID = 1; - var partySize = dataLoader.Model.PartySize(); + var partySize = dataLoader.Model.PartySizeDictionary.FirstOrDefault().Value; using (var transaction = conn.BeginTransaction()) { @@ -588,7 +589,8 @@ public void InsertPersonalBest(DataLoader dataLoader, long currentPersonalBest, Monster1PartThresholdDictionary, Monster2PartThresholdDictionary, IsHighGradeEdition, - RefreshRate + RefreshRate, + PartySizeDictionary ) VALUES ( @QuestHash, @CreatedAt, @@ -638,7 +640,8 @@ public void InsertPersonalBest(DataLoader dataLoader, long currentPersonalBest, @Monster1PartThresholdDictionary, @Monster2PartThresholdDictionary, @IsHighGradeEdition, - @RefreshRate + @RefreshRate, + @PartySizeDictionary )"; using (var cmd = new SQLiteCommand(sql, conn)) @@ -755,9 +758,16 @@ public void InsertPersonalBest(DataLoader dataLoader, long currentPersonalBest, var isHighGradeEdition = dataLoader.IsHighGradeEdition ? 1 : 0; var refreshRate = s.RefreshRate; + var partySizeDictionary = dataLoader.Model.PartySizeDictionary; + hitsTakenBlockedPerSecondDictionary = TimeService.FilterFramesBySecond(hitsTakenBlockedPerSecondDictionary); + damagePerSecondDictionary = TimeService.FilterFramesBySecond(damagePerSecondDictionary); + hitsPerSecondDictionary = TimeService.FilterFramesBySecond(hitsPerSecondDictionary); + actionsPerMinuteDictionary = TimeService.FilterFramesBySecond(actionsPerMinuteDictionary); + + // NOTE: 2024-10-22 v0.41 filtered certain fields to save space. var questData = string.Format(CultureInfo.InvariantCulture, - "{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}{14}{15}{16}{17}{18}{19}{20}{21}{22}{23}{24}{25}{26}{27}{28}{29}{30}{31}{32}{33}{34}{35}{36}{37}{38}{39}{40}{41}{42}{43}{44}{45}{46}{47}", + "{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}{14}{15}{16}{17}{18}{19}{20}{21}{22}{23}{24}{25}{26}{27}{28}{29}{30}{31}{32}{33}{34}{35}{36}{37}{38}{39}{40}{41}{42}{43}{44}{45}{46}{47}{48}", runID, createdAt, createdBy, questID, timeLeft, finalTimeValue, finalTimeDisplay, objectiveImage, objectiveTypeID, objectiveQuantity, starGrade, rankName, objectiveName, date, attackBuffDictionary, @@ -767,7 +777,7 @@ public void InsertPersonalBest(DataLoader dataLoader, long currentPersonalBest, actualOverlayMode, partySize, monster1HPDictionary, monster2HPDictionary, monster3HPDictionary, monster4HPDictionary, monster1AttackMultiplierDictionary, monster1DefenseRateDictionary, monster1SizeMultiplierDictionary, monster1PoisonThresholdDictionary, monster1SleepThresholdDictionary, monster1ParalysisThresholdDictionary, monster1BlastThresholdDictionary, monster1StunThresholdDictionary, monster1PartThresholdDictionary, - monster2PartThresholdDictionary, isHighGradeEdition, refreshRate); + monster2PartThresholdDictionary, isHighGradeEdition, refreshRate, partySizeDictionary); // Calculate the hash value for the data in the row var questHash = CalculateStringHash(questData); // concatenate the relevant data from the row @@ -821,6 +831,7 @@ public void InsertPersonalBest(DataLoader dataLoader, long currentPersonalBest, cmd.Parameters.AddWithValue("@Monster2PartThresholdDictionary", JsonConvert.SerializeObject(monster2PartThresholdDictionary)); cmd.Parameters.AddWithValue("@IsHighGradeEdition", isHighGradeEdition); cmd.Parameters.AddWithValue("@RefreshRate", refreshRate); + cmd.Parameters.AddWithValue("@PartySizeDictionary", JsonConvert.SerializeObject(partySizeDictionary)); cmd.ExecuteNonQuery(); } @@ -3866,7 +3877,7 @@ public string GetOverlayHash() // Find the path of the first found process with the name "MHFZ_Overlay.exe" var exeName = "MHFZ_Overlay.exe"; - var processes = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(exeName)); + var processes = Process.GetProcessesByName(System.IO.Path.GetFileNameWithoutExtension(exeName)); var exePath = string.Empty; if (processes.Length > 0) { @@ -3882,7 +3893,7 @@ public string GetOverlayHash() } else { - exePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exeName); + exePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exeName); } if (exePath == null) @@ -3919,7 +3930,7 @@ public string StoreOverlayHash() // Find the path of the first found process with the name "MHFZ_Overlay.exe" var exeName = "MHFZ_Overlay.exe"; - var processes = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(exeName)); + var processes = Process.GetProcessesByName(System.IO.Path.GetFileNameWithoutExtension(exeName)); var exePath = string.Empty; if (processes.Length > 0) { @@ -3935,7 +3946,7 @@ public string StoreOverlayHash() } else { - exePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exeName); + exePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exeName); } if (exePath == null) @@ -4419,7 +4430,7 @@ private Dictionary> CreateReferenceSchemaJSON { // Serialize the schema dictionary to a JSON string var json = JsonConvert.SerializeObject(schema, Formatting.Indented); - var referenceSchemaFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHFZ_Overlay\\reference_schema.json"); + var referenceSchemaFilePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHFZ_Overlay\\reference_schema.json"); // Write the JSON string to the reference schema file FileService.WriteToFile(referenceSchemaFilePath, json); @@ -4464,7 +4475,7 @@ private void RecreateReferenceSchemaFile() Restarting overlay after deleting the file.", Messages.InfoTitle, MessageBoxButton.OK, MessageBoxImage.Information); - var referenceSchemaFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHFZ_Overlay\\reference_schema.json"); + var referenceSchemaFilePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHFZ_Overlay\\reference_schema.json"); var databaseFolder = this.GetDatabaseFolderPath(); if (string.IsNullOrEmpty(databaseFolder)) @@ -4474,7 +4485,7 @@ private void RecreateReferenceSchemaFile() } // Create the backups folder if it does not exist - var backupsFolderPath = Path.Combine(databaseFolder, BackupFolderName); + var backupsFolderPath = System.IO.Path.Combine(databaseFolder, BackupFolderName); if (!Directory.Exists(backupsFolderPath)) { Directory.CreateDirectory(backupsFolderPath); @@ -4485,7 +4496,7 @@ private void RecreateReferenceSchemaFile() var backupFileName = $"reference_schema_backup_{timestamp}.json"; // Create the full path for the backup file - var backupFilePath = Path.Combine(backupsFolderPath, backupFileName); + var backupFilePath = System.IO.Path.Combine(backupsFolderPath, backupFileName); Logger.Info(CultureInfo.InvariantCulture, "Making reference schema backup. Reference schema file path: {0}. Backup file path: {1}", referenceSchemaFilePath, backupFilePath); FileService.CopyFileToDestination(referenceSchemaFilePath, backupFilePath); FileService.DeleteFile(referenceSchemaFilePath); @@ -5012,6 +5023,7 @@ ObjectiveTypeID INTEGER NOT NULL CHECK (ObjectiveTypeID >= 0) DEFAULT 0, Monster2PartThresholdDictionary TEXT NOT NULL DEFAULT '{}', IsHighGradeEdition INTEGER NOT NULL CHECK (IsHighGradeEdition IN (0, 1)) DEFAULT 0, RefreshRate INTEGER NOT NULL CHECK (RefreshRate >= 1 AND RefreshRate <= 30) DEFAULT 30, + PartySizeDictionary TEXT NOT NULL DEFAULT '{}', FOREIGN KEY(ObjectiveTypeID) REFERENCES ObjectiveType(ObjectiveTypeID) -- FOREIGN KEY(RankNameID) REFERENCES RankName(RankNameID) )"; @@ -16754,9 +16766,16 @@ private void MigrateToSchemaFromVersion(SQLiteConnection conn, int fromVersion, case 5: // 0.37.1 { this.PerformUpdateToVersion_0_37_1(conn, dataLoader); - this.EnforceForeignKeys(conn); newVersion++; Logger.Info(CultureInfo.InvariantCulture, "Updated schema to version v0.37.1. newVersion {0}", newVersion); + goto case 6; + } + case 6: // 0.41.0 + { + this.PerformUpdateToVersion_0_41_0(conn, dataLoader); + this.EnforceForeignKeys(conn); + newVersion++; + Logger.Info(CultureInfo.InvariantCulture, "Updated schema to version v0.41.0. newVersion {0}", newVersion); break; } // case 2://v0.24.0 @@ -16788,7 +16807,6 @@ private void MigrateToSchemaFromVersion(SQLiteConnection conn, int fromVersion, // 12. If foreign keys constraints were originally enabled, re-enable them now. EnableForeignKeyConstraints(conn); - // [self endTransaction]; // Stop the stopwatch stopwatch.Stop(); @@ -17024,7 +17042,7 @@ private void UpdateDatabaseSchema(SQLiteConnection connection, DataLoader dataLo } this.MigrateToSchemaFromVersion(connection, currentUserVersion, dataLoader); - var referenceSchemaFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHFZ_Overlay\\reference_schema.json"); + var referenceSchemaFilePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHFZ_Overlay\\reference_schema.json"); FileService.DeleteFile(referenceSchemaFilePath); // later on it creates it @@ -17389,6 +17407,8 @@ v0.24 to v0.25 (fixing the refreshrate check is above, but the fix is implemente Monster1PartThresholdDictionary TEXT NOT NULL DEFAULT '{}', Monster2PartThresholdDictionary TEXT NOT NULL DEFAULT '{}', + + v0.41 adds PartySizeDictionary */ using (var updateCommand = new SQLiteCommand(updateQuery, connection)) @@ -17939,6 +17959,7 @@ ObjectiveTypeID INTEGER NOT NULL CHECK (ObjectiveTypeID >= 0) DEFAULT 0, Monster2PartThresholdDictionary TEXT NOT NULL DEFAULT '{}', IsHighGradeEdition INTEGER NOT NULL CHECK (IsHighGradeEdition IN (0, 1)) DEFAULT 0, RefreshRate INTEGER NOT NULL CHECK (RefreshRate >= 1 AND RefreshRate <= 30) DEFAULT 30, + PartySizeDictionary TEXT NOT NULL DEFAULT '{}', FOREIGN KEY(ObjectiveTypeID) REFERENCES ObjectiveType(ObjectiveTypeID) -- FOREIGN KEY(RankNameID) REFERENCES RankName(RankNameID) )"; @@ -17997,7 +18018,8 @@ UPDATE new_Quests Monster1PartThresholdDictionary = (SELECT Monster1PartThresholdDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), Monster2PartThresholdDictionary = (SELECT Monster2PartThresholdDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), IsHighGradeEdition = (SELECT IsHighGradeEdition FROM Quests WHERE Quests.RunID = new_Quests.RunID), - RefreshRate = (SELECT RefreshRate FROM Quests WHERE Quests.RunID = new_Quests.RunID) + RefreshRate = (SELECT RefreshRate FROM Quests WHERE Quests.RunID = new_Quests.RunID), + PartySizeDictionary = (SELECT PartySizeDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID) WHERE EXISTS (SELECT 1 FROM Quests WHERE Quests.RunID = new_Quests.RunID)" ; @@ -18210,6 +18232,275 @@ private void PerformUpdateToVersion_0_37_1(SQLiteConnection connection, DataLoad FillQuestsGamePatch(connection, dataLoader); } + private void PerformUpdateToVersion_0_41_0(SQLiteConnection connection, DataLoader dataLoader) + { + UpdateQuestsDictionariesToFiltered(connection, dataLoader); + UpdateTableQuests_0_41_0(connection); + } + + private void UpdateTableQuests_0_41_0(SQLiteConnection connection) + { + var sql = @"CREATE TABLE IF NOT EXISTS new_Quests + ( + QuestHash TEXT NOT NULL DEFAULT '', + CreatedAt TEXT NOT NULL DEFAULT '', + CreatedBy TEXT NOT NULL DEFAULT '', + RunID INTEGER PRIMARY KEY AUTOINCREMENT, + QuestID INTEGER NOT NULL CHECK (QuestID >= 0) DEFAULT 0, + TimeLeft INTEGER NOT NULL DEFAULT 0, + FinalTimeValue INTEGER NOT NULL DEFAULT 0, + FinalTimeDisplay TEXT NOT NULL DEFAULT '', + ObjectiveImage TEXT NOT NULL DEFAULT '', + ObjectiveTypeID INTEGER NOT NULL CHECK (ObjectiveTypeID >= 0) DEFAULT 0, + ObjectiveQuantity INTEGER NOT NULL DEFAULT 0, + StarGrade INTEGER NOT NULL DEFAULT 0, + RankName TEXT NOT NULL DEFAULT '', + ObjectiveName TEXT NOT NULL DEFAULT '', + Date TEXT NOT NULL DEFAULT '', + YouTubeID TEXT DEFAULT 'dQw4w9WgXcQ', -- default value for YouTubeID is a Rick Roll video + -- DpsData TEXT NOT NULL DEFAULT '', + AttackBuffDictionary TEXT NOT NULL DEFAULT '{}', + HitCountDictionary TEXT NOT NULL DEFAULT '{}', + HitsPerSecondDictionary TEXT NOT NULL DEFAULT '{}', + DamageDealtDictionary TEXT NOT NULL DEFAULT '{}', + DamagePerSecondDictionary TEXT NOT NULL DEFAULT '{}', + AreaChangesDictionary TEXT NOT NULL DEFAULT '{}', + CartsDictionary TEXT NOT NULL DEFAULT '{}', + HitsTakenBlockedDictionary TEXT NOT NULL DEFAULT '{}', + HitsTakenBlockedPerSecondDictionary TEXT NOT NULL DEFAULT '{}', + PlayerHPDictionary TEXT NOT NULL DEFAULT '{}', + PlayerStaminaDictionary TEXT NOT NULL DEFAULT '{}', + KeystrokesDictionary TEXT NOT NULL DEFAULT '{}', + MouseInputDictionary TEXT NOT NULL DEFAULT '{}', + GamepadInputDictionary TEXT NOT NULL DEFAULT '{}', + ActionsPerMinuteDictionary TEXT NOT NULL DEFAULT '{}', + OverlayModeDictionary TEXT NOT NULL DEFAULT '{}', + ActualOverlayMode TEXT NOT NULL DEFAULT 'Standard', + PartySize INTEGER NOT NULL DEFAULT 0, + Monster1HPDictionary TEXT NOT NULL DEFAULT '{}', + Monster2HPDictionary TEXT NOT NULL DEFAULT '{}', + Monster3HPDictionary TEXT NOT NULL DEFAULT '{}', + Monster4HPDictionary TEXT NOT NULL DEFAULT '{}', + Monster1AttackMultiplierDictionary TEXT NOT NULL DEFAULT '{}', + Monster1DefenseRateDictionary TEXT NOT NULL DEFAULT '{}', + Monster1SizeMultiplierDictionary TEXT NOT NULL DEFAULT '{}', + Monster1PoisonThresholdDictionary TEXT NOT NULL DEFAULT '{}', + Monster1SleepThresholdDictionary TEXT NOT NULL DEFAULT '{}', + Monster1ParalysisThresholdDictionary TEXT NOT NULL DEFAULT '{}', + Monster1BlastThresholdDictionary TEXT NOT NULL DEFAULT '{}', + Monster1StunThresholdDictionary TEXT NOT NULL DEFAULT '{}', + Monster1PartThresholdDictionary TEXT NOT NULL DEFAULT '{}', + Monster2PartThresholdDictionary TEXT NOT NULL DEFAULT '{}', + IsHighGradeEdition INTEGER NOT NULL CHECK (IsHighGradeEdition IN (0, 1)) DEFAULT 0, + RefreshRate INTEGER NOT NULL CHECK (RefreshRate >= 1 AND RefreshRate <= 30) DEFAULT 30, + PartySizeDictionary TEXT NOT NULL DEFAULT '{}', + FOREIGN KEY(ObjectiveTypeID) REFERENCES ObjectiveType(ObjectiveTypeID) + -- FOREIGN KEY(RankNameID) REFERENCES RankName(RankNameID) + )"; + using (var cmd = new SQLiteCommand(sql, connection)) + { + cmd.ExecuteNonQuery(); + } + + var updateQuery = @" + UPDATE new_Quests + SET QuestHash = (SELECT QuestHash FROM Quests WHERE Quests.RunID = new_Quests.RunID), + CreatedAt = (SELECT CreatedAt FROM Quests WHERE Quests.RunID = new_Quests.RunID), + CreatedBy = (SELECT CreatedBy FROM Quests WHERE Quests.RunID = new_Quests.RunID), + QuestID = (SELECT QuestID FROM Quests WHERE Quests.RunID = new_Quests.RunID), + TimeLeft = (SELECT TimeLeft FROM Quests WHERE Quests.RunID = new_Quests.RunID), + FinalTimeValue = (SELECT FinalTimeValue FROM Quests WHERE Quests.RunID = new_Quests.RunID), + FinalTimeDisplay = (SELECT FinalTimeDisplay FROM Quests WHERE Quests.RunID = new_Quests.RunID), + ObjectiveImage = (SELECT ObjectiveImage FROM Quests WHERE Quests.RunID = new_Quests.RunID), + ObjectiveTypeID = (SELECT ObjectiveTypeID FROM Quests WHERE Quests.RunID = new_Quests.RunID), + ObjectiveQuantity = (SELECT ObjectiveQuantity FROM Quests WHERE Quests.RunID = new_Quests.RunID), + StarGrade = (SELECT StarGrade FROM Quests WHERE Quests.RunID = new_Quests.RunID), + RankName = (SELECT RankName FROM Quests WHERE Quests.RunID = new_Quests.RunID), + ObjectiveName = (SELECT ObjectiveName FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Date = (SELECT Date FROM Quests WHERE Quests.RunID = new_Quests.RunID), + YouTubeID = (SELECT YouTubeID FROM Quests WHERE Quests.RunID = new_Quests.RunID), + AttackBuffDictionary = (SELECT AttackBuffDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + HitCountDictionary = (SELECT HitCountDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + HitsPerSecondDictionary = (SELECT HitsPerSecondDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + DamageDealtDictionary = (SELECT DamageDealtDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + DamagePerSecondDictionary = (SELECT DamagePerSecondDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + AreaChangesDictionary = (SELECT AreaChangesDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + CartsDictionary = (SELECT CartsDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + HitsTakenBlockedDictionary = (SELECT HitsTakenBlockedDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + HitsTakenBlockedPerSecondDictionary = (SELECT HitsTakenBlockedPerSecondDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + PlayerHPDictionary = (SELECT PlayerHPDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + PlayerStaminaDictionary = (SELECT PlayerStaminaDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + KeystrokesDictionary = (SELECT KeystrokesDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + MouseInputDictionary = (SELECT MouseInputDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + GamepadInputDictionary = (SELECT GamepadInputDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + ActionsPerMinuteDictionary = (SELECT ActionsPerMinuteDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + OverlayModeDictionary = (SELECT OverlayModeDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + ActualOverlayMode = (SELECT ActualOverlayMode FROM Quests WHERE Quests.RunID = new_Quests.RunID), + PartySize = (SELECT PartySize FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster1HPDictionary = (SELECT Monster1HPDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster2HPDictionary = (SELECT Monster2HPDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster3HPDictionary = (SELECT Monster3HPDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster4HPDictionary = (SELECT Monster4HPDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster1AttackMultiplierDictionary = (SELECT Monster1AttackMultiplierDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster1DefenseRateDictionary = (SELECT Monster1DefenseRateDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster1SizeMultiplierDictionary = (SELECT Monster1SizeMultiplierDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster1PoisonThresholdDictionary = (SELECT Monster1PoisonThresholdDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster1SleepThresholdDictionary = (SELECT Monster1SleepThresholdDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster1ParalysisThresholdDictionary = (SELECT Monster1ParalysisThresholdDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster1BlastThresholdDictionary = (SELECT Monster1BlastThresholdDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster1StunThresholdDictionary = (SELECT Monster1StunThresholdDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster1PartThresholdDictionary = (SELECT Monster1PartThresholdDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + Monster2PartThresholdDictionary = (SELECT Monster2PartThresholdDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID), + IsHighGradeEdition = (SELECT IsHighGradeEdition FROM Quests WHERE Quests.RunID = new_Quests.RunID), + RefreshRate = (SELECT RefreshRate FROM Quests WHERE Quests.RunID = new_Quests.RunID), + PartySizeDictionary = (SELECT PartySizeDictionary FROM Quests WHERE Quests.RunID = new_Quests.RunID) + WHERE EXISTS (SELECT 1 FROM Quests WHERE Quests.RunID = new_Quests.RunID)" + ; + + AlterTableData(connection, sql, updateQuery, "Quests"); + } + + private void VacuumDatabaseFile(SQLiteConnection conn) + { + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot vacuum database file. dataSource: {0}", this.dataSource); + return; + } + + try + { + Logger.Info(CultureInfo.InvariantCulture, "Cleaning up database file: {0}", this.dataSource); + + // First, force the completion of any pending operations + using (var pragmaCmd = new SQLiteCommand("PRAGMA schema_version;", conn)) + { + pragmaCmd.ExecuteScalar(); + } + + // Now attempt the VACUUM + using (var vacuumCmd = new SQLiteCommand(conn)) + { + // Create a new connection specifically for VACUUM + using (var vacuumConn = new SQLiteConnection(conn.ConnectionString)) + { + vacuumConn.Open(); + using (var cmd = new SQLiteCommand("VACUUM;", vacuumConn)) + { + cmd.ExecuteNonQuery(); + } + } + } + + Logger.Info(CultureInfo.InvariantCulture, "Vacuum command finished successfully."); + } + catch (SQLiteException ex) + { + Logger.Error(CultureInfo.InvariantCulture, "Failed to vacuum database: {0}", ex.Message); + throw; + } + } + + private void UpdateQuestsDictionariesToFiltered(SQLiteConnection conn, DataLoader dataLoader) + { + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot update quests dictionaries to filtered. dataSource: {0}", this.dataSource); + return; + } + + // Start a transaction + using (var transaction = conn.BeginTransaction()) + { + try + { + using (var cmd0 = new SQLiteCommand(conn)) + { + cmd0.CommandText = @"DROP TRIGGER IF EXISTS prevent_quest_updates"; + cmd0.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + cmd.CommandText = "SELECT * FROM Quests"; + + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + string hitsPerSecondString = reader["HitsPerSecondDictionary"].ToString() ?? "{}"; + string hitsTakenBlockedPerSecondString = reader["HitsTakenBlockedPerSecondDictionary"].ToString() ?? "{}"; + string damagePerSecondString = reader["DamagePerSecondDictionary"].ToString() ?? "{}"; + string actionsPerMinuteString = reader["ActionsPerMinuteDictionary"].ToString() ?? "{}"; + + var runID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); + + if (runID == 0) + { + continue; + } + + var hitsPerSecondDictionary = JsonConvert.DeserializeObject>(hitsPerSecondString); + var hitsTakenBlockedPerSecondDictionary = JsonConvert.DeserializeObject>(hitsTakenBlockedPerSecondString); + var damagePerSecondDictionary = JsonConvert.DeserializeObject>(damagePerSecondString); + var actionsPerMinuteDictionary = JsonConvert.DeserializeObject>(actionsPerMinuteString); + + Dictionary newHitsPerSecondDictionary = new(); + Dictionary newHitsTakenBlockedPerSecondDictionary = new(); + Dictionary newDamagePerSecondDictionary = new(); + Dictionary newActionsPerMinuteDictionary = new(); + + newHitsPerSecondDictionary = TimeService.FilterFramesBySecond(hitsPerSecondDictionary); + newHitsTakenBlockedPerSecondDictionary = TimeService.FilterFramesBySecond(hitsTakenBlockedPerSecondDictionary); + newDamagePerSecondDictionary = TimeService.FilterFramesBySecond(damagePerSecondDictionary); + newActionsPerMinuteDictionary = TimeService.FilterFramesBySecond(actionsPerMinuteDictionary); + + using (var cmd2 = new SQLiteCommand(conn)) + { + cmd2.CommandText = @"UPDATE + Quests + SET + HitsPerSecondDictionary = @HitsPerSecondDictionary, + HitsTakenBlockedPerSecondDictionary = @HitsTakenBlockedPerSecondDictionary, + DamagePerSecondDictionary = @DamagePerSecondDictionary, + ActionsPerMinuteDictionary = @ActionsPerMinuteDictionary WHERE + RunID = @RunID"; + cmd2.Parameters.AddWithValue("@RunID", runID); + cmd2.Parameters.AddWithValue("@HitsPerSecondDictionary", JsonConvert.SerializeObject(newHitsPerSecondDictionary)); + cmd2.Parameters.AddWithValue("@HitsTakenBlockedPerSecondDictionary", JsonConvert.SerializeObject(newHitsTakenBlockedPerSecondDictionary)); + cmd2.Parameters.AddWithValue("@DamagePerSecondDictionary", JsonConvert.SerializeObject(newDamagePerSecondDictionary)); + cmd2.Parameters.AddWithValue("@ActionsPerMinuteDictionary", JsonConvert.SerializeObject(newActionsPerMinuteDictionary)); + + cmd2.ExecuteNonQuery(); + } + } + } + } + + using (var cmd1 = new SQLiteCommand(conn)) + { + cmd1.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quest_updates + AFTER UPDATE ON Quests + FOR EACH ROW + WHEN NEW.YouTubeID = OLD.YouTubeID + BEGIN + SELECT RAISE(ABORT, 'Cannot update quest fields'); + END;"; + cmd1.ExecuteNonQuery(); + } + + // Commit the transaction + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + + Logger.Debug("Updated dictionaries HitsTakenBlockedPerSecondDictionary, ActionsPerMinuteDictionary, DamagePerSecondDictionary and HitsPerSecondDictionary in Quests table"); + } + private void ChangeGameFolderPath(SQLiteConnection conn) { if (string.IsNullOrEmpty(this.dataSource)) diff --git a/MHFZ_Overlay/Services/FileService.cs b/MHFZ_Overlay/Services/FileService.cs index 96bbfec8..7d7e9ed3 100644 --- a/MHFZ_Overlay/Services/FileService.cs +++ b/MHFZ_Overlay/Services/FileService.cs @@ -38,7 +38,41 @@ public sealed class FileService public static TimeSpan SnackbarTimeOut { get; set; } = TimeSpan.FromSeconds(5); - public static bool OpenApplicationFolder(SnackbarPresenter snackbarPresenter, Style snackbarStyle, TimeSpan snackbarTimeout) + public static bool GenerateSpeedrunFiles(SnackbarPresenter snackbarPresenter, Style snackbarStyle, TimeSpan snackbarTimeout) + { + try + { + var snackbar = new Snackbar(snackbarPresenter) + { + Style = snackbarStyle, + Title = Messages.ErrorTitle, + Content = "Could not generate speedrun files", + Icon = new SymbolIcon(SymbolRegular.ErrorCircle24), + Appearance = ControlAppearance.Danger, + Timeout = snackbarTimeout, + }; + snackbar.Show(); + return true; + } + catch (Exception ex) + { + // TODO maybe a snackbar helper class? + Logger.Error(ex); + var snackbar = new Snackbar(snackbarPresenter) + { + Style = snackbarStyle, + Title = Messages.ErrorTitle, + Content = "Could not generate speedrun files", + Icon = new SymbolIcon(SymbolRegular.ErrorCircle24), + Appearance = ControlAppearance.Danger, + Timeout = snackbarTimeout, + }; + snackbar.Show(); + return false; + } + } + + public static bool OpenApplicationFolder(SnackbarPresenter snackbarPresenter, Style snackbarStyle, TimeSpan snackbarTimeout) { try { diff --git a/MHFZ_Overlay/Services/Hotkey/HotkeySettings.cs b/MHFZ_Overlay/Services/Hotkey/HotkeySettings.cs new file mode 100644 index 00000000..8b029615 --- /dev/null +++ b/MHFZ_Overlay/Services/Hotkey/HotkeySettings.cs @@ -0,0 +1,94 @@ +namespace MHFZ_Overlay.Services.Hotkey; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +// HotkeySettings.cs +public class HotkeySettings +{ + public string OpenSettings { get; set; } = "Shift + F1"; + public string RestartProgram { get; set; } = "Shift + F5"; + public string CloseProgram { get; set; } = "Shift + F6"; +} + +public class HotkeyManager : IDisposable +{ + private readonly Dictionary _hotkeyActions = new(); + private HotkeySettings _settings; + private readonly GlobalHotKey _globalHotKey; + + public HotkeyManager() + { + _globalHotKey = new GlobalHotKey(); + LoadSettings(); + } + + + public void LoadSettings() + { + var s = (Settings)System.Windows.Application.Current.TryFindResource("Settings"); + + // Load from user settings or create default + _settings = new HotkeySettings + { + OpenSettings = s.OpenSettingsHotkey ?? "Shift + F1", + RestartProgram = s.RestartProgramHotkey ?? "Shift + F5", + CloseProgram = s.CloseProgramHotkey ?? "Shift + F6" + }; + } + + public void SaveSettings() + { + var s = (Settings)System.Windows.Application.Current.TryFindResource("Settings"); + + s.OpenSettingsHotkey = _settings.OpenSettings; + s.RestartProgramHotkey = _settings.RestartProgram; + s.CloseProgramHotkey = _settings.CloseProgram; + s.Save(); + } + + public void RegisterHotkeys(Action openSettings, Action restart, Action close) + { + // Store the actions + _hotkeyActions["OpenSettings"] = openSettings; + _hotkeyActions["RestartProgram"] = restart; + _hotkeyActions["CloseProgram"] = close; + + // Register hotkeys using existing GlobalHotKey class + GlobalHotKey.RegisterHotKey(_settings.OpenSettings, openSettings); + GlobalHotKey.RegisterHotKey(_settings.RestartProgram, restart); + GlobalHotKey.RegisterHotKey(_settings.CloseProgram, close); + } + + public void UpdateHotkey(string action, string newHotkey) + { + // Update the settings + switch (action) + { + case "OpenSettings": + _settings.OpenSettings = newHotkey; + break; + case "RestartProgram": + _settings.RestartProgram = newHotkey; + break; + case "CloseProgram": + _settings.CloseProgram = newHotkey; + break; + } + + SaveSettings(); + + // Re-register the hotkey if we have an action for it + if (_hotkeyActions.TryGetValue(action, out var hotkeyAction)) + { + GlobalHotKey.RegisterHotKey(newHotkey, hotkeyAction); + } + } + + public void Dispose() + { + _globalHotKey?.Dispose(); + } +} diff --git a/MHFZ_Overlay/Services/TimeService.cs b/MHFZ_Overlay/Services/TimeService.cs index f1bb1cb5..a329b1a2 100644 --- a/MHFZ_Overlay/Services/TimeService.cs +++ b/MHFZ_Overlay/Services/TimeService.cs @@ -34,6 +34,27 @@ private static TimeSpan GetTimeSpanFromFrames(decimal frames) return TimeSpan.FromSeconds((double)frames / (double)Numbers.FramesPerSecond); } + public static Dictionary FilterFramesBySecond(Dictionary? originalData, int framesPerSecond = (int)Numbers.FramesPerSecond) + { + if (originalData == null || !originalData.Any()) + return new Dictionary(); + + var firstFrame = originalData.Keys.Max(); + + return originalData + .OrderByDescending(x => x.Key) + .Aggregate( + new { LastFrame = firstFrame, Result = new Dictionary { { firstFrame, originalData[firstFrame] } } }, + (acc, curr) => curr.Key == firstFrame ? acc : + (acc.LastFrame - curr.Key >= framesPerSecond ? + new + { + LastFrame = curr.Key, + Result = new Dictionary(acc.Result) { [curr.Key] = curr.Value } + } : acc), + acc => acc.Result); + } + public static string GetTimeLeftPercent(decimal timeDefInt, decimal timeInt, bool isDure) { if (timeDefInt < timeInt || timeDefInt <= 0) diff --git a/MHFZ_Overlay/Settings.Designer.cs b/MHFZ_Overlay/Settings.Designer.cs index 2657a844..d2dbaa39 100644 --- a/MHFZ_Overlay/Settings.Designer.cs +++ b/MHFZ_Overlay/Settings.Designer.cs @@ -4162,5 +4162,41 @@ public bool EnableAchievementsTracking { this["EnableAchievementsTracking"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Shift + F1")] + public string OpenSettingsHotkey { + get { + return ((string)(this["OpenSettingsHotkey"])); + } + set { + this["OpenSettingsHotkey"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Shift + F5")] + public string RestartProgramHotkey { + get { + return ((string)(this["RestartProgramHotkey"])); + } + set { + this["RestartProgramHotkey"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Shift + F6")] + public string CloseProgramHotkey { + get { + return ((string)(this["CloseProgramHotkey"])); + } + set { + this["CloseProgramHotkey"] = value; + } + } } } diff --git a/MHFZ_Overlay/Settings.settings b/MHFZ_Overlay/Settings.settings index 17bf6b45..3d979bad 100644 --- a/MHFZ_Overlay/Settings.settings +++ b/MHFZ_Overlay/Settings.settings @@ -1037,5 +1037,14 @@ True + + Shift + F1 + + + Shift + F5 + + + Shift + F6 + \ No newline at end of file diff --git a/MHFZ_Overlay/ViewModels/Windows/AddressModel.cs b/MHFZ_Overlay/ViewModels/Windows/AddressModel.cs index 3277abcc..15290f9d 100644 --- a/MHFZ_Overlay/ViewModels/Windows/AddressModel.cs +++ b/MHFZ_Overlay/ViewModels/Windows/AddressModel.cs @@ -4314,7 +4314,7 @@ public string Monster1HP } /// - /// Gets the name of the rank. + /// Gets the name of the rank.TODO: In database the spaces are there. /// /// The identifier. /// @@ -12204,6 +12204,10 @@ public double CalculateHitsPerSecond() public int PreviousDualSwordsSharpens { get; set; } + public Dictionary PartySizeDictionary { get; set; } = new(); + + public int PreviousPartySize { get; set; } + // Get all countries public static IEnumerable Countries => RestCountriesService.GetAllCountries(); @@ -13289,6 +13293,19 @@ public void InsertQuestInfoIntoDictionaries() LoggerInstance.Warn(ex, "Could not insert into DualSwordsSharpensDictionary"); } } + + if (this.PreviousPartySize != this.PartySize() && !this.PartySizeDictionary.ContainsKey(this.TimeInt())) + { + try + { + this.PreviousPartySize = this.PartySize(); + this.PartySizeDictionary.Add(this.TimeInt(), this.PartySize()); + } + catch (Exception ex) + { + LoggerInstance.Warn(ex, "Could not insert into PartySizeDictionary"); + } + } } public void ResetQuestInfoVariables() @@ -13426,6 +13443,7 @@ public void ResetQuestInfoVariables() this.PreviousMonster2Part9Threshold = 0; this.PreviousMonster2Part10Threshold = 0; this.PreviousDualSwordsSharpens = 0; + this.PreviousPartySize = 0; } public void ClearQuestInfoDictionaries() @@ -13466,6 +13484,7 @@ public void ClearQuestInfoDictionaries() this.Monster2PartThresholdDictionary.Clear(); this.DualSwordsSharpensDictionary.Clear(); + this.PartySizeDictionary.Clear(); } public void ClearGraphCollections() diff --git a/MHFZ_Overlay/Views/Windows/ConfigWindow.xaml b/MHFZ_Overlay/Views/Windows/ConfigWindow.xaml index b818139d..5f653f8e 100644 --- a/MHFZ_Overlay/Views/Windows/ConfigWindow.xaml +++ b/MHFZ_Overlay/Views/Windows/ConfigWindow.xaml @@ -1543,38 +1543,42 @@ + - + - + + + + - - + + - - + + - + - + - + Current Final - +