diff --git a/CHANGELOG.md b/CHANGELOG.md index f77f5d8e..ac6e9ca6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ +## [0.34.0](https://github.com/DorielRivalet/MHFZ_Overlay/compare/v0.33.0...v0.34.0) (2024-02-02) + + +### Features + +* add buff timers ([b76b8ce](https://github.com/DorielRivalet/MHFZ_Overlay/commit/b76b8cec3a7219a71ef42c44e76f1a0244d1ece0)), closes [DorielRivalet/mhfz-overlay#260](https://github.com/DorielRivalet/mhfz-overlay/issues/260) [DorielRivalet/mhfz-overlay#268](https://github.com/DorielRivalet/mhfz-overlay/issues/268) [DorielRivalet/mhfz-overlay#267](https://github.com/DorielRivalet/mhfz-overlay/issues/267) +* add course address ([48e8856](https://github.com/DorielRivalet/MHFZ_Overlay/commit/48e88561e515bcc542f04d1658200d95eea3d1e0)), closes [DorielRivalet/mhfz-overlay#251](https://github.com/DorielRivalet/mhfz-overlay/issues/251) +* add course rights enum ([1217abe](https://github.com/DorielRivalet/MHFZ_Overlay/commit/1217abe8a67984c3e34e4a25a771ff17bc9ec6fe)) +* add missing run buffs ([d7f2527](https://github.com/DorielRivalet/MHFZ_Overlay/commit/d7f25276cc8804ff97eb484ecbb42546956c1d58)), closes [DorielRivalet/mhfz-overlay#259](https://github.com/DorielRivalet/mhfz-overlay/issues/259) [DorielRivalet/mhfz-overlay#265](https://github.com/DorielRivalet/mhfz-overlay/issues/265) [DorielRivalet/mhfz-overlay#250](https://github.com/DorielRivalet/mhfz-overlay/issues/250) [DorielRivalet/mhfz-overlay#264](https://github.com/DorielRivalet/mhfz-overlay/issues/264) +* add more achievements ([8345092](https://github.com/DorielRivalet/MHFZ_Overlay/commit/83450920034dbee12aabf3d0a8d9325d1ca688aa)) +* add player position addresses ([e8077ac](https://github.com/DorielRivalet/MHFZ_Overlay/commit/e8077ac3aa6da7a2ded3f5601f0908d2488384b6)), closes [DorielRivalet/mhfz-overlay#252](https://github.com/DorielRivalet/mhfz-overlay/issues/252) +* improve quest logs youtube section ([4745ef2](https://github.com/DorielRivalet/MHFZ_Overlay/commit/4745ef2e42ef5f6ca10683188e825dbd4cfdc6ac)), closes [DorielRivalet/mhfz-overlay#256](https://github.com/DorielRivalet/mhfz-overlay/issues/256) +* **sqlite:** add active feature table ([3410975](https://github.com/DorielRivalet/MHFZ_Overlay/commit/3410975440c0eeb1358dae0c2c10b7b508c275e9)), closes [DorielRivalet/mhfz-overlay#249](https://github.com/DorielRivalet/mhfz-overlay/issues/249) +* **sqlite:** add missing triggers ([3e42389](https://github.com/DorielRivalet/MHFZ_Overlay/commit/3e42389de8c9c791dc5616d6c09b257b17350918)) +* **sqlite:** link run id with overlay hash ([852cbf7](https://github.com/DorielRivalet/MHFZ_Overlay/commit/852cbf7a1dfc0c2ae9238e49a756a30c5b2b52a7)), closes [DorielRivalet/mhfz-overlay#248](https://github.com/DorielRivalet/mhfz-overlay/issues/248) +* **struct:** add diva prayer gem information ([498aa50](https://github.com/DorielRivalet/MHFZ_Overlay/commit/498aa50754169051319f660ac228e79f38c29643)) +* **struct:** add enums ([6934803](https://github.com/DorielRivalet/MHFZ_Overlay/commit/6934803d147ea38b9b3a61f0c4e3e1055980fd6a)) +* update gear section ([2559133](https://github.com/DorielRivalet/MHFZ_Overlay/commit/2559133d33a250bf9dffdb3c7d6af4798bf41891)) + + +### Bug Fixes + +* **address:** fix guild food address ([b58bde1](https://github.com/DorielRivalet/MHFZ_Overlay/commit/b58bde117ccf8454a14d0c50a4b16d7a62a612b7)), closes [DorielRivalet/mhfz-overlay#270](https://github.com/DorielRivalet/mhfz-overlay/issues/270) +* filter by solo quests in quest pace section ([08ac525](https://github.com/DorielRivalet/MHFZ_Overlay/commit/08ac52536a7aa91ec92b7f2a0b8c054916b63a85)), closes [DorielRivalet/mhfz-overlay#247](https://github.com/DorielRivalet/mhfz-overlay/issues/247) +* guild food timer visibility ([8c2d8aa](https://github.com/DorielRivalet/MHFZ_Overlay/commit/8c2d8aa52adcddb39e959bb4d1a316827d1d29e0)) +* personal bests by attempts graph ([ea6a0fd](https://github.com/DorielRivalet/MHFZ_Overlay/commit/ea6a0fdb7052f451a0b091bd21bddccb08d332bd)), closes [DorielRivalet/mhfz-overlay#245](https://github.com/DorielRivalet/mhfz-overlay/issues/245) +* quest attempts ([884e4f6](https://github.com/DorielRivalet/MHFZ_Overlay/commit/884e4f69936a8b0b3169248954ca158787f48027)), closes [DorielRivalet/mhfz-overlay#253](https://github.com/DorielRivalet/mhfz-overlay/issues/253) +* quest pace calculation ([f0a5fdf](https://github.com/DorielRivalet/MHFZ_Overlay/commit/f0a5fdf0552639d49f98ff376ff0101e6dc60ecb)) +* rights flag check ([d701a3a](https://github.com/DorielRivalet/MHFZ_Overlay/commit/d701a3a2abd7e29515f38c6962bffa30fd3affee)) +* **sqlite:** quest attempts and personal best attempts in multiplayer ([8c3bff1](https://github.com/DorielRivalet/MHFZ_Overlay/commit/8c3bff1a0c53b94cde499fe8a49b11f7286f3fa4)), closes [DorielRivalet/mhfz-overlay#269](https://github.com/DorielRivalet/mhfz-overlay/issues/269) +* weapon slots decorations with sigils ([ab1101e](https://github.com/DorielRivalet/MHFZ_Overlay/commit/ab1101e9dd770af94a7980ca3908ad16c3490072)), closes [DorielRivalet/mhfz-overlay#254](https://github.com/DorielRivalet/mhfz-overlay/issues/254) + + +### For Developers + +* add v0.34.0 information ([8433a0b](https://github.com/DorielRivalet/MHFZ_Overlay/commit/8433a0b761768f77cfc0b27cc1671a23b897e7a8)) +* bump version ([efbdc6d](https://github.com/DorielRivalet/MHFZ_Overlay/commit/efbdc6dd1ebb7c177b02c39a45fdb89e5fe87899)) + ## [0.33.0](https://github.com/DorielRivalet/MHFZ_Overlay/compare/v0.32.0...v0.33.0) (2024-01-16) diff --git a/FAQ.md b/FAQ.md index 9f5a47f8..0b80429a 100644 --- a/FAQ.md +++ b/FAQ.md @@ -54,8 +54,8 @@ Use the preset option found in the General tab. Keep reading if you want to know - Speedrun Mode Categories: Enable the required settings in the Quest Logs section, disable **everything** else, including Quest Pace Color (Monster Icon, Quest Timer + Percentage, KBM Layout, Personal Best and Discord Rich Presence optional) -- Time Attack: Do not use diva skills. -- Freestyle: Use diva skills with/without Secret Technique Style Rank Skill. +- Time Attack: Do not use halk, guild poogie, active feature, guild food, diva skill and diva prayer gem. +- Freestyle: Use any of the above or everything else with/without Secret Technique Style Rank Skill. **Important**: It is recommended to make a backup of the `MHFZ_Overlay.sqlite` file periodically. The file is located inside the database folder, which is inside your game folder. Don't lose your speedrun records! @@ -65,7 +65,7 @@ Use the preset option found in the General tab. Keep reading if you want to know ![Discord Rich Presence](./demo/discord11.png) -~~Congrats, now you won't be accused of cheating~~ +The current speedrun categories are pending an overhaul. Zen mode is not counted as a speedrun mode. ## How to Record Videos with the Overlay? @@ -241,6 +241,8 @@ If DS4Windows does not work for you, you can search for alternatives, but it is |Personal Best Attempts|✔️|❌|❌| |Completions Counter|✔️|❌|❌| |Run Pace|✔️ Color|❌|❌| +|Quest Pace Graph|✔️|❌|❌| +|Player Coordinates|✔️|❌|❌| |Settings Save on Update|✔️|❌|❌| |Save Stats to File|✔️|❌|❌| |Copy Stats to Clipboard|✔️|❌|❌| @@ -249,9 +251,9 @@ If DS4Windows does not work for you, you can search for alternatives, but it is |Database Backups|✔️ Local|❌|❌| |Past Quests Info|✔️|❌|❌| |Achievements|✔️|❌|❌| -|Show Quest Change|✔️|❌|❌ +|Show Quest Change|✔️|❌|❌| |Show Area Change|✔️|❌|❌| -|KBM Layout|✔️|❌|❌ +|KBM Layout|✔️|❌|❌| |Gamepad Layout|✔️|❌|❌| |Input Logs|✔️|❌|❌| |Quest ID|✔️|❌|❌| @@ -270,7 +272,7 @@ If DS4Windows does not work for you, you can search for alternatives, but it is |Logging|✔️|❌|❌| |Logging Options|✔️|❌|❌| |Color Options|✔️|✔️|✔️| -|Buff Icons |❌|❌|❌ +|Buff Icons |✔️|❌|❌| |Debuff Icons |❌|❌|❌| |Hub Activities|❌|❌|❌| |Settings Search|❌|❌|❌| @@ -301,7 +303,7 @@ If DS4Windows does not work for you, you can search for alternatives, but it is As an added bonus: -| | mhf-z overlay v0.25.0 (DorielRivalet) | HunterPie v2.8.0 (HunterPie) | +| | mhf-z overlay v0.34.0 (DorielRivalet) | HunterPie v2.8.0 (HunterPie) | |:----------|:---------:|:---------:| |Discord Rich Presence|✔️|✔️| |Rich Presence Quest Name|✔️|❌| @@ -333,6 +335,8 @@ As an added bonus: |Personal Best Attempts|✔️|❌| |Completions Counter|✔️|❌| |Run Pace|✔️ Color|❌| +|Quest Pace Graph|✔️|❌| +|Player Coordinates|✔️|❌| |Settings Save on Update|✔️|✔️| |Save Stats to File|✔️|❌| |Copy Stats to Clipboard|✔️|❌| @@ -362,7 +366,7 @@ As an added bonus: |Logging|✔️ File|✔️ Console| |Logging Options|✔️|❌| |Color Options|✔️|❌| -|Buff Icons |❌|✔️| +|Buff Icons |✔️|✔️| |Debuff Icons |❌|✔️| |Hub Activities|❌|✔️| |Settings Search|❌|✔️ by Section| diff --git a/MHFZ_Overlay/App.config b/MHFZ_Overlay/App.config index c6a8b0b6..442d32ca 100644 --- a/MHFZ_Overlay/App.config +++ b/MHFZ_Overlay/App.config @@ -993,6 +993,36 @@ False + + False + + + 5 + + + 140 + + + Automatic + + + True + + + 5 + + + 100 + + + True + + + 5 + + + 60 + diff --git a/MHFZ_Overlay/Assets/Icons/png/blacksmith.png b/MHFZ_Overlay/Assets/Icons/png/blacksmith.png new file mode 100644 index 00000000..4d2ed92d Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/blacksmith.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/diva_fountain.png b/MHFZ_Overlay/Assets/Icons/png/diva_fountain.png new file mode 100644 index 00000000..d7fd9ec1 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/diva_fountain.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/guild_hall.png b/MHFZ_Overlay/Assets/Icons/png/guild_hall.png new file mode 100644 index 00000000..d0fca085 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/guild_hall.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/my_gallery.png b/MHFZ_Overlay/Assets/Icons/png/my_gallery.png new file mode 100644 index 00000000..2076b7d6 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/my_gallery.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/my_garden.png b/MHFZ_Overlay/Assets/Icons/png/my_garden.png new file mode 100644 index 00000000..f377bce3 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/my_garden.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/my_house.png b/MHFZ_Overlay/Assets/Icons/png/my_house.png new file mode 100644 index 00000000..0e5eaec0 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/my_house.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/my_missions.png b/MHFZ_Overlay/Assets/Icons/png/my_missions.png new file mode 100644 index 00000000..6a1ea402 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/my_missions.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/my_support.png b/MHFZ_Overlay/Assets/Icons/png/my_support.png new file mode 100644 index 00000000..e686d5b0 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/my_support.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/my_tore.png b/MHFZ_Overlay/Assets/Icons/png/my_tore.png new file mode 100644 index 00000000..6b688a39 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/my_tore.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/pallone_caravan.png b/MHFZ_Overlay/Assets/Icons/png/pallone_caravan.png new file mode 100644 index 00000000..c5c19e9a Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/pallone_caravan.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/pvp.png b/MHFZ_Overlay/Assets/Icons/png/pvp.png new file mode 100644 index 00000000..0e0cb8cd Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/pvp.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/rasta_bar.png b/MHFZ_Overlay/Assets/Icons/png/rasta_bar.png new file mode 100644 index 00000000..7c88f742 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/rasta_bar.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/tenrou.png b/MHFZ_Overlay/Assets/Icons/png/tenrou.png new file mode 100644 index 00000000..ad1a24b3 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/tenrou.png differ diff --git a/MHFZ_Overlay/Assets/Icons/png/tent.png b/MHFZ_Overlay/Assets/Icons/png/tent.png new file mode 100644 index 00000000..124d92d4 Binary files /dev/null and b/MHFZ_Overlay/Assets/Icons/png/tent.png differ diff --git a/MHFZ_Overlay/MHFZ_Overlay.csproj b/MHFZ_Overlay/MHFZ_Overlay.csproj index d3fbd0c9..2cdd2777 100644 --- a/MHFZ_Overlay/MHFZ_Overlay.csproj +++ b/MHFZ_Overlay/MHFZ_Overlay.csproj @@ -20,7 +20,7 @@ mhfz-overlay Doriel Rivalet Doriel Rivalet - 0.33.0 + 0.34.0 https://github.com/DorielRivalet/mhfz-overlay https://github.com/DorielRivalet/mhfz-overlay.git git @@ -168,6 +168,7 @@ + @@ -185,6 +186,7 @@ + @@ -257,6 +259,7 @@ + @@ -691,7 +694,14 @@ + + + + + + + @@ -699,11 +709,13 @@ + + @@ -727,6 +739,8 @@ + + @@ -1178,6 +1192,7 @@ + @@ -1195,6 +1210,7 @@ + @@ -1267,6 +1283,7 @@ + @@ -1701,7 +1718,14 @@ + + + + + + + @@ -1709,11 +1733,13 @@ + + @@ -1737,6 +1763,8 @@ + + @@ -1831,7 +1859,7 @@ - + diff --git a/MHFZ_Overlay/Models/Addresses/AddressModelHGE.cs b/MHFZ_Overlay/Models/Addresses/AddressModelHGE.cs index 05e32818..61f2b532 100644 --- a/MHFZ_Overlay/Models/Addresses/AddressModelHGE.cs +++ b/MHFZ_Overlay/Models/Addresses/AddressModelHGE.cs @@ -456,7 +456,10 @@ public AddressModelHGE(Mem m) public override int GZenny() => this.M.ReadInt("mhfo-hd.dll+ED3ACB4"); /// - public override int GuildFoodSkill() => this.M.Read2Byte("mhfo-hd.dll+E7FED00"); + /// public override int GuildFoodSkill() => this.M.Read2Byte("mhfo-hd.dll+E7FED00"); + /// + /// + public override int GuildFoodSkill() => this.M.Read2Byte("mhfo-hd.dll+E6CCD9E"); /// public override int GalleryEvaluationScore() => this.M.ReadInt("mhfo-hd.dll+ED3D9F0"); @@ -527,6 +530,60 @@ public AddressModelHGE(Mem m) /// public override int HalkFullness() => this.M.ReadByte("mhfo-hd.dll+ED3C123"); + /// + public override int HalkLevel() => this.M.ReadByte("mhfo-hd.dll+ED3C124"); + + /// + public override int HalkIntimacy() => this.M.ReadByte("mhfo-hd.dll+ED3C125"); + + /// + public override int HalkHealth() => this.M.ReadByte("mhfo-hd.dll+ED3C126"); + + /// + public override int HalkAttack() => this.M.ReadByte("mhfo-hd.dll+ED3C127"); + + /// + public override int HalkDefense() => this.M.ReadByte("mhfo-hd.dll+ED3C128"); + + /// + public override int HalkIntellect() => this.M.ReadByte("mhfo-hd.dll+ED3C129"); + + /// + public override int HalkSkill1() => this.M.ReadByte("mhfo-hd.dll+ED3C12A"); + + /// + public override int HalkSkill2() => this.M.ReadByte("mhfo-hd.dll+ED3C12B"); + + /// + public override int HalkSkill3() => this.M.ReadByte("mhfo-hd.dll+ED3C12C"); + + /// + public override int HalkElementNone() => this.M.ReadByte("mhfo-hd.dll+ED3C12E"); + + /// + public override int HalkFire() => this.M.ReadByte("mhfo-hd.dll+ED3C12F"); + + /// + public override int HalkThunder() => this.M.ReadByte("mhfo-hd.dll+ED3C130"); + + /// + public override int HalkWater() => this.M.ReadByte("mhfo-hd.dll+ED3C131"); + + /// + public override int HalkIce() => this.M.ReadByte("mhfo-hd.dll+ED3C132"); + + /// + public override int HalkDragon() => this.M.ReadByte("mhfo-hd.dll+ED3C133"); + + /// + public override int HalkSleep() => this.M.ReadByte("mhfo-hd.dll+ED3C134"); + + /// + public override int HalkParalysis() => this.M.ReadByte("mhfo-hd.dll+ED3C135"); + + /// + public override int HalkPoison() => this.M.ReadByte("mhfo-hd.dll+ED3C136"); + /// public override int RankBand() => this.M.ReadByte("mhfo-hd.dll+2AFA788"); @@ -1748,4 +1805,86 @@ public AddressModelHGE(Mem m) /// public override int QuestToggleMonsterMode() => this.M.ReadByte("mhfo-hd.dll+E73D7B6"); + + /// + public override int Rights() => this.M.Read2Byte("mhfo-hd.dll+EABD3C4"); + + /// + public override decimal PlayerPositionX() => (decimal)this.M.ReadFloat("mhfo-hd.dll+E8E6640", string.Empty, false); + + /// + public override decimal PlayerPositionY() => (decimal)this.M.ReadFloat("mhfo-hd.dll+E8E6644", string.Empty, false); + + /// + public override decimal PlayerPositionZ() => (decimal)this.M.ReadFloat("mhfo-hd.dll+E8E6648", string.Empty, false); + + /// + public override decimal PlayerPositionInQuestX() => (decimal)this.M.ReadFloat("mhfo-hd.dll+21B3530", string.Empty, false); + + /// + public override decimal PlayerPositionInQuestY() => (decimal)this.M.ReadFloat("mhfo-hd.dll+21B3534", string.Empty, false); + + /// + public override decimal PlayerPositionInQuestZ() => (decimal)this.M.ReadFloat("mhfo-hd.dll+21B3538", string.Empty, false); + + /// + public override int ActiveFeature1() => this.M.Read2Byte("mhfo-hd.dll+1C2AB0C"); + + /// + public override int ActiveFeature2() => this.M.Read2Byte("mhfo-hd.dll+1C2AB14"); + + /// + public override int ActiveFeature3() => this.M.Read2Byte("mhfo-hd.dll+E41A2A8"); + + /// + public override int ServerHeartbeat() => this.M.ReadInt("mhfo-hd.dll+EABD4F0"); + + /// + public override int GuildFoodStart() => this.M.ReadInt("mhfo-hd.dll+E7FED08"); + + /// + public override int DivaSongStart() => this.M.ReadInt("mhfo-hd.dll+ED3DB50"); + + /// + //public override int DivaPrayerGemStart() => this.M.ReadInt("mhfo-hd.dll+E820DF8"); + + /// + public override int GuildPoogie1Skill() => this.M.ReadByte("mhfo-hd.dll+E76BBDB"); + + /// + public override int GuildPoogie2Skill() => this.M.ReadByte("mhfo-hd.dll+E76BBEB"); + + /// + public override int GuildPoogie3Skill() => this.M.ReadByte("mhfo-hd.dll+E76BBFB"); + + /// + public override int DivaPrayerGemRedSkill() => this.M.Read2Byte("mhfo-hd.dll+E6CCF14"); + + /// + public override int DivaPrayerGemRedLevel() => this.M.Read2Byte("mhfo-hd.dll+E6CCF16"); + + /// + public override int DivaPrayerGemYellowSkill() => this.M.Read2Byte("mhfo-hd.dll+E6CCF18"); + + /// + public override int DivaPrayerGemYellowLevel() => this.M.Read2Byte("mhfo-hd.dll+E6CCF1A"); + + /// + public override int DivaPrayerGemGreenSkill() => this.M.Read2Byte("mhfo-hd.dll+E6CCF1C"); + + /// + public override int DivaPrayerGemGreenLevel() => this.M.Read2Byte("mhfo-hd.dll+E6CCF1E"); + + /// + public override int DivaPrayerGemBlueSkill() => this.M.Read2Byte("mhfo-hd.dll+E6CCF20"); + + /// + public override int DivaPrayerGemBlueLevel() => this.M.Read2Byte("mhfo-hd.dll+E6CCF22"); + + /// + public override bool HalkOn() => this.M.ReadByte("mhfo-hd.dll+E7FE233") > 0 ? true : false; + + /// + public override bool HalkPotEffectOn() => this.M.ReadByte("mhfo-hd.dll+DC6C524") > 0 ? true : false; + } diff --git a/MHFZ_Overlay/Models/Addresses/AddressModelNotHGE.cs b/MHFZ_Overlay/Models/Addresses/AddressModelNotHGE.cs index e158b472..9b0b6708 100644 --- a/MHFZ_Overlay/Models/Addresses/AddressModelNotHGE.cs +++ b/MHFZ_Overlay/Models/Addresses/AddressModelNotHGE.cs @@ -370,8 +370,11 @@ public AddressModelNotHGE(Mem m) /// public override int GZenny() => this.M.ReadInt("mhfo.dll+6100514"); + ///// + //public override int GuildFoodSkill() => this.M.Read2Byte("mhfo.dll+5BC70D8"); + /// - public override int GuildFoodSkill() => this.M.Read2Byte("mhfo.dll+5BC70D8"); + public override int GuildFoodSkill() => this.M.Read2Byte("mhfo.dll+5A951DE"); /// public override int GalleryEvaluationScore() => this.M.ReadInt("mhfo.dll+6103250"); @@ -442,6 +445,60 @@ public AddressModelNotHGE(Mem m) /// public override int HalkFullness() => this.M.ReadByte("mhfo.dll+6101983"); + /// + public override int HalkLevel() => this.M.ReadByte("mhfo.dll+6101984"); + + /// + public override int HalkIntimacy() => this.M.ReadByte("mhfo.dll+6101985"); + + /// + public override int HalkHealth() => this.M.ReadByte("mhfo.dll+6101986"); + + /// + public override int HalkAttack() => this.M.ReadByte("mhfo.dll+6101987"); + + /// + public override int HalkDefense() => this.M.ReadByte("mhfo.dll+6101988"); + + /// + public override int HalkIntellect() => this.M.ReadByte("mhfo.dll+6101989"); + + /// + public override int HalkSkill1() => this.M.ReadByte("mhfo.dll+610198A"); + + /// + public override int HalkSkill2() => this.M.ReadByte("mhfo.dll+610198B"); + + /// + public override int HalkSkill3() => this.M.ReadByte("mhfo.dll+610198C"); + + /// + public override int HalkElementNone() => this.M.ReadByte("mhfo.dll+610198E"); + + /// + public override int HalkFire() => this.M.ReadByte("mhfo.dll+610198F"); + + /// + public override int HalkThunder() => this.M.ReadByte("mhfo.dll+6101990"); + + /// + public override int HalkWater() => this.M.ReadByte("mhfo.dll+6101991"); + + /// + public override int HalkIce() => this.M.ReadByte("mhfo.dll+6101992"); + + /// + public override int HalkDragon() => this.M.ReadByte("mhfo.dll+6101993"); + + /// + public override int HalkSleep() => this.M.ReadByte("mhfo.dll+6101994"); + + /// + public override int HalkParalysis() => this.M.ReadByte("mhfo.dll+6101995"); + + /// + public override int HalkPoison() => this.M.ReadByte("mhfo.dll+6101996"); + /// public override int RankBand() => this.M.ReadByte("mhfo.dll+28C2BD8"); @@ -1665,5 +1722,87 @@ public AddressModelNotHGE(Mem m) /// public override int PartnyaBagItem10Qty() => this.M.Read2Byte("mhfo.dll+57457AE"); + /// public override int QuestToggleMonsterMode() => this.M.ReadByte("mhfo.dll+5B05B8E"); + + /// + public override int Rights() => this.M.Read2Byte("mhfo.dll+5D98294"); + + /// + public override decimal PlayerPositionX() => (decimal)this.M.ReadFloat("mhfo.dll+5CACB50", string.Empty, false); + + /// + public override decimal PlayerPositionY() => (decimal)this.M.ReadFloat("mhfo.dll+5CACB54", string.Empty, false); + + /// + public override decimal PlayerPositionZ() => (decimal)this.M.ReadFloat("mhfo.dll+5CACB58", string.Empty, false); + + /// + public override decimal PlayerPositionInQuestX() => (decimal)this.M.ReadFloat("mhfo.dll+20BB540", string.Empty, false); + + /// + public override decimal PlayerPositionInQuestY() => (decimal)this.M.ReadFloat("mhfo.dll+20BB544", string.Empty, false); + + /// + public override decimal PlayerPositionInQuestZ() => (decimal)this.M.ReadFloat("mhfo.dll+20BB548", string.Empty, false); + + /// + public override int ActiveFeature1() => this.M.Read2Byte("mhfo.dll+1BD2F50"); + + /// + public override int ActiveFeature2() => this.M.Read2Byte("mhfo.dll+1BD2F58"); + + /// + public override int ActiveFeature3() => this.M.Read2Byte("mhfo.dll+57E26E8"); + + /// + public override int ServerHeartbeat() => this.M.ReadInt("mhfo.dll+5E83A00"); + + /// + public override int GuildFoodStart() => this.M.ReadInt("mhfo.dll+5BC70E0"); + + /// + public override int DivaSongStart() => this.M.ReadInt("mhfo.dll+61033B0"); + + /// + //public override int DivaPrayerGemStart() => this.M.ReadInt("mhfo.dll+5BE91C8"); + + /// + public override int GuildPoogie1Skill() => this.M.ReadByte("mhfo.dll+5B33FB3"); + + /// + public override int GuildPoogie2Skill() => this.M.ReadByte("mhfo.dll+5B33FC3"); + + /// + public override int GuildPoogie3Skill() => this.M.ReadByte("mhfo.dll+5B33FD3"); + + /// + public override int DivaPrayerGemRedSkill() => this.M.Read2Byte("mhfo.dll+5A95354"); + + /// + public override int DivaPrayerGemRedLevel() => this.M.Read2Byte("mhfo.dll+5A95356"); + + /// + public override int DivaPrayerGemYellowSkill() => this.M.Read2Byte("mhfo.dll+5A95358"); + + /// + public override int DivaPrayerGemYellowLevel() => this.M.Read2Byte("mhfo.dll+5A9535A"); + + /// + public override int DivaPrayerGemGreenSkill() => this.M.Read2Byte("mhfo.dll+5A9535C"); + + /// + public override int DivaPrayerGemGreenLevel() => this.M.Read2Byte("mhfo.dll+5A9535E"); + + /// + public override int DivaPrayerGemBlueSkill() => this.M.Read2Byte("mhfo.dll+5A95360"); + + /// + public override int DivaPrayerGemBlueLevel() => this.M.Read2Byte("mhfo.dll+5A95362"); + + /// + public override bool HalkOn() => this.M.ReadByte("mhfo.dll+5BC6603") > 0 ? true : false; + + /// + public override bool HalkPotEffectOn() => this.M.ReadByte("mhfo.dll+5034964") > 0 ? true : false; } diff --git a/MHFZ_Overlay/Models/Collections/Achievements.cs b/MHFZ_Overlay/Models/Collections/Achievements.cs index 96180deb..1ebeb2c1 100644 --- a/MHFZ_Overlay/Models/Collections/Achievements.cs +++ b/MHFZ_Overlay/Models/Collections/Achievements.cs @@ -5806,5 +5806,83 @@ [] [U] [] [] [] Hint = "POWER!", } }, + { + 441, new Achievement() + { + CompletionDate = DateTime.UnixEpoch, + Title = "Muse", + Description = string.Empty, + Rank = AchievementRank.Gold, + Image = @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/diva_fountain.png", + Objective = "Use Diva Song buff in 100 quests.", + IsSecret = false, + Hint = string.Empty, + } + }, + { + 442, new Achievement() + { + CompletionDate = DateTime.UnixEpoch, + Title = "Blessed Hunter", + Description = string.Empty, + Rank = AchievementRank.Platinum, + Image = @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/diva_prayer_gems.png", + Objective = "Use Diva prayer gems in 777 quests.", + IsSecret = false, + Hint = string.Empty, + } + }, + { + 443, new Achievement() + { + CompletionDate = DateTime.UnixEpoch, + Title = "Oink oink!", + Description = string.Empty, + Rank = AchievementRank.Silver, + Image = @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/my_tore.png", + Objective = "Use guild poogie skill in 100 quests.", + IsSecret = false, + Hint = string.Empty, + } + }, + { + 444, new Achievement() + { + CompletionDate = DateTime.UnixEpoch, + Title = "Halk's Friend", + Description = string.Empty, + Rank = AchievementRank.Gold, + Image = @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/my_support.png", + Objective = "Level up your halk to LV3.", + IsSecret = false, + Hint = string.Empty, + } + }, + { + 445, new Achievement() + { + CompletionDate = DateTime.UnixEpoch, + Title = "Active Hunter", + Description = string.Empty, + Rank = AchievementRank.Silver, + Image = @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/small_gs.png", + Objective = "Use the active features of all weapon types.", + IsSecret = false, + Hint = string.Empty, + } + }, + { + 446, new Achievement() + { + CompletionDate = DateTime.UnixEpoch, + Title = "That's a Lotta Damage!", + Description = string.Empty, + Rank = AchievementRank.Silver, + Image = @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/attack_up.png", + Objective = "Reach the maximum true raw in a quest.", + IsSecret = false, + Hint = string.Empty, + } + } }); } diff --git a/MHFZ_Overlay/Models/Collections/DivaPrayerGems.cs b/MHFZ_Overlay/Models/Collections/DivaPrayerGems.cs new file mode 100644 index 00000000..4173b1c8 --- /dev/null +++ b/MHFZ_Overlay/Models/Collections/DivaPrayerGems.cs @@ -0,0 +1,46 @@ +// © 2023 The mhfz-overlay developers. +// Use of this source code is governed by a MIT license that can be +// found in the LICENSE file. + +namespace MHFZ_Overlay.Models.Collections; + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using MHFZ_Overlay.Models.Structures; + +/// +/// The diva prayer gems list. +/// +public static class DivaPrayerGems +{ + public static ReadOnlyDictionary PrayerGems { get; } = new(new Dictionary + { + { DivaPrayerGemType.None, new DivaPrayerGem(){ Description= "None", Level=0, MaxLevel=0, Type = DivaPrayerGemType.None} }, + { DivaPrayerGemType.WindStorm, new DivaPrayerGem(){ Description= "Sharpness does not decrease with blademaster weapons. Works for 5, 10 or 20 quests depending on level during the prayer active window.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.WindStorm} }, + { DivaPrayerGemType.Agility, new DivaPrayerGem(){ Description= "Reduces the recoil and reload speed of Gunner weapons.", Level=0, MaxLevel=7, Type = DivaPrayerGemType.Agility, PartyEffect=true, Unused=true} }, + { DivaPrayerGemType.SeveringPower, new DivaPrayerGem(){ Description= "Tails can be cut with any damage type.", Level=0, MaxLevel=1, Type = DivaPrayerGemType.SeveringPower} }, + { DivaPrayerGemType.Elegance, new DivaPrayerGem(){ Description= "Adds passive HP recovery to all quests.", Level=0, MaxLevel=2, Type = DivaPrayerGemType.Elegance} }, + { DivaPrayerGemType.Earth, new DivaPrayerGem(){ Description= "Makes it easier to scare monsters by attacking with Earth Style.", Level=0, MaxLevel=4, Type = DivaPrayerGemType.Earth, Unused=true} }, + { DivaPrayerGemType.Heaven, new DivaPrayerGem(){ Description= "Makes it easier to scare monsters by attacking with Heaven Style.", Level=0, MaxLevel=4, Type = DivaPrayerGemType.Heaven, Unused=true} }, + { DivaPrayerGemType.Tempest, new DivaPrayerGem(){ Description= "Makes it easier to scare monsters by attacking with Storm Style.", Level=0, MaxLevel=4, Type = DivaPrayerGemType.Tempest, Unused=true} }, + { DivaPrayerGemType.CuttingEdge, new DivaPrayerGem(){ Description= "Increases the amount of raw damage dealt by a cutting weapon by adjusting hitboxes to be weaker against the damage type.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.CuttingEdge} }, + { DivaPrayerGemType.Striking, new DivaPrayerGem(){ Description= "Increases the amount of raw damage dealt by an impact weapon by adjusting hitboxes to be weaker against the damage type.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.Striking} }, + { DivaPrayerGemType.RisingBullet, new DivaPrayerGem(){ Description= "Increases the amount of raw damage dealt by a ranged weapon by adjusting hitboxes to be weaker against the damage type.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.RisingBullet} }, + { DivaPrayerGemType.StatusLength, new DivaPrayerGem(){ Description= "Increases the duration of status effects on monsters.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.StatusLength, PartyEffect=true} }, + { DivaPrayerGemType.Abnormality, new DivaPrayerGem(){ Description= "Monsters are more susceptible to status ailments.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.Abnormality, PartyEffect=true, Unused=true} }, + { DivaPrayerGemType.Lethality, new DivaPrayerGem(){ Description= "Increases damage when striking body parts upon which your attacks are highly effective. However, element damage does not change.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.Lethality, Unused=true} }, + { DivaPrayerGemType.HeavyThunder, new DivaPrayerGem(){ Description= "Elemental damage increases based on level.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.HeavyThunder} }, + { DivaPrayerGemType.Unshakable, new DivaPrayerGem(){ Description= "Monsters cannot flee if in the same area as a hunter.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.Unshakable, PartyEffect=true} }, + { DivaPrayerGemType.Ringing, new DivaPrayerGem(){ Description= "Adds new items to the GCP store based on level.", Level=0, MaxLevel=1, Type = DivaPrayerGemType.Ringing} }, + { DivaPrayerGemType.Mobilisation, new DivaPrayerGem(){ Description= "Attack will go up based on the number of human hunters in a quest.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.Mobilisation, PartyEffect=true} }, + { DivaPrayerGemType.Protection, new DivaPrayerGem(){ Description= "Gives Divine Protection, Goddess' Embrace or Soul Revival based on level.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.Protection} }, + { DivaPrayerGemType.PowerfulStrikes, new DivaPrayerGem(){ Description= "Increases affinity of all weapons based on the level of the song.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.PowerfulStrikes} }, + { DivaPrayerGemType.Fireproof, new DivaPrayerGem(){ Description= "Increases fire resistance.", Level=0, MaxLevel=0, Type = DivaPrayerGemType.Fireproof, Unused=true} }, + { DivaPrayerGemType.Waterproof, new DivaPrayerGem(){ Description= "Increases water resistance.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.Waterproof, Unused=true} }, + { DivaPrayerGemType.Iceproof, new DivaPrayerGem(){ Description= "Increases ice resistance.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.Iceproof, Unused=true} }, + { DivaPrayerGemType.Dragonproof, new DivaPrayerGem(){ Description= "Increases dragon resistance.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.Dragonproof, Unused=true} }, + { DivaPrayerGemType.Thunderproof, new DivaPrayerGem(){ Description= "Increases thunder resistance.", Level=0, MaxLevel=3, Type = DivaPrayerGemType.Thunderproof, Unused=true} }, + { DivaPrayerGemType.Immunity, new DivaPrayerGem(){ Description= "Increases resistance to all elements.", Level=0, MaxLevel=2, Type = DivaPrayerGemType.Immunity, Unused=true} }, + + }); +} diff --git a/MHFZ_Overlay/Models/Collections/DivaPrayerGemsPoints.cs b/MHFZ_Overlay/Models/Collections/DivaPrayerGemsPoints.cs new file mode 100644 index 00000000..4ac952d8 --- /dev/null +++ b/MHFZ_Overlay/Models/Collections/DivaPrayerGemsPoints.cs @@ -0,0 +1,47 @@ +// © 2023 The mhfz-overlay developers. +// Use of this source code is governed by a MIT license that can be +// found in the LICENSE file. + +namespace MHFZ_Overlay.Models.Collections; + +using System.Collections.Generic; +using System.Collections.ObjectModel; + +/// +/// The diva prayer gems points target list. +/// +public static class DivaPrayerGemsPoints +{ + public static ReadOnlyDictionary Targets { get; } = new (new Dictionary + { + { 0, new DivaPrayerGemPoint(){ Points = 0, MaxUses = 0, Level = 0,} }, + { 500000, new DivaPrayerGemPoint(){ Points = 500_000, MaxUses = 1, Level = 0,} }, + { 1000000, new DivaPrayerGemPoint(){ Points = 1_000000, MaxUses = 2, Level = 0,} }, + { 2000000, new DivaPrayerGemPoint(){ Points = 2_000000, MaxUses = 3, Level = 0,} }, + { 3000000, new DivaPrayerGemPoint(){ Points = 3_000000, MaxUses = 4, Level = 0,} }, + { 4000000, new DivaPrayerGemPoint(){ Points = 4_000000, MaxUses = 5, Level = 0,} }, + { 5000000, new DivaPrayerGemPoint(){ Points = 5_000000, MaxUses = 6, Level = 0,} }, + { 6000000, new DivaPrayerGemPoint(){ Points = 6_000000, MaxUses = 7, Level = 0,} }, + { 7000000, new DivaPrayerGemPoint(){ Points = 7_000000, MaxUses = 8, Level = 0,} }, + { 8000000, new DivaPrayerGemPoint(){ Points = 8_000000, MaxUses = 9, Level = 0,} }, + { 9000000, new DivaPrayerGemPoint(){ Points = 9_000_000, MaxUses = 10, Level = 1,} }, + + { 10000000, new DivaPrayerGemPoint(){ Points = 10_000000, MaxUses = 11, Level = 1,} }, + { 15000000, new DivaPrayerGemPoint(){ Points = 15_000000, MaxUses = 12, Level = 1,} }, + { 20000000, new DivaPrayerGemPoint(){ Points = 20_000000, MaxUses = 13, Level = 1,} }, + { 25000000, new DivaPrayerGemPoint(){ Points = 25_000000, MaxUses = 14, Level = 1,} }, + { 30000000, new DivaPrayerGemPoint(){ Points = 30_000000, MaxUses = 15, Level = 2,} }, + + { 35000000, new DivaPrayerGemPoint(){ Points = 35_000000, MaxUses = 16, Level = 2,} }, + { 40000000, new DivaPrayerGemPoint(){ Points = 40_000000, MaxUses = 17, Level = 2,} }, + { 45000000, new DivaPrayerGemPoint(){ Points = 45_000000, MaxUses = 18, Level = 2,} }, + { 50000000, new DivaPrayerGemPoint(){ Points = 50_000000, MaxUses = 19, Level = 2,} }, + { 55000000, new DivaPrayerGemPoint(){ Points = 55_000000, MaxUses = 20, Level = 3,} }, + + { 60000000, new DivaPrayerGemPoint(){ Points = 60_000_000, MaxUses = 21, Level = 3,} }, + { 70000000, new DivaPrayerGemPoint(){ Points = 70_000_000, MaxUses = 22, Level = 3,} }, + { 80000000, new DivaPrayerGemPoint(){ Points = 80_000_000, MaxUses = 23, Level = 3,} }, + { 90000000, new DivaPrayerGemPoint(){ Points = 90_000_000, MaxUses = 24, Level = 3,} }, + { 100000000, new DivaPrayerGemPoint(){ Points = 100_000_000, MaxUses = 25, Level = 3,} }, + }); +} diff --git a/MHFZ_Overlay/Models/Collections/Monsters.cs b/MHFZ_Overlay/Models/Collections/Monsters.cs index ea0d4b54..0c69119d 100644 --- a/MHFZ_Overlay/Models/Collections/Monsters.cs +++ b/MHFZ_Overlay/Models/Collections/Monsters.cs @@ -47,7 +47,7 @@ public static class Monsters { 29, "Rocks" }, { 30, "Ioprey" }, { 31, "Iodrome" }, - { 32, "Pugis" }, + { 32, "Poogies" }, { 33, "Kirin" }, { 34, "Cephalos" }, { 35, "Giaprey / Giadrome" }, diff --git a/MHFZ_Overlay/Models/DivaPrayerGem.cs b/MHFZ_Overlay/Models/DivaPrayerGem.cs new file mode 100644 index 00000000..f8f88f6c --- /dev/null +++ b/MHFZ_Overlay/Models/DivaPrayerGem.cs @@ -0,0 +1,30 @@ +// © 2023 The mhfz-overlay developers. +// Use of this source code is governed by a MIT license that can be +// found in the LICENSE file. + +namespace MHFZ_Overlay.Models; + +using System; +using MHFZ_Overlay.Models.Structures; + +// TODO: ORM +public sealed class DivaPrayerGem +{ + public DivaPrayerGemType Type { get; set; } + + public int Level { get; set; } + + public int MaxLevel { get; set; } + + public string Description { get; set; } = "None"; + + /// + /// Unused in official servers. + /// + public bool Unused { get; set; } + + /// + /// Whether the gem affects the whole party or only the user. + /// + public bool PartyEffect { get; set; } +} diff --git a/MHFZ_Overlay/Models/DivaPrayerGemPoint.cs b/MHFZ_Overlay/Models/DivaPrayerGemPoint.cs new file mode 100644 index 00000000..9b7b3ad5 --- /dev/null +++ b/MHFZ_Overlay/Models/DivaPrayerGemPoint.cs @@ -0,0 +1,20 @@ +// © 2023 The mhfz-overlay developers. +// Use of this source code is governed by a MIT license that can be +// found in the LICENSE file. + +namespace MHFZ_Overlay.Models; + +using System; +using MHFZ_Overlay.Models.Structures; + +// TODO: ORM +public sealed class DivaPrayerGemPoint +{ + public DivaPrayerGemColor Color { get; set; } = DivaPrayerGemColor.None; + + public int Points { get; set; } + + public int MaxUses { get; set; } + + public int Level { get; set; } +} diff --git a/MHFZ_Overlay/Models/FastestRun.cs b/MHFZ_Overlay/Models/FastestRun.cs index 27ff1f91..7bf7f3df 100644 --- a/MHFZ_Overlay/Models/FastestRun.cs +++ b/MHFZ_Overlay/Models/FastestRun.cs @@ -17,7 +17,7 @@ public sealed class FastestRun public long QuestID { get; set; } - public string YoutubeID { get; set; } = Messages.RickRollID; + public string YouTubeID { get; set; } = Messages.RickRollID; public string FinalTimeDisplay { get; set; } = Messages.MaximumTimerPlaceholder; diff --git a/MHFZ_Overlay/Models/QuestsActiveFeature.cs b/MHFZ_Overlay/Models/QuestsActiveFeature.cs new file mode 100644 index 00000000..1a037d7c --- /dev/null +++ b/MHFZ_Overlay/Models/QuestsActiveFeature.cs @@ -0,0 +1,17 @@ +// © 2023 The mhfz-overlay developers. +// Use of this source code is governed by a MIT license that can be +// found in the LICENSE file. + +namespace MHFZ_Overlay.Models; + +using System; + +// TODO: ORM +public sealed class QuestsActiveFeature +{ + public long? QuestsActiveFeatureID { get; set; } + + public long? ActiveFeature { get; set; } + + public long? RunID { get; set; } +} diff --git a/MHFZ_Overlay/Models/QuestsCourse.cs b/MHFZ_Overlay/Models/QuestsCourse.cs new file mode 100644 index 00000000..baca0c85 --- /dev/null +++ b/MHFZ_Overlay/Models/QuestsCourse.cs @@ -0,0 +1,17 @@ +// © 2023 The mhfz-overlay developers. +// Use of this source code is governed by a MIT license that can be +// found in the LICENSE file. + +namespace MHFZ_Overlay.Models; + +using System; + +// TODO: ORM +public sealed class QuestsCourse +{ + public long? QuestsCourseID { get; set; } + + public long? Rights { get; set; } + + public long? RunID { get; set; } +} diff --git a/MHFZ_Overlay/Models/QuestsDiva.cs b/MHFZ_Overlay/Models/QuestsDiva.cs new file mode 100644 index 00000000..0792c017 --- /dev/null +++ b/MHFZ_Overlay/Models/QuestsDiva.cs @@ -0,0 +1,23 @@ +// © 2023 The mhfz-overlay developers. +// Use of this source code is governed by a MIT license that can be +// found in the LICENSE file. + +namespace MHFZ_Overlay.Models; + +using System; + +// TODO: ORM +public sealed class QuestsDiva +{ + public long? QuestsDivaID { get; set; } + public long? DivaSongBuffOn { get; set; } + public long? DivaPrayerGemRedSkill { get; set; } + public long? DivaPrayerGemRedLevel { get; set; } + public long? DivaPrayerGemYellowSkill { get; set; } + public long? DivaPrayerGemYellowLevel { get; set; } + public long? DivaPrayerGemGreenSkill { get; set; } + public long? DivaPrayerGemGreenLevel { get; set; } + public long? DivaPrayerGemBlueSkill { get; set; } + public long? DivaPrayerGemBlueLevel { get; set; } + public long? RunID { get; set; } +} diff --git a/MHFZ_Overlay/Models/QuestsGuildPoogie.cs b/MHFZ_Overlay/Models/QuestsGuildPoogie.cs new file mode 100644 index 00000000..0cca5513 --- /dev/null +++ b/MHFZ_Overlay/Models/QuestsGuildPoogie.cs @@ -0,0 +1,21 @@ +// © 2023 The mhfz-overlay developers. +// Use of this source code is governed by a MIT license that can be +// found in the LICENSE file. + +namespace MHFZ_Overlay.Models; + +using System; + +// TODO: ORM +public sealed class QuestsGuildPoogie +{ + public long? QuestsGuildPoogieID { get; set; } + + public long? GuildPoogie1Skill { get; set; } + + public long? GuildPoogie2Skill { get; set; } + + public long? GuildPoogie3Skill { get; set; } + + public long? RunID { get; set; } +} diff --git a/MHFZ_Overlay/Models/QuestsHalk.cs b/MHFZ_Overlay/Models/QuestsHalk.cs new file mode 100644 index 00000000..1dc4f67d --- /dev/null +++ b/MHFZ_Overlay/Models/QuestsHalk.cs @@ -0,0 +1,57 @@ +// © 2023 The mhfz-overlay developers. +// Use of this source code is governed by a MIT license that can be +// found in the LICENSE file. + +namespace MHFZ_Overlay.Models; + +using System; + +// TODO: ORM +public sealed class QuestsHalk +{ + public long? QuestsHalkID { get; set; } + + public long? HalkOn { get; set; } + + public long? HalkPotEffectOn { get; set; } + + public long? HalkFullness { get; set; } + + public long? HalkLevel { get; set; } + + public long? HalkIntimacy { get; set; } + + public long? HalkHealth { get; set; } + + public long? HalkAttack { get; set; } + + public long? HalkDefense { get; set; } + + public long? HalkIntellect { get; set; } + + public long? HalkSkill1 { get; set; } + + public long? HalkSkill2 { get; set; } + + public long? HalkSkill3 { get; set; } + + public long? HalkElementNone { get; set; } + + public long? HalkFire { get; set; } + + public long? HalkThunder { get; set; } + + public long? HalkWater { get; set; } + + public long? HalkIce { get; set; } + + public long? HalkDragon { get; set; } + + public long? HalkSleep { get; set; } + + public long? HalkParalysis { get; set; } + + public long? HalkPoison { get; set; } + + public long? RunID { get; set; } +} diff --git a/MHFZ_Overlay/Models/QuestsOverlayHash.cs b/MHFZ_Overlay/Models/QuestsOverlayHash.cs new file mode 100644 index 00000000..86788f7f --- /dev/null +++ b/MHFZ_Overlay/Models/QuestsOverlayHash.cs @@ -0,0 +1,17 @@ +// © 2023 The mhfz-overlay developers. +// Use of this source code is governed by a MIT license that can be +// found in the LICENSE file. + +namespace MHFZ_Overlay.Models; + +using System; + +// TODO: ORM +public sealed class QuestsOverlayHash +{ + public long? QuestsOverlayHashID { get; set; } + + public string? OverlayHash { get; set; } + + public long? RunID { get; set; } +} diff --git a/MHFZ_Overlay/Models/RecentRuns.cs b/MHFZ_Overlay/Models/RecentRuns.cs index a8320aaf..3fa340b7 100644 --- a/MHFZ_Overlay/Models/RecentRuns.cs +++ b/MHFZ_Overlay/Models/RecentRuns.cs @@ -17,7 +17,7 @@ public sealed class RecentRuns public long QuestID { get; set; } - public string YoutubeID { get; set; } = Messages.RickRollID; + public string YouTubeID { get; set; } = Messages.RickRollID; public string FinalTimeDisplay { get; set; } = Messages.MaximumTimerPlaceholder; diff --git a/MHFZ_Overlay/Models/Structures/Bitfields.cs b/MHFZ_Overlay/Models/Structures/Bitfields.cs index ee0fe56a..bc4dcc45 100644 --- a/MHFZ_Overlay/Models/Structures/Bitfields.cs +++ b/MHFZ_Overlay/Models/Structures/Bitfields.cs @@ -245,7 +245,7 @@ public enum QuestWeaponTypesDisabled : uint Bow = 1024, Tonfa = 2048, SwitchAxeF = 4096, - // MS Flag 64 + // TODO MS Flag 64 } /// @@ -391,3 +391,119 @@ public enum GauntletBoost : uint Solstice = 2, Musou = 4, } + +/// +/// Course Rights first byte. Byte position 1. +/// +[Flags] +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum CourseRightsFirstByte : uint +{ + /// + /// or netcafe? + /// + [DefaultValue(None)] + None = 0, + Assist = 1, + N = 2, + Hiden = 4, + /// + /// or aid + /// + Support = 8, + NBoost = 16, + All = Assist | N | Hiden | Support | NBoost, +} + +/// +/// Course Rights second byte. Byte position 0. +/// +[Flags] +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum CourseRightsSecondByte : uint +{ + [DefaultValue(None)] + None = 0, + UNK1 = 1, + Trial = 2, + HunterLife = 4, + Extra = 8, + UNK2 = 16, + UNK3 = 32, + Premium = 64, + All = UNK1 | Trial | HunterLife | Extra | UNK2 | UNK3 | Premium, +} + +/// +/// TODO Run filters by buff. +/// +[Flags] +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum RunBuff : uint +{ + [DefaultValue(None)] + None = 0, + Halk = 1, + PoogieItem = 2, + DivaSong = 4, + HalkPotEffect = 8, + Bento = 16, + GuildPoogie = 32, + ActiveFeature = 64, + GuildFood = 128, + DivaSkill = 256, + SecretTechnique = 512, + DivaPrayerGem = 1024, + /// + /// Can come from hunter aid/support course. + /// + CourseAttackBoost = 2048, + + // old categories + TimeAttack = PoogieItem | DivaSong | Bento, + FreestyleNoSecretTech = Halk | PoogieItem | DivaSong | Bento | GuildPoogie | ActiveFeature | GuildFood | DivaSkill | DivaPrayerGem, + FreestyleWithSecretTech = FreestyleNoSecretTech | SecretTechnique, +} + +/// +/// Equipment type. +/// +[Flags] +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum EquipmentType : uint +{ + [DefaultValue(General)] + General = 0, + SP = 1, + Gou = 2, + Evolution = 4, + HC = 8, + UNK1 = 16, + Ravi = HC | UNK1, +} + +/// +/// Active features enabled. +/// +[Flags] +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum ActiveFeature : uint +{ + [DefaultValue(None)] + None = 0, + GreatSword = 1, + HeavyBowgun = 2, + Hammer = 4, + Lance = 8, + SwordAndShield = 16, + LightBowgun = 32, + DualSwords = 64, + LongSword = 128, + HuntingHorn = 256, + Gunlance = 512, + Bow = 1024, + Tonfa = 2048, + SwitchAxeF = 4096, + MagnetSpike = 8192, + All = GreatSword | HeavyBowgun | Hammer | Lance | SwordAndShield | LightBowgun | DualSwords | LongSword | HuntingHorn | Gunlance | Bow | Tonfa | SwitchAxeF | MagnetSpike, +} diff --git a/MHFZ_Overlay/Models/Structures/Enums.cs b/MHFZ_Overlay/Models/Structures/Enums.cs index dbe46e97..28417a4b 100644 --- a/MHFZ_Overlay/Models/Structures/Enums.cs +++ b/MHFZ_Overlay/Models/Structures/Enums.cs @@ -515,3 +515,332 @@ public enum FrontierMonsterType Leviathan, FangedWyvern, } + +/// +/// Weapon and armor. +/// +public enum EquipmentSlot +{ + None, + One, + Two, + Three +} + +/// +/// +/// +public enum FrontierEquipmentType +{ + Legs, + Unk1, + Head, + Chest, + Arms, + Waist, + Melee, + Ranged, +} + +/// +/// +/// +public enum FrontierItemIcon : uint +{ + SMOKE = 0x00, // Ex: Farcaster, Smoke Bomb + ORB = 0x01, // Ex: Paintball, Flash Bomb + BOMB = 0x02, // Ex: Small Barrel Bomb + MEAT = 0x03, // Ex: Raw Meat, Burnt Steak + FISH_BAIT = 0x04, // Ex: Cricket, Worm, Tuna Bait + FISH = 0x05, // Ex: Gormet Fish + BOX = 0x06, // Ex: Trap Kit + WHETSTONE = 0x07, // Ex: Whetstone + DUNG = 0x08, // Ex: Dung, Dung Bomb + MONSTER = 0x09, // Ex: Bullfango Head + BONES = 0x0A, // Ex: Sm Monster Bone + BINOCULARS = 0x0B, // Ex: Binoculars + MUSHROOM = 0x0C, // Ex: Blue Mushroom + BUGNET = 0x0D, // Ex: Bugnet + PELT = 0x0E, // Ex: Mosswine Hide, Bullfango Pelt + LEAF = 0x0F, // Ex: Herb + PICKAXE = 0x10, // Ex: Iron Pickaxe + BARREL = 0x11, // Ex: Small Barrel, Large Barrel + SEED = 0x12, // Ex: Paintberry, Power Seed, Scatternut + BBQ_SPIT = 0x13, // Ex: BBQ Spit + INSECT = 0x14, // Ex: Insect Husk, Thunderbug + TRAP = 0x15, // Ex: Pitfall Trap, Shock Trap + NET = 0x16, // Ex: Web, Net + SCALE = 0x17, // Ex: Broken Shell, Kut-Ku Scale + DRINK = 0x18, // Ex: Potion, Cold Drink, Demondrug + EGG = 0x19, // Ex: Monster Egg, Round Egg + AMMO = 0x1A, // Ex: Normal S LV1 + STONE = 0x1B, // Ex: Stone, Iron Ore + HUSK = 0x1C, // Ex: Huskberry, Bone Husks + MAP = 0x1D, // Ex: Map + FLUTE = 0x1E, // Ex: Flute + FANG = 0x1F, // Ex: Wyvern Fang, Wyvern Claw + GRID = 0x20, // Ex: Honey + QUESTION_MARK = 0x21, // Ex: Garbage + COIN = 0x22, // Ex: N Medal, Hunting Medal + SAC = 0x23, // Ex: Catalyst, Screamer Sac, Might Pill + BOOK = 0x24, // Ex: Book of Combos + TICKET = 0x25, // Ex: Guild Ticket, Commendation, 30k Cheque + KNIFE = 0x26, // Ex: Throwing Knife + MUSIC_SHEET = 0x29, // Ex: HH Info + JEWEL = 0x2B, // Ex: Aquaglow Jewel, Attack 1 Deco + HOUSE = 0x2C, // Ex: House Expansion 1 + PlANT = 0x2D, // Ex: Green Onion + MOCHA = 0x2F, // Ex: Mocha Green + POT = 0x30, // Ex: Mocha Pot + BOOMERANG = 0x31, // Ex: Boomerang + COATING = 0x32, // Ex: Power Coating, Poison Coating + EMPTY_BOTTLE = 0x33, // Ex: Empty Bottle + CARAPACE = 0x34, // Ex: Small LobsterShl, Hermitaur Shell + /* Probably wrong + COMPASS = 0x35, // Datamined + QUARTER_CIRCLE = 0x36, // Datamined + CROSS = 0x37, // Datamined + CROSSED_OUT = 0x3C, // Datamined + MYSTERY_WEAPON = 0x3D, // Datamined + GREATSWORD = 0x3E, // Datamined + HEAVY_BOWGUN = 0x3F, // Datamined + HAMMER = 0x40, // Datamined + LANCE = 0x41, // Datamined + SWORD_SHIELD = 0x42, // Datamined + LIGHT_BOWGUN = 0x43, // Datamined + DUAL_BLADES = 0x44, // Datamined + LONGSWORD = 0x45, // Datamined + HUNTING_HORN = 0x46, // Datamined + GUNLANCE = 0x47, // Datamined + BOW = 0x48, // Datamined + LEGWEAR = 0x49, // Datamined + EQUIPED_MARKER = 0x4A, // Datamined + HEADWEAR = 0x4B, // Datamined + BODYWEAR = 0x4C, // Datamined + ARMWEAR = 0x4D, // Datamined + WAISTWEAR = 0x4E, // Datamined + */ + SWORD_CRYSTAL = 0x4F, // Ex: Bomb S.Crystal + POTION = 0x50, // Ex: Halk Pot + FRUIT = 0x52, // Ex: Shiriagari Fruit + /* Definitely wrong + SIGIL = 0x54, // Datamined + G_RANK_MARKER = 0x58, // Datamined + CAMERA = 0x5B, // Datamined + CORNER_ARROWS = 0x5C, // Datamined + TONFAS = 0x5D, // Datamined + TREASURE = 0x5E, // Datamined + PARTNYA_SWORD = 0x60, // Datamined + PARTNYA_CLUB = 0x61, // Datamined + PARTNYA_HELM = 0x62, // Datamined + PARTNYA_CHEST = 0x63, // Datamined + PARTNYA_HEAD = 0x64, // Datamined + PARTNYA_CROWN = 0x65, // Datamined + PARTNYA_MARKER = 0x66, // Datamined + TOWER_MARKER = 0x67, // Datamined + */ + TOWER_W_SIGIL = 0x56, // Ex: (SnS) Beam Slash + TOWER_A_SIGIL = 0x5B, // Ex: Skill UP Sigil + MANTLE = 0x5C, // Ex: Narga Mantle + ARMOR_SPHERE = 0x5D, // Ex: Armor Sphere +}; + +/// +/// +/// +public enum FrontierElement +{ + None, + Fire, + Water, + Thunder, + Dragon, + Ice, + Flame, + Light, + ThunderPole, + Tenshou, + Okiko, + BlackFlame, + Kanade, + Darkness, + CrimsonDemon, + Wind, + Sound, + BurningZero, + EmperorsRoar, +} + +/// +/// +/// +public enum FrontierStatusAilment +{ + None, + Poison, + Paralysis, + Sleep, + Blast, +} + +/// +/// +/// +public enum FrontierMap : uint +{ + Siege_Fortress_Day = 1, + Forest_and_Hills_Day = 2, + Desert_Day = 3, + Swamp_Day = 4, + Volcano_Day = 5, + Jungle_Day = 6, + Castle_Schrade = 7, + Crimson_Battleground = 8, + Arena_with_Ledge_Day = 9, + Arena_with_Pillar_Day = 10, + Snowy_Mountains_Day = 11, + Town_Siege_Day = 12, + Tower_1 = 13, + Tower_2 = 14, + Tower_3 = 15, + Forest_and_Hills_Night = 16, + Desert_Night = 17, + Swamp_Night = 18, + Volcano_Night = 19, + Jungle_Night = 20, + Snowy_Mountains_Night = 21, + Town_Siege_night = 22, + Siege_Fortress_Night = 23, + Arena_with_Ledge_Night = 24, + Arena_with_Pillar_Night = 25, + Great_Forest_Day = 26, + Great_Forest_Night = 27, + Volcano_2_Day = 28, + Volcano_2_Night = 29, + Jungle_Dream = 30, + Gorge_Day = 31, + Gorge_Night = 32, + Battlefield_Day = 35, + Top_of_Great_Forest = 44, + Caravan_Balloon_Day = 45, + Caravan_Balloon_Night = 46, + Solitude_Isle_1 = 47, + Solitude_Isle_2 = 48, + Solitude_Isle_3 = 49, + Highlands_Day = 50, + Highlands_Night = 51, + Tower_with_Nesthole = 52, + Arena_with_Moat_Day = 53, + Arena_with_Moat_Night = 54, + Fortress_Day = 55, + Fortress_Night = 56, + Tidal_Island_Day = 57, + Tidal_Island_Night = 58, + Polar_Sea_Day = 60, + Polar_Sea_Night = 61, + Worlds_End = 62, + Large_Airship = 63, + Flower_Field_Day = 64, + Flower_Field_Night = 65, + Deep_Crater = 66, + Bamboo_Forest_Day = 67, + Bamboo_Forest_Night = 68, + Battlefield_2_Day = 69, + Unimplemented_map = 70, + Ist_Dist_Tower_1 = 71, + Ist_Dist_Tower_2 = 72, + Tnd_Dist_Tower_1 = 73, + Tnd_Dist_Tower_2 = 74, + Urgent_Tower = 75, + n3rd_Dist_Tower = 76, + n3rd_Dist_Tower_2 = 77, + n4th_Dist_Tower = 78, + White_Lake_Day = 79, + White_Lake_Night = 80, + Solitude_Depths_Slay_1 = 81, + Solitude_Depths_Slay_2 = 82, + Solitude_Depths_Slay_3 = 83, + Solitude_Depths_Slay_4 = 84, + Solitude_Depths_Slay_5 = 85, + Solitude_Depths_Support_1 = 86, + Solitude_Depths_Support_2 = 87, + Solitude_Depths_Support_3 = 88, + Solitude_Depths_Support_4 = 89, + Solitude_Depths_Support_5 = 90, + Cloud_Viewing_Fortress = 91, + Painted_Falls_Day = 92, + Painted_Falls_Night = 93, + Sanctuary = 94, + Hunters_Road = 95, + Sacred_Pinnacle = 96, + Historic_Site = 97 +}; + +/// +/// +/// +public enum FrontierIconColor +{ + White = 0, + Red = 1, + Green = 2, + Blue = 3, + Yellow = 4, + Purple = 5, + LightBlue = 6, + Unk1 = 7, + Pink = 8, + Unk2 = 9, + Gray = 10, +} + +/// +/// +/// +public enum FrontierMonsterRank +{ + Unk1 = -1, + LowRank = 0, + HighRank = 1, + Gou = 3, + GRank = 7, +} + +public enum DivaPrayerGemType +{ + None = 0, + WindStorm = 1, + Agility = 2, + SeveringPower = 3, + Elegance = 4, + Earth = 5, + Heaven = 6, + Tempest = 7, + CuttingEdge = 8, + Striking = 9, + RisingBullet = 10, + StatusLength = 11, + Abnormality = 12, + Lethality = 13, + HeavyThunder = 14, + Unshakable = 15, + Ringing = 16, + Mobilisation = 17, + Protection = 18, + PowerfulStrikes = 19, + Fireproof = 20, + Waterproof = 21, + Iceproof = 22, + Dragonproof = 23, + Thunderproof = 24, + Immunity = 25, +} + +public enum DivaPrayerGemColor +{ + None, + Red, + Yellow, + Green, + Blue, +} diff --git a/MHFZ_Overlay/Services/AchievementService.cs b/MHFZ_Overlay/Services/AchievementService.cs index 9c929ecc..f22af6ec 100644 --- a/MHFZ_Overlay/Services/AchievementService.cs +++ b/MHFZ_Overlay/Services/AchievementService.cs @@ -12,12 +12,14 @@ namespace MHFZ_Overlay.Services; using System.Linq; using System.Windows; using System.Windows.Media; +using EZlion.Mapper; using MHFZ_Overlay; using MHFZ_Overlay.Models; using MHFZ_Overlay.Models.Collections; using MHFZ_Overlay.Models.Constant; using MHFZ_Overlay.Models.Structures; using MHFZ_Overlay.Services.Contracts; +using MHFZ_Overlay.ViewModels.Windows; using MHFZ_Overlay.Views.Windows; using Newtonsoft.Json; using NLog; @@ -2247,7 +2249,7 @@ join playerGear in databaseManagerInstance.AllPlayerGear on quest.RunID equals p } case 342: - if (dataLoader.Model.GetOverlayMode() is OverlayMode.Freestyle or OverlayMode.FreestyleSecretTech ) + if (dataLoader.Model.GetOverlayMode() is OverlayMode.Freestyle or OverlayMode.FreestyleSecretTech or OverlayMode.TimeAttack) { return true; } @@ -2646,6 +2648,88 @@ join styleRankSkills in databaseManagerInstance.AllStyleRankSkills on quest.RunI { return false; } + case 441: + if (databaseManagerInstance.AllQuestsDiva.Count(questsDiva => questsDiva.DivaSongBuffOn > 0) >= 100) + { + return true; + } + else + { + return false; + } + case 442: + if (databaseManagerInstance.AllQuestsDiva.Count(questsDiva => (questsDiva.DivaPrayerGemRedLevel > 0 && questsDiva.DivaPrayerGemRedSkill > 0) || (questsDiva.DivaPrayerGemYellowLevel > 0 && questsDiva.DivaPrayerGemYellowSkill > 0) || (questsDiva.DivaPrayerGemGreenLevel > 0 && questsDiva.DivaPrayerGemGreenSkill > 0) || (questsDiva.DivaPrayerGemBlueLevel > 0 && questsDiva.DivaPrayerGemBlueSkill > 0)) >= 777) + { + return true; + } + else + { + return false; + } + case 443: + if (databaseManagerInstance.AllQuestsGuildPoogie.Count(questsGuildPoogie => questsGuildPoogie.GuildPoogie1Skill > 0 || questsGuildPoogie.GuildPoogie2Skill > 0 || questsGuildPoogie.GuildPoogie3Skill > 0) >= 100) + { + return true; + } + else + { + return false; + } + case 444: + return databaseManagerInstance.AllQuestsHalk.Any(quest => quest.HalkLevel == 3); + case 445: + // Initialize the array with zeros + int[] weaponUsageArray = new int[14]; + + foreach (var playerGear in databaseManagerInstance.AllPlayerGear) + { + // Find the corresponding active feature for the run + var activeFeature = databaseManagerInstance.AllQuestsActiveFeature.FirstOrDefault(af => af.RunID == playerGear.RunID); + + // If an active feature is found, update the array based on the weapon type + if (activeFeature != null) + { + var weaponType = (FrontierWeaponType)playerGear.WeaponTypeID; + + if (activeFeature.ActiveFeature == null) + { + activeFeature.ActiveFeature = 0; + } + + //if (dataLoader.Model.HasBitfieldFlag((uint)activeFeature.ActiveFeature, (ActiveFeature)weaponType, (uint)ActiveFeature.All)) + if (dataLoader.Model.IsActiveFeatureOn((long)activeFeature.ActiveFeature, playerGear.WeaponTypeID)) + { + weaponUsageArray[(int)weaponType] = 1; + } + } + } + + return weaponUsageArray.All(n => n == 1); + case 446: // TODO test + var maxTrueRaw = 8_000; + + var foundQuestData = from quest in databaseManagerInstance.AllQuests + where (quest.AttackBuffDictionary != null && + JsonConvert.DeserializeObject>(quest.AttackBuffDictionary) != null) + select quest; + + if (foundQuestData == null) + { + return false; + } + + var foundMaxTrueRaw = from quest in databaseManagerInstance.AllQuests + where (quest.AttackBuffDictionary != null && JsonConvert.DeserializeObject>(quest.AttackBuffDictionary)?.Values.Max() >= maxTrueRaw) + select quest; + + if (foundMaxTrueRaw != null && foundMaxTrueRaw.Any()) + { + return true; + } + else + { + return false; + } } } diff --git a/MHFZ_Overlay/Services/DatabaseService.cs b/MHFZ_Overlay/Services/DatabaseService.cs index 40d14d75..8d15a51f 100644 --- a/MHFZ_Overlay/Services/DatabaseService.cs +++ b/MHFZ_Overlay/Services/DatabaseService.cs @@ -32,11 +32,13 @@ namespace MHFZ_Overlay.Services; using MHFZ_Overlay.Models.Constant; using MHFZ_Overlay.Models.Messengers; using MHFZ_Overlay.Models.Structures; +using MHFZ_Overlay.ViewModels.Windows; using MHFZ_Overlay.Views.Windows; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; using Octokit; +using SharpCompress.Common; using Wpf.Ui.Common; using Wpf.Ui.Controls; using Formatting = Newtonsoft.Json.Formatting; @@ -86,6 +88,15 @@ public sealed class DatabaseService public HashSet AllQuestsToggleMode { get; set; } + public HashSet AllQuestsActiveFeature { get; set; } + + public HashSet AllQuestsHalk { get; set; } + + public HashSet AllQuestsDiva { get; set; } + + public HashSet AllQuestsGuildPoogie { get; set; } + + public TimeSpan SnackbarTimeOut { get; set; } = TimeSpan.FromSeconds(5); private string? connectionString { get; set; } @@ -484,6 +495,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(); using (var transaction = conn.BeginTransaction()) { @@ -708,8 +720,6 @@ public void InsertPersonalBest(DataLoader dataLoader, long currentPersonalBest, actualOverlayMode = GetActualOverlayMode(dataLoader); - var partySize = dataLoader.Model.PartySize(); - var monster1HPDictionary = dataLoader.Model.Monster1HPDictionary; var monster2HPDictionary = dataLoader.Model.Monster2HPDictionary; var monster3HPDictionary = dataLoader.Model.Monster3HPDictionary; @@ -806,82 +816,85 @@ public void InsertPersonalBest(DataLoader dataLoader, long currentPersonalBest, runID = Convert.ToInt32(cmd.ExecuteScalar(), CultureInfo.InvariantCulture); } - if (dataLoader.Model.PartySize() == 1) + long personalBest = 0; + var weaponType = dataLoader.Model.WeaponType(); + var improvedPersonalBest = false; + + using (var cmd = new SQLiteCommand( + @"SELECT + TimeLeft, + FinalTimeValue, + FinalTimeDisplay, + ActualOverlayMode, + PartySize, + pg.WeaponTypeID + FROM + Quests q + JOIN + PlayerGear pg ON q.RunID = pg.RunID + WHERE + q.QuestID = @questID + AND pg.WeaponTypeID = @weaponTypeID + AND q.ActualOverlayMode = @category + AND q.PartySize = @partySize + ORDER BY + FinalTimeValue ASC + LIMIT 1", conn)) { - long personalBest = 0; - var weaponType = dataLoader.Model.WeaponType(); - var improvedPersonalBest = false; + cmd.Parameters.AddWithValue("@questID", questID); + cmd.Parameters.AddWithValue("@weaponTypeID", weaponType); + cmd.Parameters.AddWithValue("@category", actualOverlayMode); + cmd.Parameters.AddWithValue("@partySize", partySize); - using (var cmd = new SQLiteCommand( - @"SELECT - TimeLeft, - FinalTimeValue, - FinalTimeDisplay, - ActualOverlayMode, - pg.WeaponTypeID - FROM - Quests q - JOIN - PlayerGear pg ON q.RunID = pg.RunID - WHERE - QuestID = @questID - AND pg.WeaponTypeID = @weaponTypeID - AND ActualOverlayMode = @category - AND PartySize = 1 - ORDER BY - FinalTimeValue ASC - LIMIT 1", conn)) + var reader = cmd.ExecuteReader(); + if (reader.Read()) { - cmd.Parameters.AddWithValue("@questID", questID); - cmd.Parameters.AddWithValue("@weaponTypeID", weaponType); - cmd.Parameters.AddWithValue("@category", actualOverlayMode); - - var reader = cmd.ExecuteReader(); - if (reader.Read()) - { - long time = 0; - time = reader.GetInt64(reader.GetOrdinal("FinalTimeValue")); - personalBest = time; - } + long time = 0; + time = reader.GetInt64(reader.GetOrdinal("FinalTimeValue")); + personalBest = time; } + } - sql = @"INSERT INTO PersonalBests( - RunID, - Attempts - ) VALUES ( - @RunID, - @Attempts)"; - using (var cmd = new SQLiteCommand(sql, conn)) + sql = @"INSERT INTO PersonalBests( + RunID, + Attempts, + PartySize + ) VALUES ( + @RunID, + @Attempts, + @PartySize)"; + using (var cmd = new SQLiteCommand(sql, conn)) + { + if (finalTimeValue < personalBest || personalBest == 0) { - if (finalTimeValue < personalBest || personalBest == 0) - { - improvedPersonalBest = true; - cmd.Parameters.AddWithValue("@RunID", runID); - cmd.Parameters.AddWithValue("@Attempts", attempts); + improvedPersonalBest = true; + cmd.Parameters.AddWithValue("@RunID", runID); + cmd.Parameters.AddWithValue("@Attempts", attempts); + cmd.Parameters.AddWithValue("@PartySize", partySize); - // Execute the stored procedure - cmd.ExecuteNonQuery(); - Logger.Debug("Inserted into PersonalBests table"); - } + // Execute the stored procedure + cmd.ExecuteNonQuery(); + Logger.Debug("Inserted into PersonalBests table"); } + } - if (improvedPersonalBest) + if (improvedPersonalBest) + { + sql = @"UPDATE PersonalBestAttempts + SET + Attempts = 0 + WHERE + (QuestID, WeaponTypeID, ActualOverlayMode, PartySize) = (@QuestID, @WeaponTypeID, @ActualOverlayMode, @PartySize)"; + using (var cmd = new SQLiteCommand(sql, conn)) { - sql = @"UPDATE PersonalBestAttempts - SET - Attempts = 0 - WHERE - (QuestID, WeaponTypeID, ActualOverlayMode) = (@QuestID, @WeaponTypeID, @ActualOverlayMode)"; - using (var cmd = new SQLiteCommand(sql, conn)) - { - cmd.Parameters.AddWithValue("@QuestID", questID); - cmd.Parameters.AddWithValue("@WeaponTypeID", weaponType); - cmd.Parameters.AddWithValue("@ActualOverlayMode", actualOverlayMode); + cmd.Parameters.AddWithValue("@QuestID", questID); + cmd.Parameters.AddWithValue("@WeaponTypeID", weaponType); + cmd.Parameters.AddWithValue("@ActualOverlayMode", actualOverlayMode); + cmd.Parameters.AddWithValue("@PartySize", partySize); - // Execute the stored procedure - cmd.ExecuteNonQuery(); - Logger.Debug("Updated PersonalBestAttempts table"); - } + // Execute the stored procedure + cmd.ExecuteNonQuery(); + Logger.Debug("Updated PersonalBestAttempts table"); } } @@ -1811,6 +1824,217 @@ FinalTimeValue ASC Logger.Debug("Inserted into QuestsToggleMode table"); + sql = @"INSERT INTO QuestsCourse ( + Rights, + RunID + ) VALUES ( + @Rights, + @RunID + )"; + + using (var cmd = new SQLiteCommand(sql, conn)) + { + var rights = model.Rights(); + + cmd.Parameters.AddWithValue("@Rights", rights); + cmd.Parameters.AddWithValue("@RunID", runID); + cmd.ExecuteNonQuery(); + } + + Logger.Debug("Inserted into QuestsCourse table"); + + sql = @"INSERT INTO QuestsOverlayHash ( + OverlayHash, + RunID + ) VALUES ( + @OverlayHash, + @RunID + )"; + + using (var cmd = new SQLiteCommand(sql, conn)) + { + var hash = GetOverlayHash(); + + cmd.Parameters.AddWithValue("@OverlayHash", hash); + cmd.Parameters.AddWithValue("@RunID", runID); + cmd.ExecuteNonQuery(); + } + + Logger.Debug("Inserted into QuestsOverlayHash table"); + + sql = @"INSERT INTO QuestsActiveFeature ( + ActiveFeature, + RunID + ) VALUES ( + @ActiveFeature, + @RunID + )"; + + using (var cmd = new SQLiteCommand(sql, conn)) + { + var activeFeature = dataLoader.Model.GetActiveFeature(); + + cmd.Parameters.AddWithValue("@ActiveFeature", activeFeature); + cmd.Parameters.AddWithValue("@RunID", runID); + cmd.ExecuteNonQuery(); + } + + Logger.Debug("Inserted into QuestsActiveFeature table"); + + sql = @"INSERT INTO QuestsGuildPoogie ( + GuildPoogie1Skill, + GuildPoogie2Skill, + GuildPoogie3Skill, + RunID + ) VALUES ( + @GuildPoogie1Skill, + @GuildPoogie2Skill, + @GuildPoogie3Skill, + @RunID + )"; + + using (var cmd = new SQLiteCommand(sql, conn)) + { + cmd.Parameters.AddWithValue("@GuildPoogie1Skill", model.GuildPoogie1Skill()); + cmd.Parameters.AddWithValue("@GuildPoogie2Skill", model.GuildPoogie2Skill()); + cmd.Parameters.AddWithValue("@GuildPoogie3Skill", model.GuildPoogie3Skill()); + cmd.Parameters.AddWithValue("@RunID", runID); + cmd.ExecuteNonQuery(); + } + + Logger.Debug("Inserted into QuestsGuildPoogie table"); + + sql = @"INSERT INTO QuestsHalk ( + HalkOn, + HalkPotEffectOn, + HalkFullness, + HalkLevel, + HalkIntimacy, + HalkHealth, + HalkAttack, + HalkDefense, + HalkIntellect, + HalkSkill1, + HalkSkill2, + HalkSkill3, + HalkElementNone, + HalkFire, + HalkThunder, + HalkWater, + HalkIce, + HalkDragon, + HalkSleep, + HalkParalysis, + HalkPoison, + RunID + ) VALUES ( + @HalkOn, + @HalkPotEffectOn, + @HalkFullness, + @HalkLevel, + @HalkIntimacy, + @HalkHealth, + @HalkAttack, + @HalkDefense, + @HalkIntellect, + @HalkSkill1, + @HalkSkill2, + @HalkSkill3, + @HalkElementNone, + @HalkFire, + @HalkThunder, + @HalkWater, + @HalkIce, + @HalkDragon, + @HalkSleep, + @HalkParalysis, + @HalkPoison, + @RunID + )"; + + using (var cmd = new SQLiteCommand(sql, conn)) + { + cmd.Parameters.AddWithValue("@HalkOn", model.HalkOn()); + cmd.Parameters.AddWithValue("@HalkPotEffectOn", model.HalkPotEffectOn()); + cmd.Parameters.AddWithValue("@HalkFullness", model.HalkFullness()); + cmd.Parameters.AddWithValue("@HalkLevel", model.HalkLevel()); + cmd.Parameters.AddWithValue("@HalkIntimacy", model.HalkIntimacy()); + cmd.Parameters.AddWithValue("@HalkHealth", model.HalkHealth()); + cmd.Parameters.AddWithValue("@HalkAttack", model.HalkAttack()); + cmd.Parameters.AddWithValue("@HalkDefense", model.HalkDefense()); + cmd.Parameters.AddWithValue("@HalkIntellect", model.HalkIntellect()); + cmd.Parameters.AddWithValue("@HalkSkill1", model.HalkSkill1()); + cmd.Parameters.AddWithValue("@HalkSkill2", model.HalkSkill2()); + cmd.Parameters.AddWithValue("@HalkSkill3", model.HalkSkill3()); + cmd.Parameters.AddWithValue("@HalkElementNone", model.HalkElementNone()); + cmd.Parameters.AddWithValue("@HalkFire", model.HalkFire()); + cmd.Parameters.AddWithValue("@HalkThunder", model.HalkThunder()); + cmd.Parameters.AddWithValue("@HalkWater", model.HalkWater()); + cmd.Parameters.AddWithValue("@HalkIce", model.HalkIce()); + cmd.Parameters.AddWithValue("@HalkDragon", model.HalkDragon()); + cmd.Parameters.AddWithValue("@HalkSleep", model.HalkSleep()); + cmd.Parameters.AddWithValue("@HalkParalysis", model.HalkParalysis()); + cmd.Parameters.AddWithValue("@HalkPoison", model.HalkPoison()); + cmd.Parameters.AddWithValue("@RunID", runID); + + cmd.ExecuteNonQuery(); + } + + Logger.Debug("Inserted into QuestsHalk table"); + + sql = @"INSERT INTO QuestsDiva ( + DivaSongBuffOn, + DivaPrayerGemRedSkill, + DivaPrayerGemRedLevel, + DivaPrayerGemYellowSkill, + DivaPrayerGemYellowLevel, + DivaPrayerGemGreenSkill, + DivaPrayerGemGreenLevel, + DivaPrayerGemBlueSkill, + DivaPrayerGemBlueLevel, + RunID + ) VALUES ( + @DivaSongBuffOn, + @DivaPrayerGemRedSkill, + @DivaPrayerGemRedLevel, + @DivaPrayerGemYellowSkill, + @DivaPrayerGemYellowLevel, + @DivaPrayerGemGreenSkill, + @DivaPrayerGemGreenLevel, + @DivaPrayerGemBlueSkill, + @DivaPrayerGemBlueLevel, + @RunID + )"; + + using (var cmd = new SQLiteCommand(sql, conn)) + { + // TODO test + cmd.Parameters.AddWithValue("@DivaSongBuffOn", model.DivaSongActive); + cmd.Parameters.AddWithValue("@DivaPrayerGemRedSkill", model.DivaPrayerGemRedSkill()); + cmd.Parameters.AddWithValue("@DivaPrayerGemRedLevel", model.DivaPrayerGemRedLevel()); + cmd.Parameters.AddWithValue("@DivaPrayerGemYellowSkill", model.DivaPrayerGemYellowSkill()); + cmd.Parameters.AddWithValue("@DivaPrayerGemYellowLevel", model.DivaPrayerGemYellowLevel()); + cmd.Parameters.AddWithValue("@DivaPrayerGemGreenSkill", model.DivaPrayerGemGreenSkill()); + cmd.Parameters.AddWithValue("@DivaPrayerGemGreenLevel", model.DivaPrayerGemGreenLevel()); + cmd.Parameters.AddWithValue("@DivaPrayerGemBlueSkill", model.DivaPrayerGemBlueSkill()); + cmd.Parameters.AddWithValue("@DivaPrayerGemBlueLevel", model.DivaPrayerGemBlueLevel()); + cmd.Parameters.AddWithValue("@RunID", runID); + cmd.ExecuteNonQuery(); + } + + Logger.Debug("Inserted into QuestsDiva table"); + + + + + + + + + // TODO more tables + + + var gearName = s.GearDescriptionExport; if (string.IsNullOrEmpty(gearName)) { @@ -1819,9 +2043,9 @@ FinalTimeValue ASC var weaponTypeID = model.WeaponType(); var weaponClassID = weaponTypeID; - var weaponSlot1 = model.GetDecoName(model.WeaponDeco1ID(), 1); // no sigils in database ig - var weaponSlot2 = model.GetDecoName(model.WeaponDeco2ID(), 2); - var weaponSlot3 = model.GetDecoName(model.WeaponDeco3ID(), 3); + var weaponSlot1 = model.GetDecoName(model.WeaponDeco1ID(), EquipmentSlot.One); // no sigils in database ig + var weaponSlot2 = model.GetDecoName(model.WeaponDeco2ID(), EquipmentSlot.Two); + var weaponSlot3 = model.GetDecoName(model.WeaponDeco3ID(), EquipmentSlot.Three); var headID = model.ArmorHeadID(); var headSlot1 = model.ArmorHeadDeco1ID(); var headSlot2 = model.ArmorHeadDeco2ID(); @@ -1846,7 +2070,7 @@ FinalTimeValue ASC var cuffSlot2 = model.Cuff2ID(); var styleID = model.WeaponStyle(); var weaponIconID = weaponTypeID; - var divaSkillID = model.DivaSkill(); + var divaSkillID = model.DivaSkillUsesLeft() > 0 ? model.DivaSkill() : 0; var guildFoodID = model.GuildFoodSkill(); var poogieItemID = model.PoogieItemUseID(); @@ -2251,6 +2475,10 @@ private void UpdateHashSets(SQLiteConnection conn) var lastGachaCard = this.GetLastGachaCard(conn); var lastPlayerInventory = this.GetLastPlayerInventory(conn); var lastQuestsToggleMode = this.GetLastQuestsToggleMode(conn); + var lastQuestsActiveFeature = this.GetLastQuestsActiveFeature(conn); + var lastQuestsDiva = this.GetLastQuestsDiva(conn); + var lastQuestsHalk = this.GetLastQuestsHalk(conn); + var lastQuestsGuildPoogie = this.GetLastQuestsGuildPoogie(conn); if (lastQuest.RunID != 0) { @@ -2386,6 +2614,42 @@ private void UpdateHashSets(SQLiteConnection conn) Logger.Warn(CultureInfo.InvariantCulture, "Last quests toggle mode already found in hash set"); } } + + if (lastQuestsHalk.QuestsHalkID != 0) + { + var questsHalkAdded = this.AllQuestsHalk.Add(lastQuestsHalk); + if (!questsHalkAdded) + { + Logger.Warn(CultureInfo.InvariantCulture, "Last quests halk already found in hash set"); + } + } + + if (lastQuestsDiva.QuestsDivaID != 0) + { + var questsDivaAdded = this.AllQuestsDiva.Add(lastQuestsDiva); + if (!questsDivaAdded) + { + Logger.Warn(CultureInfo.InvariantCulture, "Last quests diva already found in hash set"); + } + } + + if (lastQuestsActiveFeature.QuestsActiveFeatureID != 0) + { + var questsActiveFeatureAdded = this.AllQuestsActiveFeature.Add(lastQuestsActiveFeature); + if (!questsActiveFeatureAdded) + { + Logger.Warn(CultureInfo.InvariantCulture, "Last quests active feature already found in hash set"); + } + } + + if (lastQuestsGuildPoogie.QuestsGuildPoogieID != 0) + { + var questsGuildPoogieAdded = this.AllQuestsGuildPoogie.Add(lastQuestsGuildPoogie); + if (!questsGuildPoogieAdded) + { + Logger.Warn(CultureInfo.InvariantCulture, "Last quests guild poogie already found in hash set"); + } + } } private void CreateDatabaseTriggers(SQLiteConnection conn) @@ -2409,6 +2673,7 @@ private void CreateDatabaseTriggers(SQLiteConnection conn) // bingo // mezfesminigames // mezfes + // run buffs tables: prayer gem, guild poogie, halk, active feature, etc. using (var cmd = new SQLiteCommand(conn)) { cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_audit_deletion @@ -2651,8 +2916,8 @@ AFTER DELETE ON Overlay using (var cmd = new SQLiteCommand(conn)) { - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_bingo_updates - AFTER UPDATE ON Bingo + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_course_updates + AFTER UPDATE ON QuestsCourse BEGIN SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); END;"; @@ -2661,8 +2926,8 @@ AFTER UPDATE ON Bingo using (var cmd = new SQLiteCommand(conn)) { - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_bingo_deletion - AFTER DELETE ON Bingo + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_course_deletion + AFTER DELETE ON QuestsCourse BEGIN SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); END;"; @@ -2671,8 +2936,8 @@ AFTER DELETE ON Bingo using (var cmd = new SQLiteCommand(conn)) { - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_mezfesminigames_updates - AFTER UPDATE ON MezFesMinigames + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_toggle_mode_updates + AFTER UPDATE ON QuestsToggleMode BEGIN SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); END;"; @@ -2681,8 +2946,8 @@ AFTER UPDATE ON MezFesMinigames using (var cmd = new SQLiteCommand(conn)) { - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_mezfesminigames_deletion - AFTER DELETE ON MezFesMinigames + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_toggle_mode_deletion + AFTER DELETE ON QuestsToggleMode BEGIN SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); END;"; @@ -2691,8 +2956,8 @@ AFTER DELETE ON MezFesMinigames using (var cmd = new SQLiteCommand(conn)) { - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_mezfes_updates - AFTER UPDATE ON MezFes + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_overlay_hash_updates + AFTER UPDATE ON QuestsOverlayHash BEGIN SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); END;"; @@ -2701,8 +2966,8 @@ AFTER UPDATE ON MezFes using (var cmd = new SQLiteCommand(conn)) { - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_mezfes_deletion - AFTER DELETE ON MezFes + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_overlay_hash_deletion + AFTER DELETE ON QuestsOverlayHash BEGIN SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); END;"; @@ -2711,8 +2976,8 @@ AFTER DELETE ON MezFes using (var cmd = new SQLiteCommand(conn)) { - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_playerachievements_updates - AFTER UPDATE ON PlayerAchievements + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_diva_updates + AFTER UPDATE ON QuestsDiva BEGIN SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); END;"; @@ -2721,8 +2986,8 @@ AFTER UPDATE ON PlayerAchievements using (var cmd = new SQLiteCommand(conn)) { - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_playerachievements_deletion - AFTER DELETE ON PlayerAchievements + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_diva_deletion + AFTER DELETE ON QuestsDiva BEGIN SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); END;"; @@ -2731,8 +2996,8 @@ AFTER DELETE ON PlayerAchievements using (var cmd = new SQLiteCommand(conn)) { - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_zenithgauntlet_updates - AFTER UPDATE ON ZenithGauntlets + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_halk_updates + AFTER UPDATE ON QuestsHalk BEGIN SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); END;"; @@ -2741,8 +3006,8 @@ AFTER UPDATE ON ZenithGauntlets using (var cmd = new SQLiteCommand(conn)) { - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_zenithgauntlet_deletion - AFTER DELETE ON ZenithGauntlets + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_halk_deletion + AFTER DELETE ON QuestsHalk BEGIN SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); END;"; @@ -2751,60 +3016,202 @@ AFTER DELETE ON ZenithGauntlets using (var cmd = new SQLiteCommand(conn)) { - // TODO: add playergear, datfolder, zenithskills, automaticskills, activeskills, playerinventory, roaddureskills, etc - - // Create the trigger - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS trigger_insert_quests_insert_audit - AFTER INSERT ON Quests + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_active_feature_updates + AFTER UPDATE ON QuestsActiveFeature BEGIN - INSERT INTO Audit ( - CreatedAt, - CreatedBy, - ChangeType, - HashValue - ) VALUES ( - new.CreatedAt, - new.CreatedBy, - 'INSERT', - new.QuestHash - ); + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); END;"; - cmd.ExecuteNonQuery(); } using (var cmd = new SQLiteCommand(conn)) { - // Create the trigger - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS trigger_update_quests_insert_audit - AFTER UPDATE ON Quests + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_active_feature_deletion + AFTER DELETE ON QuestsActiveFeature BEGIN - INSERT INTO Audit ( - CreatedAt, - CreatedBy, - ChangeType, - HashValue - ) VALUES ( - new.CreatedAt, - new.CreatedBy, - 'UPDATE', - new.QuestHash - ); + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); END;"; + cmd.ExecuteNonQuery(); + } + using (var cmd = new SQLiteCommand(conn)) + { + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_guild_poogie_updates + AFTER UPDATE ON QuestsGuildPoogie + BEGIN + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); + END;"; cmd.ExecuteNonQuery(); } using (var cmd = new SQLiteCommand(conn)) { - // Create the trigger - // TODO this doesnt work - cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quest_deletion - BEFORE DELETE ON Quests - FOR EACH ROW + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quests_guild_poogie_deletion + AFTER DELETE ON QuestsGuildPoogie BEGIN - INSERT INTO Audit ( - CreatedAt, + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); + END;"; + cmd.ExecuteNonQuery(); + } + + // TODO run buffs + + using (var cmd = new SQLiteCommand(conn)) + { + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_bingo_updates + AFTER UPDATE ON Bingo + BEGIN + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); + END;"; + cmd.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_bingo_deletion + AFTER DELETE ON Bingo + BEGIN + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); + END;"; + cmd.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_mezfesminigames_updates + AFTER UPDATE ON MezFesMinigames + BEGIN + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); + END;"; + cmd.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_mezfesminigames_deletion + AFTER DELETE ON MezFesMinigames + BEGIN + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); + END;"; + cmd.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_mezfes_updates + AFTER UPDATE ON MezFes + BEGIN + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); + END;"; + cmd.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_mezfes_deletion + AFTER DELETE ON MezFes + BEGIN + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); + END;"; + cmd.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_playerachievements_updates + AFTER UPDATE ON PlayerAchievements + BEGIN + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); + END;"; + cmd.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_playerachievements_deletion + AFTER DELETE ON PlayerAchievements + BEGIN + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); + END;"; + cmd.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_zenithgauntlet_updates + AFTER UPDATE ON ZenithGauntlets + BEGIN + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); + END;"; + cmd.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_zenithgauntlet_deletion + AFTER DELETE ON ZenithGauntlets + BEGIN + SELECT RAISE(ROLLBACK, 'Updating rows is not allowed. Keep in mind that all attempted modifications are logged into the central database.'); + END;"; + cmd.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + // TODO: add playergear, datfolder, zenithskills, automaticskills, activeskills, playerinventory, roaddureskills, etc + + // Create the trigger + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS trigger_insert_quests_insert_audit + AFTER INSERT ON Quests + BEGIN + INSERT INTO Audit ( + CreatedAt, + CreatedBy, + ChangeType, + HashValue + ) VALUES ( + new.CreatedAt, + new.CreatedBy, + 'INSERT', + new.QuestHash + ); + END;"; + + cmd.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + // Create the trigger + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS trigger_update_quests_insert_audit + AFTER UPDATE ON Quests + BEGIN + INSERT INTO Audit ( + CreatedAt, + CreatedBy, + ChangeType, + HashValue + ) VALUES ( + new.CreatedAt, + new.CreatedBy, + 'UPDATE', + new.QuestHash + ); + END;"; + + cmd.ExecuteNonQuery(); + } + + using (var cmd = new SQLiteCommand(conn)) + { + // Create the trigger + // TODO this doesnt work + cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quest_deletion + BEFORE DELETE ON Quests + FOR EACH ROW + BEGIN + INSERT INTO Audit ( + CreatedAt, CreatedBy, ChangeType, HashValue @@ -2841,7 +3248,7 @@ BEFORE UPDATE ON Quests cmd.CommandText = @"CREATE TRIGGER IF NOT EXISTS prevent_quest_updates AFTER UPDATE ON Quests FOR EACH ROW - WHEN NEW.YoutubeID = OLD.YoutubeID + WHEN NEW.YouTubeID = OLD.YouTubeID BEGIN SELECT RAISE(ABORT, 'Cannot update quest fields'); END;"; @@ -2939,6 +3346,49 @@ private static void HandleError(SQLiteTransaction? transaction, Exception ex) LoggingService.WriteCrashLog(ex, $"SQLite error (version: {serverVersion})"); } + public string GetOverlayHash() + { + var overlayHash = string.Empty; + + // 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 exePath = string.Empty; + if (processes.Length > 0) + { + var module = processes[0].MainModule; + if (module == null) + { + return string.Empty; + } + else + { + exePath = module.FileName; + } + } + else + { + exePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exeName); + } + + if (exePath == null) + { + return string.Empty; + } + + // Calculate the SHA256 hash of the executable + using (var sha256 = SHA256.Create()) + { + using (var stream = File.OpenRead(exePath)) + { + var hash = sha256.ComputeHash(stream); + overlayHash = BitConverter.ToString(hash).Replace("-", string.Empty); + } + } + + return overlayHash; + } + /// /// Stores the overlay hash. /// @@ -4900,12 +5350,13 @@ FOREIGN KEY(RoadDureSkill16ID) REFERENCES AllRoadDureSkills(RoadDureSkillID) sql = @"CREATE TABLE IF NOT EXISTS QuestAttempts( QuestAttemptsID INTEGER PRIMARY KEY AUTOINCREMENT, - QuestID INTEGER NOT NULL, - WeaponTypeID INTEGER NOT NULL, - ActualOverlayMode TEXT NOT NULL, - Attempts INTEGER NOT NULL, + QuestID INTEGER NOT NULL CHECK (QuestID >= 0) DEFAULT 0, + WeaponTypeID INTEGER NOT NULL DEFAULT 0, + ActualOverlayMode TEXT NOT NULL DEFAULT 'Standard', + Attempts INTEGER NOT NULL DEFAULT 1, + PartySize INTEGER NOT NULL DEFAULT 1, FOREIGN KEY(WeaponTypeID) REFERENCES WeaponType(WeaponTypeID), - UNIQUE (QuestID, WeaponTypeID, ActualOverlayMode) + UNIQUE (QuestID, WeaponTypeID, ActualOverlayMode, PartySize) ) "; using (var cmd = new SQLiteCommand(sql, conn)) @@ -4915,9 +5366,12 @@ FOREIGN KEY(WeaponTypeID) REFERENCES WeaponType(WeaponTypeID), sql = @"CREATE TABLE IF NOT EXISTS PersonalBests( PersonalBestsID INTEGER PRIMARY KEY AUTOINCREMENT, - RunID INTEGER NOT NULL, - Attempts INTEGER NOT NULL, - FOREIGN KEY(RunID) REFERENCES Quests(RunID))"; + RunID INTEGER NOT NULL DEFAULT 0, + Attempts INTEGER NOT NULL DEFAULT 1, + PartySize INTEGER NOT NULL DEFAULT 1, + FOREIGN KEY(RunID) REFERENCES Quests(RunID) + ) + "; using (var cmd = new SQLiteCommand(sql, conn)) { cmd.ExecuteNonQuery(); @@ -4983,14 +5437,14 @@ FOREIGN KEY(MezFesMinigameID) REFERENCES MezFesMinigames(MezFesMinigameID) sql = @"CREATE TABLE IF NOT EXISTS PersonalBestAttempts( PersonalBestAttemptsID INTEGER PRIMARY KEY AUTOINCREMENT, - QuestID INTEGER NOT NULL, - WeaponTypeID INTEGER NOT NULL, - ActualOverlayMode TEXT NOT NULL, - Attempts INTEGER NOT NULL, + QuestID INTEGER NOT NULL CHECK (QuestID >= 0) DEFAULT 0, + WeaponTypeID INTEGER NOT NULL DEFAULT 0, + ActualOverlayMode TEXT NOT NULL DEFAULT 'Standard', + Attempts INTEGER NOT NULL DEFAULT 0, + PartySize INTEGER NOT NULL DEFAULT 1, FOREIGN KEY(WeaponTypeID) REFERENCES WeaponType(WeaponTypeID), - UNIQUE (QuestID, WeaponTypeID, ActualOverlayMode) - ) - "; + UNIQUE (QuestID, WeaponTypeID, ActualOverlayMode, PartySize) + )"; using (var cmd = new SQLiteCommand(sql, conn)) { cmd.ExecuteNonQuery(); @@ -5175,48 +5629,146 @@ FOREIGN KEY(RunID) REFERENCES Quests(RunID) cmd.ExecuteNonQuery(); } - // a mh game but like a MUD. hunt in-game to get many kinds of points for this game. hunt and tame monsters. challenge other CPU players/monsters. - sql = @"CREATE TABLE IF NOT EXISTS GachaMaterial( - GachaMaterialID INTEGER PRIMARY KEY, - GachaMaterialName TEXT NOT NULL - )"; + sql = @"CREATE TABLE IF NOT EXISTS QuestsCourse( + QuestsCourseID INTEGER PRIMARY KEY AUTOINCREMENT, + Rights INTEGER NOT NULL DEFAULT 0, + RunID INTEGER NOT NULL, + FOREIGN KEY(RunID) REFERENCES Quests(RunID) + )"; using (var cmd = new SQLiteCommand(sql, conn)) { cmd.ExecuteNonQuery(); } - sql = @"CREATE TABLE IF NOT EXISTS GachaPlayerCurrency ( - GachaPlayerID INTEGER PRIMARY KEY, - TrialGachaCoins INTEGER NOT NULL DEFAULT 0, - PremiumGachaCoins INTEGER NOT NULL DEFAULT 0, - PrismaticGachaCoins INTEGER NOT NULL DEFAULT 0, - FrontierPoints INTEGER NOT NULL DEFAULT 0, - LegendaryTickets INTEGER NOT NULL DEFAULT 0, - NetCafePoints INTEGER NOT NULL DEFAULT 0, - MonsterCoins INTEGER NOT NULL DEFAULT 0, - GZenny INTEGER NOT NULL DEFAULT 0, - Zenny INTEGER NOT NULL DEFAULT 0, - GCP INTEGER NOT NULL DEFAULT 0, - CP INTEGER NOT NULL DEFAULT 0, - Gg INTEGER NOT NULL DEFAULT 0, - g INTEGER NOT NULL DEFAULT 0, - GreatSlayingPoints INTEGER NOT NULL DEFAULT 0, - MezFesCoins INTEGER NOT NULL DEFAULT 0, - RdP INTEGER NOT NULL DEFAULT 0, -- Road - Gm INTEGER NOT NULL DEFAULT 0, - TowerMedals INTEGER NOT NULL DEFAULT 0, - TowerPoints INTEGER NOT NULL DEFAULT 0, - Souls INTEGER NOT NULL DEFAULT 0, - FestivalPoints INTEGER NOT NULL DEFAULT 0, - FestivalTickets INTEGER NOT NULL DEFAULT 0, - FestivalGems INTEGER NOT NULL DEFAULT 0, - FestivalMarks INTEGER NOT NULL DEFAULT 0, - GuildTickets INTEGER NOT NULL DEFAULT 0, - GuildMedals INTEGER NOT NULL DEFAULT 0, - HuntingMedals INTEGER NOT NULL DEFAULT 0, - InterceptionPoints INTEGER NOT NULL DEFAULT 0, - DivaNotes INTEGER NOT NULL DEFAULT 0, - PoogiePoints INTEGER NOT NULL DEFAULT 0, + sql = @"CREATE TABLE IF NOT EXISTS QuestsOverlayHash( + QuestsOverlayHashID INTEGER PRIMARY KEY AUTOINCREMENT, + OverlayHash TEXT NOT NULL DEFAULT '', + RunID INTEGER NOT NULL, + FOREIGN KEY(RunID) REFERENCES Quests(RunID) + )"; + using (var cmd = new SQLiteCommand(sql, conn)) + { + cmd.ExecuteNonQuery(); + } + + sql = @"CREATE TABLE IF NOT EXISTS QuestsActiveFeature( + QuestsActiveFeatureID INTEGER PRIMARY KEY AUTOINCREMENT, + ActiveFeature INTEGER NOT NULL DEFAULT 0, + RunID INTEGER NOT NULL, + FOREIGN KEY(RunID) REFERENCES Quests(RunID) + )"; + using (var cmd = new SQLiteCommand(sql, conn)) + { + cmd.ExecuteNonQuery(); + } + + sql = @"CREATE TABLE IF NOT EXISTS QuestsGuildPoogie( + QuestsGuildPoogieID INTEGER PRIMARY KEY AUTOINCREMENT, + GuildPoogie1Skill INTEGER NOT NULL DEFAULT 0, + GuildPoogie2Skill INTEGER NOT NULL DEFAULT 0, + GuildPoogie3Skill INTEGER NOT NULL DEFAULT 0, + RunID INTEGER NOT NULL, + FOREIGN KEY(RunID) REFERENCES Quests(RunID) + )"; + using (var cmd = new SQLiteCommand(sql, conn)) + { + cmd.ExecuteNonQuery(); + } + + sql = @"CREATE TABLE IF NOT EXISTS QuestsHalk( + QuestsHalkID INTEGER PRIMARY KEY AUTOINCREMENT, + HalkOn INTEGER NOT NULL DEFAULT 0, + HalkPotEffectOn INTEGER NOT NULL DEFAULT 0, + HalkFullness INTEGER NOT NULL DEFAULT 0, + HalkLevel INTEGER NOT NULL DEFAULT 0, + HalkIntimacy INTEGER NOT NULL DEFAULT 0, + HalkHealth INTEGER NOT NULL DEFAULT 0, + HalkAttack INTEGER NOT NULL DEFAULT 0, + HalkDefense INTEGER NOT NULL DEFAULT 0, + HalkIntellect INTEGER NOT NULL DEFAULT 0, + HalkSkill1 INTEGER NOT NULL DEFAULT 0, + HalkSkill2 INTEGER NOT NULL DEFAULT 0, + HalkSkill3 INTEGER NOT NULL DEFAULT 0, + HalkElementNone INTEGER NOT NULL DEFAULT 0, + HalkFire INTEGER NOT NULL DEFAULT 0, + HalkThunder INTEGER NOT NULL DEFAULT 0, + HalkWater INTEGER NOT NULL DEFAULT 0, + HalkIce INTEGER NOT NULL DEFAULT 0, + HalkDragon INTEGER NOT NULL DEFAULT 0, + HalkSleep INTEGER NOT NULL DEFAULT 0, + HalkParalysis INTEGER NOT NULL DEFAULT 0, + HalkPoison INTEGER NOT NULL DEFAULT 0, + RunID INTEGER NOT NULL, + FOREIGN KEY(RunID) REFERENCES Quests(RunID) + )"; + using (var cmd = new SQLiteCommand(sql, conn)) + { + cmd.ExecuteNonQuery(); + } + + sql = @"CREATE TABLE IF NOT EXISTS QuestsDiva( + QuestsDivaID INTEGER PRIMARY KEY AUTOINCREMENT, + DivaSongBuffOn INTEGER NOT NULL DEFAULT 0, + DivaPrayerGemRedSkill INTEGER NOT NULL DEFAULT 0, + DivaPrayerGemRedLevel INTEGER NOT NULL DEFAULT 0, + DivaPrayerGemYellowSkill INTEGER NOT NULL DEFAULT 0, + DivaPrayerGemYellowLevel INTEGER NOT NULL DEFAULT 0, + DivaPrayerGemGreenSkill INTEGER NOT NULL DEFAULT 0, + DivaPrayerGemGreenLevel INTEGER NOT NULL DEFAULT 0, + DivaPrayerGemBlueSkill INTEGER NOT NULL DEFAULT 0, + DivaPrayerGemBlueLevel INTEGER NOT NULL DEFAULT 0, + RunID INTEGER NOT NULL, + FOREIGN KEY(RunID) REFERENCES Quests(RunID) + )"; + using (var cmd = new SQLiteCommand(sql, conn)) + { + cmd.ExecuteNonQuery(); + } + + // TODO extra tables + + // a mh game but like a MUD. hunt in-game to get many kinds of points for this game. hunt and tame monsters. challenge other CPU players/monsters. + sql = @"CREATE TABLE IF NOT EXISTS GachaMaterial( + GachaMaterialID INTEGER PRIMARY KEY, + GachaMaterialName TEXT NOT NULL + )"; + using (var cmd = new SQLiteCommand(sql, conn)) + { + cmd.ExecuteNonQuery(); + } + + sql = @"CREATE TABLE IF NOT EXISTS GachaPlayerCurrency ( + GachaPlayerID INTEGER PRIMARY KEY, + TrialGachaCoins INTEGER NOT NULL DEFAULT 0, + PremiumGachaCoins INTEGER NOT NULL DEFAULT 0, + PrismaticGachaCoins INTEGER NOT NULL DEFAULT 0, + FrontierPoints INTEGER NOT NULL DEFAULT 0, + LegendaryTickets INTEGER NOT NULL DEFAULT 0, + NetCafePoints INTEGER NOT NULL DEFAULT 0, + MonsterCoins INTEGER NOT NULL DEFAULT 0, + GZenny INTEGER NOT NULL DEFAULT 0, + Zenny INTEGER NOT NULL DEFAULT 0, + GCP INTEGER NOT NULL DEFAULT 0, + CP INTEGER NOT NULL DEFAULT 0, + Gg INTEGER NOT NULL DEFAULT 0, + g INTEGER NOT NULL DEFAULT 0, + GreatSlayingPoints INTEGER NOT NULL DEFAULT 0, + MezFesCoins INTEGER NOT NULL DEFAULT 0, + RdP INTEGER NOT NULL DEFAULT 0, -- Road + Gm INTEGER NOT NULL DEFAULT 0, + TowerMedals INTEGER NOT NULL DEFAULT 0, + TowerPoints INTEGER NOT NULL DEFAULT 0, + Souls INTEGER NOT NULL DEFAULT 0, + FestivalPoints INTEGER NOT NULL DEFAULT 0, + FestivalTickets INTEGER NOT NULL DEFAULT 0, + FestivalGems INTEGER NOT NULL DEFAULT 0, + FestivalMarks INTEGER NOT NULL DEFAULT 0, + GuildTickets INTEGER NOT NULL DEFAULT 0, + GuildMedals INTEGER NOT NULL DEFAULT 0, + HuntingMedals INTEGER NOT NULL DEFAULT 0, + InterceptionPoints INTEGER NOT NULL DEFAULT 0, + DivaNotes INTEGER NOT NULL DEFAULT 0, + PoogiePoints INTEGER NOT NULL DEFAULT 0, PartnerPoints INTEGER NOT NULL DEFAULT 0, PartnyaaPoints INTEGER NOT NULL DEFAULT 0, GalleryPoints INTEGER NOT NULL DEFAULT 0, @@ -5642,7 +6194,7 @@ public string GetYoutubeLinkForRunID(long runID) { try { - using (var cmd = new SQLiteCommand("SELECT YoutubeID FROM Quests WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT YouTubeID FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); @@ -5650,7 +6202,7 @@ public string GetYoutubeLinkForRunID(long runID) { if (reader.Read()) { - youtubeLink = (string)reader["YoutubeID"]; + youtubeLink = (string)reader["YouTubeID"]; } else { @@ -5696,7 +6248,7 @@ public bool UpdateYoutubeLink(object sender, RoutedEventArgs e, long runID, stri var count = (long)cmd.ExecuteScalar(); if (count > 0) { - using (var cmd2 = new SQLiteCommand("UPDATE Quests SET YoutubeID = @youtubeLink WHERE RunID = @runID", conn)) + using (var cmd2 = new SQLiteCommand("UPDATE Quests SET YouTubeID = @youtubeLink WHERE RunID = @runID", conn)) { cmd2.Parameters.AddWithValue("@youtubeLink", youtubeLink); cmd2.Parameters.AddWithValue("@runID", runID); @@ -5786,7 +6338,7 @@ FinalTimeValue ASC return personalBest; } - public string GetPersonalBest(long questID, int weaponTypeID, string category, string timerMode, DataLoader dataLoader) + public string GetPersonalBest(long questID, int weaponTypeID, string category, string timerMode, DataLoader dataLoader, long partySize) { var personalBest = Messages.TimerNotLoaded; if (string.IsNullOrEmpty(this.dataSource)) @@ -5816,8 +6368,8 @@ Quests q WHERE QuestID = @questID AND pg.WeaponTypeID = @weaponTypeID - AND ActualOverlayMode = @category - AND PartySize = 1 + AND q.ActualOverlayMode = @category + AND q.PartySize = @partySize ORDER BY FinalTimeValue ASC LIMIT 1", conn)) @@ -5825,6 +6377,7 @@ FinalTimeValue ASC cmd.Parameters.AddWithValue("@questID", questID); cmd.Parameters.AddWithValue("@weaponTypeID", weaponTypeID); cmd.Parameters.AddWithValue("@category", category); + cmd.Parameters.AddWithValue("@partySize", partySize); var reader = cmd.ExecuteReader(); if (reader.Read()) @@ -6138,7 +6691,7 @@ ORDER BY return personalBests; } - public int UpsertQuestAttempts(long questID, int weaponTypeID, string category) + public int UpsertQuestAttempts(long questID, int weaponTypeID, string category, long partySize) { var attempts = 0; if (string.IsNullOrEmpty(this.dataSource)) @@ -6162,11 +6715,11 @@ public int UpsertQuestAttempts(long questID, int weaponTypeID, string category) { command.CommandText = @"INSERT INTO - QuestAttempts (QuestID, WeaponTypeID, ActualOverlayMode, Attempts) + QuestAttempts (QuestID, WeaponTypeID, ActualOverlayMode, Attempts, PartySize) VALUES - (@QuestID, @WeaponTypeID, @ActualOverlayMode, 1) + (@QuestID, @WeaponTypeID, @ActualOverlayMode, 1, @PartySize) ON CONFLICT - (QuestID, WeaponTypeID, ActualOverlayMode) + (QuestID, WeaponTypeID, ActualOverlayMode, PartySize) DO UPDATE SET Attempts = Attempts + 1 @@ -6176,6 +6729,7 @@ DO UPDATE command.Parameters.AddWithValue("@QuestID", questID); command.Parameters.AddWithValue("@WeaponTypeID", weaponTypeID); command.Parameters.AddWithValue("@ActualOverlayMode", category); + command.Parameters.AddWithValue("@PartySize", partySize); attempts = Convert.ToInt32(command.ExecuteScalar(), CultureInfo.InvariantCulture); } @@ -6206,7 +6760,7 @@ DO UPDATE return attempts; } - public int UpsertPersonalBestAttempts(long questID, int weaponTypeID, string category) + public int UpsertPersonalBestAttempts(long questID, int weaponTypeID, string category, long partySize) { var attempts = 0; if (string.IsNullOrEmpty(this.dataSource)) @@ -6230,11 +6784,11 @@ public int UpsertPersonalBestAttempts(long questID, int weaponTypeID, string cat { command.CommandText = @"INSERT INTO - PersonalBestAttempts (QuestID, WeaponTypeID, ActualOverlayMode, Attempts) + PersonalBestAttempts (QuestID, WeaponTypeID, ActualOverlayMode, Attempts, PartySize) VALUES - (@QuestID, @WeaponTypeID, @ActualOverlayMode, 1) + (@QuestID, @WeaponTypeID, @ActualOverlayMode, 1, @PartySize) ON CONFLICT - (QuestID, WeaponTypeID, ActualOverlayMode) + (QuestID, WeaponTypeID, ActualOverlayMode, PartySize) DO UPDATE SET Attempts = Attempts + 1 @@ -6244,6 +6798,7 @@ DO UPDATE command.Parameters.AddWithValue("@QuestID", questID); command.Parameters.AddWithValue("@WeaponTypeID", weaponTypeID); command.Parameters.AddWithValue("@ActualOverlayMode", category); + command.Parameters.AddWithValue("@PartySize", partySize); attempts = Convert.ToInt32(command.ExecuteScalar(), CultureInfo.InvariantCulture); } @@ -6696,177 +7251,92 @@ public Quest GetQuest(long runID) return quest; } - private Quest GetLastQuest(SQLiteConnection conn) + public QuestsDiva GetDiva(long runID) { - Quest quest = new (); - using (var transaction = conn.BeginTransaction()) + QuestsDiva questsDiva = new(); + if (string.IsNullOrEmpty(this.dataSource)) { - try - { - using (var cmd = new SQLiteCommand("SELECT * FROM Quests ORDER BY RunID DESC LIMIT 1", conn)) - { - using (var reader = cmd.ExecuteReader()) - { - if (reader.Read()) - { - quest = new Quest - { - QuestHash = reader["QuestHash"].ToString(), - CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"].ToString(), - RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - TimeLeft = long.Parse(reader["TimeLeft"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - FinalTimeValue = long.Parse(reader["FinalTimeValue"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - FinalTimeDisplay = reader["FinalTimeDisplay"].ToString(), - ObjectiveImage = reader["ObjectiveImage"].ToString(), - ObjectiveTypeID = long.Parse(reader["ObjectiveTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ObjectiveQuantity = long.Parse(reader["ObjectiveQuantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - StarGrade = long.Parse(reader["StarGrade"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RankName = reader["RankName"].ToString(), - ObjectiveName = reader["ObjectiveName"].ToString(), - Date = DateTime.Parse(reader["Date"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - YouTubeID = reader["YouTubeID"].ToString(), - AttackBuffDictionary = reader["AttackBuffDictionary"].ToString(), - HitCountDictionary = reader["HitCountDictionary"].ToString(), - HitsPerSecondDictionary = reader["HitsPerSecondDictionary"].ToString(), - DamageDealtDictionary = reader["DamageDealtDictionary"].ToString(), - DamagePerSecondDictionary = reader["DamagePerSecondDictionary"].ToString(), - AreaChangesDictionary = reader["AreaChangesDictionary"].ToString(), - CartsDictionary = reader["CartsDictionary"].ToString(), - Monster1HPDictionary = reader["Monster1HPDictionary"].ToString(), - Monster2HPDictionary = reader["Monster2HPDictionary"].ToString(), - Monster3HPDictionary = reader["Monster3HPDictionary"].ToString(), - Monster4HPDictionary = reader["Monster4HPDictionary"].ToString(), - HitsTakenBlockedDictionary = reader["HitsTakenBlockedDictionary"].ToString(), - HitsTakenBlockedPerSecondDictionary = reader["HitsTakenBlockedPerSecondDictionary"].ToString(), - PlayerHPDictionary = reader["PlayerHPDictionary"].ToString(), - PlayerStaminaDictionary = reader["PlayerStaminaDictionary"].ToString(), - KeyStrokesDictionary = reader["KeyStrokesDictionary"].ToString(), - MouseInputDictionary = reader["MouseInputDictionary"].ToString(), - GamepadInputDictionary = reader["GamepadInputDictionary"].ToString(), - ActionsPerMinuteDictionary = reader["ActionsPerMinuteDictionary"].ToString(), - OverlayModeDictionary = reader["OverlayModeDictionary"].ToString(), - ActualOverlayMode = reader["ActualOverlayMode"].ToString(), - PartySize = long.Parse(reader["PartySize"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - }; - } - } - } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); - } + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get diva. dataSource: {0}", this.dataSource); + return questsDiva; } - return quest; - } - - private MezFes GetLastMezFes(SQLiteConnection conn) - { - MezFes last = new (); - using (var transaction = conn.BeginTransaction()) + // Use a SQL query to retrieve the Quest for the specific RunID from the database + using (var conn = new SQLiteConnection(this.dataSource)) { - try + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - using (var cmd = new SQLiteCommand("SELECT * FROM MezFes ORDER BY MezFesID DESC LIMIT 1", conn)) + try { - using (var reader = cmd.ExecuteReader()) + using (var cmd = new SQLiteCommand("SELECT * FROM QuestsDiva WHERE RunID = @runID", conn)) { - if (reader.Read()) + cmd.Parameters.AddWithValue("@runID", runID); + + using (var reader = cmd.ExecuteReader()) { - last = new MezFes + if (reader.Read()) { - MezFesID = long.Parse(reader["MezFesID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"]?.ToString() ?? string.Empty, - MezFesMinigameID = long.Parse(reader["MezFesMinigameID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Score = long.Parse(reader["Score"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - }; + questsDiva = new QuestsDiva + { + QuestsDivaID = long.Parse(reader["QuestsDivaID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaSongBuffOn = long.Parse(reader["DivaSongBuffOn"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemRedSkill = long.Parse(reader["DivaPrayerGemRedSkill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemRedLevel = long.Parse(reader["DivaPrayerGemRedLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemYellowSkill = long.Parse(reader["DivaPrayerGemYellowSkill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemYellowLevel = long.Parse(reader["DivaPrayerGemYellowLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemGreenSkill = long.Parse(reader["DivaPrayerGemGreenSkill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemGreenLevel = long.Parse(reader["DivaPrayerGemGreenLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemBlueSkill = long.Parse(reader["DivaPrayerGemBlueSkill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemBlueLevel = long.Parse(reader["DivaPrayerGemBlueLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; + } } } - } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); - } - } - - return last; - } - private Bingo GetLastBingo(SQLiteConnection conn) - { - Bingo last = new (); - using (var transaction = conn.BeginTransaction()) - { - try - { - using (var cmd = new SQLiteCommand("SELECT * FROM Bingo ORDER BY BingoID DESC LIMIT 1", conn)) + transaction.Commit(); + } + catch (Exception ex) { - using (var reader = cmd.ExecuteReader()) - { - if (reader.Read()) - { - last = new Bingo - { - BingoID = long.Parse(reader["BingoID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"].ToString(), - Difficulty = (Difficulty)long.Parse(reader["Difficulty"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Score = long.Parse(reader["Score"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - MonsterList = JsonConvert.DeserializeObject>(reader["MonsterList"]?.ToString() ?? "{}") ?? new List { }, - WeaponType = reader["WeaponType"]?.ToString() ?? string.Empty, - Category = ChallengeService.ConvertToBingoGauntletCategory(reader.GetInt64(reader.GetOrdinal("Category"))), - TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? string.Empty, - }; - } - } + HandleError(transaction, ex); } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } } - return last; + return questsDiva; } - public long GetPlayerBingoPoints() + public QuestsActiveFeature GetActiveFeature(long runID) { - long points = 0; - + QuestsActiveFeature data = new(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player bingo points. dataSource: {0}", this.dataSource); - return points; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get active feature. dataSource: {0}", this.dataSource); + return data; } + // Use a SQL query to retrieve the Quest for the specific RunID from the database using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); - using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand("SELECT Points FROM PlayerBingoPoints WHERE PlayerBingoPointsID = 1", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM QuestsActiveFeature WHERE RunID = @runID", conn)) { + cmd.Parameters.AddWithValue("@runID", runID); + using (var reader = cmd.ExecuteReader()) { if (reader.Read()) { - points = long.Parse(reader["Points"]?.ToString() ?? "0", CultureInfo.InvariantCulture); + data = new QuestsActiveFeature + { + QuestsActiveFeatureID = long.Parse(reader["QuestsActiveFeatureID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveFeature = long.Parse(reader["ActiveFeature"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; } } } @@ -6880,17 +7350,19 @@ public long GetPlayerBingoPoints() } } - return points; + return data; } - public void SetPlayerBingoPoints(long points) + public QuestsCourse GetCourses(long runID) { + QuestsCourse data = new(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot set player bingo points. dataSource: {0}", this.dataSource); - return; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get courses. dataSource: {0}", this.dataSource); + return data; } + // Use a SQL query to retrieve the Quest for the specific RunID from the database using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); @@ -6898,26 +7370,22 @@ public void SetPlayerBingoPoints(long points) { try { - // Create a command that will be used to insert multiple rows in a batch - using (var cmd = new SQLiteCommand(conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM QuestsCourse WHERE RunID = @runID", conn)) { - // Set the command text to insert a single row - cmd.CommandText = @"INSERT OR REPLACE INTO PlayerBingoPoints ( - PlayerBingoPointsID, - Points - ) VALUES ( - @PlayerBingoPointsID, - @Points)"; - - // Add the parameter placeholders - cmd.Parameters.Add("@PlayerBingoPointsID", DbType.Int64); - cmd.Parameters.Add("@Points", DbType.Int64); - - // Set the parameter values - cmd.Parameters["@PlayerBingoPointsID"].Value = 1; - cmd.Parameters["@Points"].Value = points; + cmd.Parameters.AddWithValue("@runID", runID); - cmd.ExecuteNonQuery(); + using (var reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + data = new QuestsCourse + { + QuestsCourseID = long.Parse(reader["QuestsCourseID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Rights = long.Parse(reader["Rights"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; + } + } } transaction.Commit(); @@ -6929,212 +7397,272 @@ public void SetPlayerBingoPoints(long points) } } - Logger.Debug("Inserted into PlayerBingoPoints table value {0}", points); + return data; } - private ZenithGauntlet GetLastZenithGauntlet(SQLiteConnection conn) + public QuestsGuildPoogie GetGuildPoogie(long runID) { - ZenithGauntlet last = new (); - using (var transaction = conn.BeginTransaction()) + QuestsGuildPoogie data = new(); + if (string.IsNullOrEmpty(this.dataSource)) { - try + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get guild poogie. dataSource: {0}", this.dataSource); + return data; + } + + // Use a SQL query to retrieve the Quest for the specific RunID from the database + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - using (var cmd = new SQLiteCommand("SELECT * FROM ZenithGauntlets ORDER BY ZenithGauntletID DESC LIMIT 1", conn)) + try { - using (var reader = cmd.ExecuteReader()) + using (var cmd = new SQLiteCommand("SELECT * FROM QuestsGuildPoogie WHERE RunID = @runID", conn)) { - if (reader.Read()) + cmd.Parameters.AddWithValue("@runID", runID); + + using (var reader = cmd.ExecuteReader()) { - last = new ZenithGauntlet + if (reader.Read()) { - ZenithGauntletID = long.Parse(reader["ZenithGauntletID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponType = reader["WeaponType"]?.ToString() ?? "0", - Category = reader["Category"]?.ToString() ?? "0", - TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", - Run1ID = long.Parse(reader["Run1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run2ID = long.Parse(reader["Run2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run3ID = long.Parse(reader["Run3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run4ID = long.Parse(reader["Run4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run5ID = long.Parse(reader["Run5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run6ID = long.Parse(reader["Run6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run7ID = long.Parse(reader["Run7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run8ID = long.Parse(reader["Run8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run9ID = long.Parse(reader["Run9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run10ID = long.Parse(reader["Run10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run11ID = long.Parse(reader["Run11ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run12ID = long.Parse(reader["Run12ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run13ID = long.Parse(reader["Run13ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run14ID = long.Parse(reader["Run14ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run15ID = long.Parse(reader["Run15ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run16ID = long.Parse(reader["Run16ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run17ID = long.Parse(reader["Run17ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run18ID = long.Parse(reader["Run18ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run19ID = long.Parse(reader["Run19ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run20ID = long.Parse(reader["Run20ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run21ID = long.Parse(reader["Run21ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run22ID = long.Parse(reader["Run22ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run23ID = long.Parse(reader["Run23ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - }; + data = new QuestsGuildPoogie + { + QuestsGuildPoogieID = long.Parse(reader["QuestsGuildPoogieID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GuildPoogie1Skill = long.Parse(reader["GuildPoogie1Skill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GuildPoogie2Skill = long.Parse(reader["GuildPoogie2Skill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GuildPoogie3Skill = long.Parse(reader["GuildPoogie3Skill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; + } } } - } - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } } } - return last; + return data; } - private SolsticeGauntlet GetLastSolsticeGauntlet(SQLiteConnection conn) + public QuestsHalk GetHalk(long runID) { - SolsticeGauntlet last = new (); - using (var transaction = conn.BeginTransaction()) + QuestsHalk data = new(); + if (string.IsNullOrEmpty(this.dataSource)) { - try + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get halk. dataSource: {0}", this.dataSource); + return data; + } + + // Use a SQL query to retrieve the Quest for the specific RunID from the database + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - using (var cmd = new SQLiteCommand("SELECT * FROM SolsticeGauntlets ORDER BY SolsticeGauntletID DESC LIMIT 1", conn)) + try { - using (var reader = cmd.ExecuteReader()) + using (var cmd = new SQLiteCommand("SELECT * FROM QuestsHalk WHERE RunID = @runID", conn)) { - if (reader.Read()) + cmd.Parameters.AddWithValue("@runID", runID); + + using (var reader = cmd.ExecuteReader()) { - last = new SolsticeGauntlet + if (reader.Read()) { - SolsticeGauntletID = long.Parse(reader["SolsticeGauntletID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponType = reader["WeaponType"]?.ToString() ?? "0", - Category = reader["Category"]?.ToString() ?? "0", - TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", - Run1ID = long.Parse(reader["Run1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run2ID = long.Parse(reader["Run2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run3ID = long.Parse(reader["Run3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run4ID = long.Parse(reader["Run4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run5ID = long.Parse(reader["Run5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run6ID = long.Parse(reader["Run6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - }; + data = new QuestsHalk + { + QuestsHalkID = long.Parse(reader["QuestsHalkID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkOn = long.Parse(reader["HalkOn"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkPotEffectOn = long.Parse(reader["HalkPotEffectOn"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkFullness = long.Parse(reader["HalkFullness"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkLevel = long.Parse(reader["HalkLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkIntimacy = long.Parse(reader["HalkIntimacy"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkHealth = long.Parse(reader["HalkHealth"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkAttack = long.Parse(reader["HalkAttack"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkDefense = long.Parse(reader["HalkDefense"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkIntellect = long.Parse(reader["HalkIntellect"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkSkill1 = long.Parse(reader["HalkSkill1"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkSkill2 = long.Parse(reader["HalkSkill2"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkSkill3 = long.Parse(reader["HalkSkill3"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkElementNone = long.Parse(reader["HalkElementNone"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkFire = long.Parse(reader["HalkFire"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkThunder = long.Parse(reader["HalkThunder"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkWater = long.Parse(reader["HalkWater"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkIce = long.Parse(reader["HalkIce"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkDragon = long.Parse(reader["HalkDragon"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkSleep = long.Parse(reader["HalkSleep"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkParalysis = long.Parse(reader["HalkParalysis"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkPoison = long.Parse(reader["HalkPoison"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; + } } } - } - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } } } - return last; + return data; } - private MusouGauntlet GetLastMusouGauntlet(SQLiteConnection conn) + public QuestsToggleMode GetQuestToggleMode(long runID) { - MusouGauntlet last = new (); - using (var transaction = conn.BeginTransaction()) + QuestsToggleMode data = new(); + if (string.IsNullOrEmpty(this.dataSource)) { - try + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quest toggle mode. dataSource: {0}", this.dataSource); + return data; + } + + // Use a SQL query to retrieve the Quest for the specific RunID from the database + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - using (var cmd = new SQLiteCommand("SELECT * FROM MusouGauntlets ORDER BY MusouGauntletID DESC LIMIT 1", conn)) + try { - using (var reader = cmd.ExecuteReader()) + using (var cmd = new SQLiteCommand("SELECT * FROM QuestsToggleMode WHERE RunID = @runID", conn)) { - if (reader.Read()) + cmd.Parameters.AddWithValue("@runID", runID); + + using (var reader = cmd.ExecuteReader()) { - last = new MusouGauntlet + if (reader.Read()) { - MusouGauntletID = long.Parse(reader["MusouGauntletID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponType = reader["WeaponType"]?.ToString() ?? "0", - Category = reader["Category"]?.ToString() ?? "0", - TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", - Run1ID = long.Parse(reader["Run1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run2ID = long.Parse(reader["Run2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run3ID = long.Parse(reader["Run3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run4ID = long.Parse(reader["Run4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run5ID = long.Parse(reader["Run5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run6ID = long.Parse(reader["Run6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run7ID = long.Parse(reader["Run7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run8ID = long.Parse(reader["Run8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run9ID = long.Parse(reader["Run9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run10ID = long.Parse(reader["Run10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - }; + data = new QuestsToggleMode + { + QuestsToggleModeID = long.Parse(reader["QuestsToggleModeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestToggleMode = long.Parse(reader["QuestToggleMode"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; + } } } - } - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } } } - return last; + return data; } - private PersonalBestAttempts GetLastPersonalBestAttempt(SQLiteConnection conn) + public QuestsOverlayHash GetOverlayHash(long runID) { - PersonalBestAttempts last = new (); - using (var transaction = conn.BeginTransaction()) + QuestsOverlayHash data = new(); + if (string.IsNullOrEmpty(this.dataSource)) { - try + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get overlay hash. dataSource: {0}", this.dataSource); + return data; + } + + // Use a SQL query to retrieve the Quest for the specific RunID from the database + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - using (var cmd = new SQLiteCommand("SELECT * FROM PersonalBestAttempts ORDER BY PersonalBestAttemptsID DESC LIMIT 1", conn)) + try { - using (var reader = cmd.ExecuteReader()) + using (var cmd = new SQLiteCommand("SELECT * FROM QuestsOverlayHash WHERE RunID = @runID", conn)) { - if (reader.Read()) + cmd.Parameters.AddWithValue("@runID", runID); + + using (var reader = cmd.ExecuteReader()) { - last = new PersonalBestAttempts + if (reader.Read()) { - PersonalBestAttemptsID = long.Parse(reader["PersonalBestAttemptsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponTypeID = long.Parse(reader["WeaponTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActualOverlayMode = reader["ActualOverlayMode"]?.ToString() ?? "0", - Attempts = long.Parse(reader["Attempts"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - }; + data = new QuestsOverlayHash + { + QuestsOverlayHashID = long.Parse(reader["QuestsOverlayHashID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + OverlayHash = reader["OverlayHash"]?.ToString() ?? "0", + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; + } } } - } - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } } } - return last; + return data; } - private QuestAttempts GetLastQuestAttempt(SQLiteConnection conn) + private Quest GetLastQuest(SQLiteConnection conn) { - QuestAttempts last = new (); + Quest quest = new (); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand("SELECT * FROM QuestAttempts ORDER BY QuestAttemptsID DESC LIMIT 1", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM Quests ORDER BY RunID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { if (reader.Read()) { - last = new QuestAttempts + quest = new Quest { - QuestAttemptsID = long.Parse(reader["QuestAttemptsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestHash = reader["QuestHash"].ToString(), + CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"].ToString(), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponTypeID = long.Parse(reader["WeaponTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActualOverlayMode = reader["ActualOverlayMode"]?.ToString() ?? "0", - Attempts = long.Parse(reader["Attempts"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + TimeLeft = long.Parse(reader["TimeLeft"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + FinalTimeValue = long.Parse(reader["FinalTimeValue"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + FinalTimeDisplay = reader["FinalTimeDisplay"].ToString(), + ObjectiveImage = reader["ObjectiveImage"].ToString(), + ObjectiveTypeID = long.Parse(reader["ObjectiveTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ObjectiveQuantity = long.Parse(reader["ObjectiveQuantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + StarGrade = long.Parse(reader["StarGrade"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RankName = reader["RankName"].ToString(), + ObjectiveName = reader["ObjectiveName"].ToString(), + Date = DateTime.Parse(reader["Date"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + YouTubeID = reader["YouTubeID"].ToString(), + AttackBuffDictionary = reader["AttackBuffDictionary"].ToString(), + HitCountDictionary = reader["HitCountDictionary"].ToString(), + HitsPerSecondDictionary = reader["HitsPerSecondDictionary"].ToString(), + DamageDealtDictionary = reader["DamageDealtDictionary"].ToString(), + DamagePerSecondDictionary = reader["DamagePerSecondDictionary"].ToString(), + AreaChangesDictionary = reader["AreaChangesDictionary"].ToString(), + CartsDictionary = reader["CartsDictionary"].ToString(), + Monster1HPDictionary = reader["Monster1HPDictionary"].ToString(), + Monster2HPDictionary = reader["Monster2HPDictionary"].ToString(), + Monster3HPDictionary = reader["Monster3HPDictionary"].ToString(), + Monster4HPDictionary = reader["Monster4HPDictionary"].ToString(), + HitsTakenBlockedDictionary = reader["HitsTakenBlockedDictionary"].ToString(), + HitsTakenBlockedPerSecondDictionary = reader["HitsTakenBlockedPerSecondDictionary"].ToString(), + PlayerHPDictionary = reader["PlayerHPDictionary"].ToString(), + PlayerStaminaDictionary = reader["PlayerStaminaDictionary"].ToString(), + KeyStrokesDictionary = reader["KeyStrokesDictionary"].ToString(), + MouseInputDictionary = reader["MouseInputDictionary"].ToString(), + GamepadInputDictionary = reader["GamepadInputDictionary"].ToString(), + ActionsPerMinuteDictionary = reader["ActionsPerMinuteDictionary"].ToString(), + OverlayModeDictionary = reader["OverlayModeDictionary"].ToString(), + ActualOverlayMode = reader["ActualOverlayMode"].ToString(), + PartySize = long.Parse(reader["PartySize"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; } } @@ -7148,77 +7676,29 @@ private QuestAttempts GetLastQuestAttempt(SQLiteConnection conn) } } - return last; + return quest; } - private PlayerGear GetLastPlayerGear(SQLiteConnection conn) + private MezFes GetLastMezFes(SQLiteConnection conn) { - PlayerGear last = new (); + MezFes last = new (); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand("SELECT * FROM PlayerGear ORDER BY PlayerGearID DESC LIMIT 1", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM MezFes ORDER BY MezFesID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { if (reader.Read()) { - last = new PlayerGear + last = new MezFes { - PlayerGearHash = reader["PlayerGearHash"]?.ToString() ?? "0", + MezFesID = long.Parse(reader["MezFesID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), CreatedBy = reader["CreatedBy"]?.ToString() ?? string.Empty, - PlayerGearID = long.Parse(reader["PlayerGearID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - PlayerID = long.Parse(reader["PlayerID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - GearName = reader["GearName"]?.ToString() ?? "0", - StyleID = long.Parse(reader["StyleID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponIconID = long.Parse(reader["WeaponIconID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponClassID = long.Parse(reader["WeaponClassID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponTypeID = long.Parse(reader["WeaponTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - BlademasterWeaponID = reader["BlademasterWeaponID"] == DBNull.Value ? null : long.Parse(reader["BlademasterWeaponID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - GunnerWeaponID = reader["GunnerWeaponID"] == DBNull.Value ? null : long.Parse(reader["GunnerWeaponID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponSlot1 = reader["WeaponSlot1"]?.ToString() ?? "0", - WeaponSlot2 = reader["WeaponSlot2"]?.ToString() ?? "0", - WeaponSlot3 = reader["WeaponSlot3"]?.ToString() ?? "0", - HeadID = long.Parse(reader["HeadID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - HeadSlot1ID = long.Parse(reader["HeadSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - HeadSlot2ID = long.Parse(reader["HeadSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - HeadSlot3ID = long.Parse(reader["HeadSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ChestID = long.Parse(reader["ChestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ChestSlot1ID = long.Parse(reader["ChestSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ChestSlot2ID = long.Parse(reader["ChestSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ChestSlot3ID = long.Parse(reader["ChestSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ArmsID = long.Parse(reader["ArmsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ArmsSlot1ID = long.Parse(reader["ArmsSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ArmsSlot2ID = long.Parse(reader["ArmsSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ArmsSlot3ID = long.Parse(reader["ArmsSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WaistID = long.Parse(reader["WaistID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WaistSlot1ID = long.Parse(reader["WaistSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WaistSlot2ID = long.Parse(reader["WaistSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WaistSlot3ID = long.Parse(reader["WaistSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - LegsID = long.Parse(reader["LegsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - LegsSlot1ID = long.Parse(reader["LegsSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - LegsSlot2ID = long.Parse(reader["LegsSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - LegsSlot3ID = long.Parse(reader["LegsSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Cuff1ID = long.Parse(reader["Cuff1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Cuff2ID = long.Parse(reader["Cuff2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ZenithSkillsID = long.Parse(reader["ZenithSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - AutomaticSkillsID = long.Parse(reader["AutomaticSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkillsID = long.Parse(reader["ActiveSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - CaravanSkillsID = long.Parse(reader["CaravanSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - DivaSkillID = long.Parse(reader["DivaSkillID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - GuildFoodID = long.Parse(reader["GuildFoodID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - StyleRankSkillsID = long.Parse(reader["StyleRankSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - PlayerInventoryID = long.Parse(reader["PlayerInventoryID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - AmmoPouchID = long.Parse(reader["AmmoPouchID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - PartnyaBagID = long.Parse(reader["PartnyaBagID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - PoogieItemID = long.Parse(reader["PoogieItemID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RoadDureSkillsID = long.Parse(reader["RoadDureSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - PlayerInventoryDictionary = reader["PlayerInventoryDictionary"].ToString(), - PlayerAmmoPouchDictionary = reader["PlayerAmmoPouchDictionary"].ToString(), - PartnyaBagDictionary = reader["PartnyaBagDictionary"].ToString(), + MezFesMinigameID = long.Parse(reader["MezFesMinigameID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Score = long.Parse(reader["Score"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; } } @@ -7235,44 +7715,31 @@ private PlayerGear GetLastPlayerGear(SQLiteConnection conn) return last; } - private ActiveSkills GetLastActiveSkills(SQLiteConnection conn) + private Bingo GetLastBingo(SQLiteConnection conn) { - ActiveSkills last = new (); + Bingo last = new (); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand("SELECT * FROM ActiveSkills ORDER BY ActiveSkillsID DESC LIMIT 1", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM Bingo ORDER BY BingoID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { if (reader.Read()) { - last = new ActiveSkills + last = new Bingo { + BingoID = long.Parse(reader["BingoID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"]?.ToString() ?? string.Empty, - ActiveSkillsID = long.Parse(reader["ActiveSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill1ID = long.Parse(reader["ActiveSkill1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill2ID = long.Parse(reader["ActiveSkill2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill3ID = long.Parse(reader["ActiveSkill3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill4ID = long.Parse(reader["ActiveSkill4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill5ID = long.Parse(reader["ActiveSkill5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill6ID = long.Parse(reader["ActiveSkill6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill7ID = long.Parse(reader["ActiveSkill7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill8ID = long.Parse(reader["ActiveSkill8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill9ID = long.Parse(reader["ActiveSkill9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill10ID = long.Parse(reader["ActiveSkill10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill11ID = long.Parse(reader["ActiveSkill11ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill12ID = long.Parse(reader["ActiveSkill12ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill13ID = long.Parse(reader["ActiveSkill13ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill14ID = long.Parse(reader["ActiveSkill14ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill15ID = long.Parse(reader["ActiveSkill15ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill16ID = long.Parse(reader["ActiveSkill16ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill17ID = long.Parse(reader["ActiveSkill17ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill18ID = long.Parse(reader["ActiveSkill18ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkill19ID = long.Parse(reader["ActiveSkill19ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"].ToString(), + Difficulty = (Difficulty)long.Parse(reader["Difficulty"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Score = long.Parse(reader["Score"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + MonsterList = JsonConvert.DeserializeObject>(reader["MonsterList"]?.ToString() ?? "{}") ?? new List { }, + WeaponType = reader["WeaponType"]?.ToString() ?? string.Empty, + Category = ChallengeService.ConvertToBingoGauntletCategory(reader.GetInt64(reader.GetOrdinal("Category"))), + TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? string.Empty, }; } } @@ -7289,69 +7756,139 @@ private ActiveSkills GetLastActiveSkills(SQLiteConnection conn) return last; } - private ZenithSkills GetLastZenithSkills(SQLiteConnection conn) + public long GetPlayerBingoPoints() { - ZenithSkills last = new(); - using (var transaction = conn.BeginTransaction()) + long points = 0; + + if (string.IsNullOrEmpty(this.dataSource)) { - try + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player bingo points. dataSource: {0}", this.dataSource); + return points; + } + + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + + using (var transaction = conn.BeginTransaction()) { - using (var cmd = new SQLiteCommand("SELECT * FROM ZenithSkills ORDER BY ZenithSkillsID DESC LIMIT 1", conn)) + try { - using (var reader = cmd.ExecuteReader()) + using (var cmd = new SQLiteCommand("SELECT Points FROM PlayerBingoPoints WHERE PlayerBingoPointsID = 1", conn)) { - if (reader.Read()) + using (var reader = cmd.ExecuteReader()) { - last = new ZenithSkills + if (reader.Read()) { - CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"]?.ToString() ?? string.Empty, - ZenithSkillsID = long.Parse(reader["ZenithSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ZenithSkill1ID = long.Parse(reader["ZenithSkill1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ZenithSkill2ID = long.Parse(reader["ZenithSkill2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ZenithSkill3ID = long.Parse(reader["ZenithSkill3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ZenithSkill4ID = long.Parse(reader["ZenithSkill4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ZenithSkill5ID = long.Parse(reader["ZenithSkill5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ZenithSkill6ID = long.Parse(reader["ZenithSkill6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ZenithSkill7ID = long.Parse(reader["ZenithSkill7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - }; + points = long.Parse(reader["Points"]?.ToString() ?? "0", CultureInfo.InvariantCulture); + } } } - } - transaction.Commit(); + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } } - catch (Exception ex) + } + + return points; + } + + public void SetPlayerBingoPoints(long points) + { + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot set player bingo points. dataSource: {0}", this.dataSource); + return; + } + + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - HandleError(transaction, ex); + try + { + // Create a command that will be used to insert multiple rows in a batch + using (var cmd = new SQLiteCommand(conn)) + { + // Set the command text to insert a single row + cmd.CommandText = @"INSERT OR REPLACE INTO PlayerBingoPoints ( + PlayerBingoPointsID, + Points + ) VALUES ( + @PlayerBingoPointsID, + @Points)"; + + // Add the parameter placeholders + cmd.Parameters.Add("@PlayerBingoPointsID", DbType.Int64); + cmd.Parameters.Add("@Points", DbType.Int64); + + // Set the parameter values + cmd.Parameters["@PlayerBingoPointsID"].Value = 1; + cmd.Parameters["@Points"].Value = points; + + cmd.ExecuteNonQuery(); + } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } } } - return last; + Logger.Debug("Inserted into PlayerBingoPoints table value {0}", points); } - private StyleRankSkills GetLastStyleRankSkills(SQLiteConnection conn) + private ZenithGauntlet GetLastZenithGauntlet(SQLiteConnection conn) { - StyleRankSkills last = new (); + ZenithGauntlet last = new (); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand("SELECT * FROM StyleRankSkills ORDER BY StyleRankSkillsID DESC LIMIT 1", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM ZenithGauntlets ORDER BY ZenithGauntletID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { if (reader.Read()) { - last = new StyleRankSkills + last = new ZenithGauntlet { - CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"]?.ToString() ?? string.Empty, - StyleRankSkillsID = long.Parse(reader["StyleRankSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - StyleRankSkill1ID = long.Parse(reader["StyleRankSkill1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - StyleRankSkill2ID = long.Parse(reader["StyleRankSkill2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ZenithGauntletID = long.Parse(reader["ZenithGauntletID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponType = reader["WeaponType"]?.ToString() ?? "0", + Category = reader["Category"]?.ToString() ?? "0", + TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", + Run1ID = long.Parse(reader["Run1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run2ID = long.Parse(reader["Run2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run3ID = long.Parse(reader["Run3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run4ID = long.Parse(reader["Run4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run5ID = long.Parse(reader["Run5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run6ID = long.Parse(reader["Run6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run7ID = long.Parse(reader["Run7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run8ID = long.Parse(reader["Run8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run9ID = long.Parse(reader["Run9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run10ID = long.Parse(reader["Run10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run11ID = long.Parse(reader["Run11ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run12ID = long.Parse(reader["Run12ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run13ID = long.Parse(reader["Run13ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run14ID = long.Parse(reader["Run14ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run15ID = long.Parse(reader["Run15ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run16ID = long.Parse(reader["Run16ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run17ID = long.Parse(reader["Run17ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run18ID = long.Parse(reader["Run18ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run19ID = long.Parse(reader["Run19ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run20ID = long.Parse(reader["Run20ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run21ID = long.Parse(reader["Run21ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run22ID = long.Parse(reader["Run22ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run23ID = long.Parse(reader["Run23ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; } } @@ -7368,23 +7905,32 @@ private StyleRankSkills GetLastStyleRankSkills(SQLiteConnection conn) return last; } - private GachaCardInventory GetLastGachaCard(SQLiteConnection conn) + private SolsticeGauntlet GetLastSolsticeGauntlet(SQLiteConnection conn) { - GachaCardInventory last = new (); + SolsticeGauntlet last = new (); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand("SELECT * FROM GachaCardInventory ORDER BY GachaCardInventoryID DESC LIMIT 1", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM SolsticeGauntlets ORDER BY SolsticeGauntletID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { if (reader.Read()) { - last = new GachaCardInventory + last = new SolsticeGauntlet { - GachaCardInventoryID = long.Parse(reader["GachaCardInventoryID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - GachaCardID = long.Parse(reader["GachaCardID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + SolsticeGauntletID = long.Parse(reader["SolsticeGauntletID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponType = reader["WeaponType"]?.ToString() ?? "0", + Category = reader["Category"]?.ToString() ?? "0", + TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", + Run1ID = long.Parse(reader["Run1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run2ID = long.Parse(reader["Run2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run3ID = long.Parse(reader["Run3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run4ID = long.Parse(reader["Run4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run5ID = long.Parse(reader["Run5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run6ID = long.Parse(reader["Run6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; } } @@ -7401,65 +7947,36 @@ private GachaCardInventory GetLastGachaCard(SQLiteConnection conn) return last; } - private PlayerInventory GetLastPlayerInventory(SQLiteConnection conn) + private MusouGauntlet GetLastMusouGauntlet(SQLiteConnection conn) { - PlayerInventory last = new (); + MusouGauntlet last = new (); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand("SELECT * FROM PlayerInventory ORDER BY PlayerInventoryID DESC LIMIT 1", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM MusouGauntlets ORDER BY MusouGauntletID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { if (reader.Read()) { - last = new PlayerInventory + last = new MusouGauntlet { - CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"].ToString(), - PlayerInventoryID = long.Parse(reader["PlayerInventoryID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item1ID = long.Parse(reader["Item1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item2ID = long.Parse(reader["Item2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item3ID = long.Parse(reader["Item3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item4ID = long.Parse(reader["Item4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item5ID = long.Parse(reader["Item5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item6ID = long.Parse(reader["Item6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item7ID = long.Parse(reader["Item7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item8ID = long.Parse(reader["Item8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item9ID = long.Parse(reader["Item9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item10ID = long.Parse(reader["Item10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item11ID = long.Parse(reader["Item11ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item12ID = long.Parse(reader["Item12ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item13ID = long.Parse(reader["Item13ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item14ID = long.Parse(reader["Item14ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item15ID = long.Parse(reader["Item15ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item16ID = long.Parse(reader["Item16ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item17ID = long.Parse(reader["Item17ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item18ID = long.Parse(reader["Item18ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item19ID = long.Parse(reader["Item19ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item20ID = long.Parse(reader["Item20ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item1Quantity = long.Parse(reader["Item1Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item2Quantity = long.Parse(reader["Item2Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item3Quantity = long.Parse(reader["Item3Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item4Quantity = long.Parse(reader["Item4Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item5Quantity = long.Parse(reader["Item5Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item6Quantity = long.Parse(reader["Item6Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item7Quantity = long.Parse(reader["Item7Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item8Quantity = long.Parse(reader["Item8Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item9Quantity = long.Parse(reader["Item9Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item10Quantity = long.Parse(reader["Item10Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item11Quantity = long.Parse(reader["Item11Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item12Quantity = long.Parse(reader["Item12Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item13Quantity = long.Parse(reader["Item13Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item14Quantity = long.Parse(reader["Item14Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item15Quantity = long.Parse(reader["Item15Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item16Quantity = long.Parse(reader["Item16Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item17Quantity = long.Parse(reader["Item17Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item18Quantity = long.Parse(reader["Item18Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item19Quantity = long.Parse(reader["Item19Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item20Quantity = long.Parse(reader["Item20Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + MusouGauntletID = long.Parse(reader["MusouGauntletID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponType = reader["WeaponType"]?.ToString() ?? "0", + Category = reader["Category"]?.ToString() ?? "0", + TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", + Run1ID = long.Parse(reader["Run1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run2ID = long.Parse(reader["Run2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run3ID = long.Parse(reader["Run3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run4ID = long.Parse(reader["Run4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run5ID = long.Parse(reader["Run5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run6ID = long.Parse(reader["Run6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run7ID = long.Parse(reader["Run7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run8ID = long.Parse(reader["Run8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run9ID = long.Parse(reader["Run9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run10ID = long.Parse(reader["Run10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; } } @@ -7476,24 +7993,26 @@ private PlayerInventory GetLastPlayerInventory(SQLiteConnection conn) return last; } - private QuestsToggleMode GetLastQuestsToggleMode(SQLiteConnection conn) + private PersonalBestAttempts GetLastPersonalBestAttempt(SQLiteConnection conn) { - QuestsToggleMode last = new(); + PersonalBestAttempts last = new (); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand("SELECT * FROM QuestsToggleMode ORDER BY QuestsToggleModeID DESC LIMIT 1", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM PersonalBestAttempts ORDER BY PersonalBestAttemptsID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { if (reader.Read()) { - last = new QuestsToggleMode + last = new PersonalBestAttempts { - QuestsToggleModeID = long.Parse(reader["QuestsToggleModeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - QuestToggleMode = long.Parse(reader["QuestToggleMode"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + PersonalBestAttemptsID = long.Parse(reader["PersonalBestAttemptsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponTypeID = long.Parse(reader["WeaponTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActualOverlayMode = reader["ActualOverlayMode"]?.ToString() ?? "0", + Attempts = long.Parse(reader["Attempts"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; } } @@ -7510,307 +8029,61 @@ private QuestsToggleMode GetLastQuestsToggleMode(SQLiteConnection conn) return last; } - /// - /// Loads the database data into hash sets. This is used to avoid querying the database when checking for achievement unlocks. - /// When inserting into these hashsets, the database must also be updated, and viceversa. - /// - /// The save icon grid. - public void LoadDatabaseDataIntoHashSets(Grid saveIconGrid, DataLoader dataLoader) - { - dataLoader.Model.ShowSaveIcon = true; - - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Fatal(CultureInfo.InvariantCulture, "Cannot run LoadDatabaseDataIntoHashSets. dataSource: {0}", this.dataSource); - LoggingService.WriteCrashLog(new Exception("Cannot run LoadDatabaseDataIntoHashSets.")); - } - - try - { - using (var conn = new SQLiteConnection(this.dataSource)) - { - conn.Open(); - this.AllQuests = this.GetAllQuests(conn); - this.AllMezFes = this.GetAllMezFes(conn); - this.AllBingo = this.GetAllBingo(conn); - this.AllZenithGauntlets = this.GetAllZenithGauntlets(conn); - this.AllSolsticeGauntlets = this.GetAllSolsticeGauntlets(conn); - this.AllMusouGauntlets = this.GetAllMusouGauntlets(conn); - this.AllPersonalBestAttempts = this.GetAllPersonalBestAttempts(conn); - this.AllPlayerGear = this.GetAllPlayerGear(conn); - this.AllActiveSkills = this.GetAllActiveSkills(conn); - this.AllZenithSkills = this.GetAllZenithSkills(conn); - this.AllStyleRankSkills = this.GetAllStyleRankSkills(conn); - this.AllQuestAttempts = this.GetAllQuestAttempts(conn); - this.AllGachaCards = this.GetAllGachaCards(conn); - this.AllPlayerInventories = this.GetAllPlayerInventories(conn); - this.AllQuestsToggleMode = this.GetAllQuestsToggleMode(conn); - } - } - catch (Exception ex) - { - LoggingService.WriteCrashLog(ex); - } - - dataLoader.Model.ShowSaveIcon = false; - } - - public RoadDureSkills GetRoadDureSkills(long runID) + private QuestAttempts GetLastQuestAttempt(SQLiteConnection conn) { - var roadDureSkills = new RoadDureSkills(); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get road/dure skills. dataSource: {0}", this.dataSource); - return roadDureSkills; - } - - // Use a SQL query to retrieve the RoadDureSkills data for the specific RunID from the database - using (var conn = new SQLiteConnection(this.dataSource)) + QuestAttempts last = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand("SELECT * FROM QuestAttempts ORDER BY QuestAttemptsID DESC LIMIT 1", conn)) { - using (var cmd = new SQLiteCommand("SELECT RoadDureSkillsID, RunID, RoadDureSkill1ID, RoadDureSkill1Level, RoadDureSkill2ID, RoadDureSkill2Level, RoadDureSkill3ID, RoadDureSkill3Level, RoadDureSkill4ID, RoadDureSkill4Level, RoadDureSkill5ID, RoadDureSkill5Level, RoadDureSkill6ID, RoadDureSkill6Level, RoadDureSkill7ID, RoadDureSkill7Level, RoadDureSkill8ID, RoadDureSkill8Level, RoadDureSkill9ID, RoadDureSkill9Level, RoadDureSkill10ID, RoadDureSkill10Level, RoadDureSkill11ID, RoadDureSkill11Level, RoadDureSkill12ID, RoadDureSkill12Level, RoadDureSkill13ID, RoadDureSkill13Level, RoadDureSkill14ID, RoadDureSkill14Level, RoadDureSkill15ID, RoadDureSkill15Level, RoadDureSkill16ID, RoadDureSkill16Level, CreatedAt, CreatedBy FROM RoadDureSkills WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - using (var reader = cmd.ExecuteReader()) + if (reader.Read()) { - if (reader.Read()) + last = new QuestAttempts { - roadDureSkills.RoadDureSkillsID = long.Parse(reader["RoadDureSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); - roadDureSkills.RunID = runID; - for (var i = 1; i <= 16; i++) - { - roadDureSkills.GetType().GetProperty("RoadDureSkill" + i + "ID").SetValue(roadDureSkills, long.Parse(reader["RoadDureSkill" + i + "ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture)); - roadDureSkills.GetType().GetProperty("RoadDureSkill" + i + "Level").SetValue(roadDureSkills, long.Parse(reader["RoadDureSkill" + i + "Level"]?.ToString() ?? "0", CultureInfo.InvariantCulture)); - } - - roadDureSkills.CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - roadDureSkills.CreatedBy = reader["CreatedBy"]?.ToString() ?? "0"; - } + QuestAttemptsID = long.Parse(reader["QuestAttemptsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponTypeID = long.Parse(reader["WeaponTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActualOverlayMode = reader["ActualOverlayMode"]?.ToString() ?? "0", + Attempts = long.Parse(reader["Attempts"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return roadDureSkills; + return last; } - private HashSet GetAllPlayerInventories(SQLiteConnection conn) + private PlayerGear GetLastPlayerGear(SQLiteConnection conn) { - HashSet hashSet = new (); + PlayerGear last = new (); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand(@"SELECT * FROM PlayerInventory", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM PlayerGear ORDER BY PlayerGearID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - PlayerInventory data = new () + last = new PlayerGear { - PlayerInventoryID = long.Parse(reader["PlayerInventoryID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + PlayerGearHash = reader["PlayerGearHash"]?.ToString() ?? "0", CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"].ToString(), - RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item1ID = long.Parse(reader["Item1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item1Quantity = long.Parse(reader["Item1Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item2ID = long.Parse(reader["Item2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item2Quantity = long.Parse(reader["Item2Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item3ID = long.Parse(reader["Item3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item3Quantity = long.Parse(reader["Item3Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item4ID = long.Parse(reader["Item4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item4Quantity = long.Parse(reader["Item4Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item5ID = long.Parse(reader["Item5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item5Quantity = long.Parse(reader["Item5Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item6ID = long.Parse(reader["Item6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item6Quantity = long.Parse(reader["Item6Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item7ID = long.Parse(reader["Item7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item7Quantity = long.Parse(reader["Item7Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item8ID = long.Parse(reader["Item8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item8Quantity = long.Parse(reader["Item8Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item9ID = long.Parse(reader["Item9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item9Quantity = long.Parse(reader["Item9Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item10ID = long.Parse(reader["Item10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item10Quantity = long.Parse(reader["Item10Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item11ID = long.Parse(reader["Item11ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item11Quantity = long.Parse(reader["Item11Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item12ID = long.Parse(reader["Item12ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item12Quantity = long.Parse(reader["Item12Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item13ID = long.Parse(reader["Item13ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item13Quantity = long.Parse(reader["Item13Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item14ID = long.Parse(reader["Item14ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item14Quantity = long.Parse(reader["Item14Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item15ID = long.Parse(reader["Item15ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item15Quantity = long.Parse(reader["Item15Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item16ID = long.Parse(reader["Item16ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item16Quantity = long.Parse(reader["Item16Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item17ID = long.Parse(reader["Item17ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item17Quantity = long.Parse(reader["Item17Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item18ID = long.Parse(reader["Item18ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item18Quantity = long.Parse(reader["Item18Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item19ID = long.Parse(reader["Item19ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item19Quantity = long.Parse(reader["Item19Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item20ID = long.Parse(reader["Item20ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Item20Quantity = long.Parse(reader["Item20Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - }; - - hashSet.Add(data); - } - } - } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); - } - } - - return hashSet; - } - - private HashSet GetAllQuestsToggleMode(SQLiteConnection conn) - { - HashSet hashSet = new(); - using (var transaction = conn.BeginTransaction()) - { - try - { - using (var cmd = new SQLiteCommand(@"SELECT * FROM QuestsToggleMode", conn)) - { - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - QuestsToggleMode data = new() - { - QuestsToggleModeID = long.Parse(reader["QuestsToggleModeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - QuestToggleMode = long.Parse(reader["QuestToggleMode"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - }; - - hashSet.Add(data); - } - } - } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); - } - } - - return hashSet; - } - - private HashSet GetAllGachaCards(SQLiteConnection conn) - { - HashSet hashSet = new (); - using (var transaction = conn.BeginTransaction()) - { - try - { - using (var cmd = new SQLiteCommand(@"SELECT * FROM GachaCardInventory", conn)) - { - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - GachaCardInventory data = new () - { - GachaCardInventoryID = long.Parse(reader["GachaCardInventoryID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - GachaCardID = long.Parse(reader["GachaCardID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - }; - - hashSet.Add(data); - } - } - } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); - } - } - - return hashSet; - } - - private HashSet GetAllQuestAttempts(SQLiteConnection conn) - { - HashSet hashSet = new (); - using (var transaction = conn.BeginTransaction()) - { - try - { - using (var cmd = new SQLiteCommand(@"SELECT * FROM QuestAttempts", conn)) - { - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - QuestAttempts data = new () - { - QuestAttemptsID = long.Parse(reader["QuestAttemptsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponTypeID = long.Parse(reader["WeaponTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActualOverlayMode = reader["ActualOverlayMode"]?.ToString() ?? "0", - Attempts = long.Parse(reader["Attempts"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - }; - - hashSet.Add(data); - } - } - } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); - } - } - - return hashSet; - } - - private HashSet GetAllPlayerGear(SQLiteConnection conn) - { - HashSet hashSet = new (); - using (var transaction = conn.BeginTransaction()) - { - try - { - using (var cmd = new SQLiteCommand(@"SELECT * FROM PlayerGear", conn)) - { - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - PlayerGear data = new () - { - PlayerGearHash = reader["PlayerGearHash"]?.ToString() ?? "0", - CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"]?.ToString() ?? "0", - PlayerGearID = long.Parse(reader["PlayerGearID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"]?.ToString() ?? string.Empty, + PlayerGearID = long.Parse(reader["PlayerGearID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), PlayerID = long.Parse(reader["PlayerID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), GearName = reader["GearName"]?.ToString() ?? "0", @@ -7823,32 +8096,26 @@ private HashSet GetAllPlayerGear(SQLiteConnection conn) WeaponSlot1 = reader["WeaponSlot1"]?.ToString() ?? "0", WeaponSlot2 = reader["WeaponSlot2"]?.ToString() ?? "0", WeaponSlot3 = reader["WeaponSlot3"]?.ToString() ?? "0", - HeadID = long.Parse(reader["HeadID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), HeadSlot1ID = long.Parse(reader["HeadSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), HeadSlot2ID = long.Parse(reader["HeadSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), HeadSlot3ID = long.Parse(reader["HeadSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ChestID = long.Parse(reader["ChestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ChestSlot1ID = long.Parse(reader["ChestSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ChestSlot2ID = long.Parse(reader["ChestSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ChestSlot3ID = long.Parse(reader["ChestSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ArmsID = long.Parse(reader["ArmsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ArmsSlot1ID = long.Parse(reader["ArmsSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ArmsSlot2ID = long.Parse(reader["ArmsSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ArmsSlot3ID = long.Parse(reader["ArmsSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WaistID = long.Parse(reader["WaistID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), WaistSlot1ID = long.Parse(reader["WaistSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), WaistSlot2ID = long.Parse(reader["WaistSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), WaistSlot3ID = long.Parse(reader["WaistSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - LegsID = long.Parse(reader["LegsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), LegsSlot1ID = long.Parse(reader["LegsSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), LegsSlot2ID = long.Parse(reader["LegsSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), LegsSlot3ID = long.Parse(reader["LegsSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Cuff1ID = long.Parse(reader["Cuff1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), Cuff2ID = long.Parse(reader["Cuff2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ZenithSkillsID = long.Parse(reader["ZenithSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), @@ -7867,8 +8134,6 @@ private HashSet GetAllPlayerGear(SQLiteConnection conn) PlayerAmmoPouchDictionary = reader["PlayerAmmoPouchDictionary"].ToString(), PartnyaBagDictionary = reader["PartnyaBagDictionary"].ToString(), }; - - hashSet.Add(data); } } } @@ -7881,27 +8146,27 @@ private HashSet GetAllPlayerGear(SQLiteConnection conn) } } - return hashSet; + return last; } - private HashSet GetAllActiveSkills(SQLiteConnection conn) + private ActiveSkills GetLastActiveSkills(SQLiteConnection conn) { - HashSet hashSet = new (); + ActiveSkills last = new (); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand(@"SELECT * FROM ActiveSkills", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM ActiveSkills ORDER BY ActiveSkillsID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - ActiveSkills data = new () + last = new ActiveSkills { - ActiveSkillsID = long.Parse(reader["ActiveSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), CreatedBy = reader["CreatedBy"]?.ToString() ?? string.Empty, + ActiveSkillsID = long.Parse(reader["ActiveSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ActiveSkill1ID = long.Parse(reader["ActiveSkill1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ActiveSkill2ID = long.Parse(reader["ActiveSkill2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), @@ -7923,8 +8188,6 @@ private HashSet GetAllActiveSkills(SQLiteConnection conn) ActiveSkill18ID = long.Parse(reader["ActiveSkill18ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ActiveSkill19ID = long.Parse(reader["ActiveSkill19ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; - - hashSet.Add(data); } } } @@ -7937,27 +8200,27 @@ private HashSet GetAllActiveSkills(SQLiteConnection conn) } } - return hashSet; + return last; } - private HashSet GetAllZenithSkills(SQLiteConnection conn) + private ZenithSkills GetLastZenithSkills(SQLiteConnection conn) { - HashSet hashSet = new(); + ZenithSkills last = new(); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand(@"SELECT * FROM ZenithSkills", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM ZenithSkills ORDER BY ZenithSkillsID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - ZenithSkills data = new() + last = new ZenithSkills { - ZenithSkillsID = long.Parse(reader["ZenithSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), CreatedBy = reader["CreatedBy"]?.ToString() ?? string.Empty, + ZenithSkillsID = long.Parse(reader["ZenithSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ZenithSkill1ID = long.Parse(reader["ZenithSkill1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ZenithSkill2ID = long.Parse(reader["ZenithSkill2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), @@ -7967,8 +8230,6 @@ private HashSet GetAllZenithSkills(SQLiteConnection conn) ZenithSkill6ID = long.Parse(reader["ZenithSkill6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), ZenithSkill7ID = long.Parse(reader["ZenithSkill7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; - - hashSet.Add(data); } } } @@ -7981,33 +8242,31 @@ private HashSet GetAllZenithSkills(SQLiteConnection conn) } } - return hashSet; + return last; } - private HashSet GetAllStyleRankSkills(SQLiteConnection conn) + private StyleRankSkills GetLastStyleRankSkills(SQLiteConnection conn) { - HashSet hashSet = new (); + StyleRankSkills last = new (); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand(@"SELECT * FROM StyleRankSkills", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM StyleRankSkills ORDER BY StyleRankSkillsID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - StyleRankSkills data = new () + last = new StyleRankSkills { - StyleRankSkillsID = long.Parse(reader["StyleRankSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"]?.ToString() ?? "0", + CreatedBy = reader["CreatedBy"]?.ToString() ?? string.Empty, + StyleRankSkillsID = long.Parse(reader["StyleRankSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), StyleRankSkill1ID = long.Parse(reader["StyleRankSkill1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), StyleRankSkill2ID = long.Parse(reader["StyleRankSkill2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; - - hashSet.Add(data); } } } @@ -8020,32 +8279,27 @@ private HashSet GetAllStyleRankSkills(SQLiteConnection conn) } } - return hashSet; + return last; } - private HashSet GetAllPersonalBestAttempts(SQLiteConnection conn) + private GachaCardInventory GetLastGachaCard(SQLiteConnection conn) { - HashSet hashSet = new (); + GachaCardInventory last = new (); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand(@"SELECT * FROM PersonalBestAttempts", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM GachaCardInventory ORDER BY GachaCardInventoryID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - PersonalBestAttempts data = new () + last = new GachaCardInventory { - PersonalBestAttemptsID = long.Parse(reader["PersonalBestAttemptsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponTypeID = long.Parse(reader["WeaponTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActualOverlayMode = reader["ActualOverlayMode"]?.ToString() ?? "0", - Attempts = long.Parse(reader["Attempts"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GachaCardInventoryID = long.Parse(reader["GachaCardInventoryID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GachaCardID = long.Parse(reader["GachaCardID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; - - hashSet.Add(data); } } } @@ -8058,42 +8312,69 @@ private HashSet GetAllPersonalBestAttempts(SQLiteConnectio } } - return hashSet; + return last; } - private HashSet GetAllMusouGauntlets(SQLiteConnection conn) + private PlayerInventory GetLastPlayerInventory(SQLiteConnection conn) { - HashSet hashSet = new (); + PlayerInventory last = new (); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand(@"SELECT * FROM MusouGauntlets", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM PlayerInventory ORDER BY PlayerInventoryID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - MusouGauntlet data = new () + last = new PlayerInventory { - MusouGauntletID = long.Parse(reader["MusouGauntletID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponType = reader["WeaponType"]?.ToString() ?? "0", - Category = reader["Category"]?.ToString() ?? "0", - TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", - Run1ID = long.Parse(reader["Run1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run2ID = long.Parse(reader["Run2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run3ID = long.Parse(reader["Run3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run4ID = long.Parse(reader["Run4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run5ID = long.Parse(reader["Run5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run6ID = long.Parse(reader["Run6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run7ID = long.Parse(reader["Run7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run8ID = long.Parse(reader["Run8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run9ID = long.Parse(reader["Run9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run10ID = long.Parse(reader["Run10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"].ToString(), + PlayerInventoryID = long.Parse(reader["PlayerInventoryID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item1ID = long.Parse(reader["Item1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item2ID = long.Parse(reader["Item2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item3ID = long.Parse(reader["Item3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item4ID = long.Parse(reader["Item4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item5ID = long.Parse(reader["Item5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item6ID = long.Parse(reader["Item6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item7ID = long.Parse(reader["Item7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item8ID = long.Parse(reader["Item8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item9ID = long.Parse(reader["Item9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item10ID = long.Parse(reader["Item10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item11ID = long.Parse(reader["Item11ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item12ID = long.Parse(reader["Item12ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item13ID = long.Parse(reader["Item13ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item14ID = long.Parse(reader["Item14ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item15ID = long.Parse(reader["Item15ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item16ID = long.Parse(reader["Item16ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item17ID = long.Parse(reader["Item17ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item18ID = long.Parse(reader["Item18ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item19ID = long.Parse(reader["Item19ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item20ID = long.Parse(reader["Item20ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item1Quantity = long.Parse(reader["Item1Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item2Quantity = long.Parse(reader["Item2Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item3Quantity = long.Parse(reader["Item3Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item4Quantity = long.Parse(reader["Item4Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item5Quantity = long.Parse(reader["Item5Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item6Quantity = long.Parse(reader["Item6Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item7Quantity = long.Parse(reader["Item7Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item8Quantity = long.Parse(reader["Item8Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item9Quantity = long.Parse(reader["Item9Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item10Quantity = long.Parse(reader["Item10Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item11Quantity = long.Parse(reader["Item11Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item12Quantity = long.Parse(reader["Item12Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item13Quantity = long.Parse(reader["Item13Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item14Quantity = long.Parse(reader["Item14Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item15Quantity = long.Parse(reader["Item15Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item16Quantity = long.Parse(reader["Item16Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item17Quantity = long.Parse(reader["Item17Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item18Quantity = long.Parse(reader["Item18Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item19Quantity = long.Parse(reader["Item19Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item20Quantity = long.Parse(reader["Item20Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; - - hashSet.Add(data); } } } @@ -8106,38 +8387,28 @@ private HashSet GetAllMusouGauntlets(SQLiteConnection conn) } } - return hashSet; + return last; } - private HashSet GetAllSolsticeGauntlets(SQLiteConnection conn) + private QuestsToggleMode GetLastQuestsToggleMode(SQLiteConnection conn) { - HashSet hashSet = new (); + QuestsToggleMode last = new(); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand(@"SELECT * FROM SolsticeGauntlets", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM QuestsToggleMode ORDER BY QuestsToggleModeID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - SolsticeGauntlet data = new () + last = new QuestsToggleMode { - SolsticeGauntletID = long.Parse(reader["SolsticeGauntletID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponType = reader["WeaponType"]?.ToString() ?? "0", - Category = reader["Category"]?.ToString() ?? "0", - TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", - Run1ID = long.Parse(reader["Run1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run2ID = long.Parse(reader["Run2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run3ID = long.Parse(reader["Run3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run4ID = long.Parse(reader["Run4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run5ID = long.Parse(reader["Run5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run6ID = long.Parse(reader["Run6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestsToggleModeID = long.Parse(reader["QuestsToggleModeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestToggleMode = long.Parse(reader["QuestToggleMode"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; - - hashSet.Add(data); } } } @@ -8150,55 +8421,28 @@ private HashSet GetAllSolsticeGauntlets(SQLiteConnection conn) } } - return hashSet; + return last; } - private HashSet GetAllZenithGauntlets(SQLiteConnection conn) + private QuestsActiveFeature GetLastQuestsActiveFeature(SQLiteConnection conn) { - HashSet hashSet = new (); + QuestsActiveFeature last = new(); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand(@"SELECT * FROM ZenithGauntlets", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM QuestsActiveFeature ORDER BY QuestsActiveFeatureID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - ZenithGauntlet data = new () + last = new QuestsActiveFeature { - ZenithGauntletID = long.Parse(reader["ZenithGauntletID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponType = reader["WeaponType"]?.ToString() ?? "0", - Category = reader["Category"]?.ToString() ?? "0", - TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", - Run1ID = long.Parse(reader["Run1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run2ID = long.Parse(reader["Run2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run3ID = long.Parse(reader["Run3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run4ID = long.Parse(reader["Run4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run5ID = long.Parse(reader["Run5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run6ID = long.Parse(reader["Run6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run7ID = long.Parse(reader["Run7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run8ID = long.Parse(reader["Run8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run9ID = long.Parse(reader["Run9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run10ID = long.Parse(reader["Run10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run11ID = long.Parse(reader["Run11ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run12ID = long.Parse(reader["Run12ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run13ID = long.Parse(reader["Run13ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run14ID = long.Parse(reader["Run14ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run15ID = long.Parse(reader["Run15ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run16ID = long.Parse(reader["Run16ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run17ID = long.Parse(reader["Run17ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run18ID = long.Parse(reader["Run18ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run19ID = long.Parse(reader["Run19ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run20ID = long.Parse(reader["Run20ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run21ID = long.Parse(reader["Run21ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run22ID = long.Parse(reader["Run22ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Run23ID = long.Parse(reader["Run23ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestsActiveFeatureID = long.Parse(reader["QuestsActiveFeatureID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveFeature = long.Parse(reader["ActiveFeature"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; - - hashSet.Add(data); } } } @@ -8211,37 +8455,30 @@ private HashSet GetAllZenithGauntlets(SQLiteConnection conn) } } - return hashSet; + return last; } - private HashSet GetAllBingo(SQLiteConnection conn) + private QuestsGuildPoogie GetLastQuestsGuildPoogie(SQLiteConnection conn) { - HashSet hashSet = new (); + QuestsGuildPoogie last = new(); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand("SELECT * FROM Bingo", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM QuestsGuildPoogie ORDER BY QuestsGuildPoogieID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - Bingo data = new () + last = new QuestsGuildPoogie { - BingoID = long.Parse(reader["BingoID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"]?.ToString() ?? "0", - Difficulty = (Difficulty)long.Parse(reader["Difficulty"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - MonsterList = JsonConvert.DeserializeObject>(reader["MonsterList"]?.ToString() ?? "{}") ?? new List { }, - WeaponType = reader["WeaponType"]?.ToString() ?? "0", - Category = ChallengeService.ConvertToBingoGauntletCategory(reader.GetInt64(reader.GetOrdinal("Category"))), - TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", - Score = long.Parse(reader["Score"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestsGuildPoogieID = long.Parse(reader["QuestsGuildPoogieID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GuildPoogie1Skill = long.Parse(reader["GuildPoogie1Skill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GuildPoogie2Skill = long.Parse(reader["GuildPoogie2Skill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GuildPoogie3Skill = long.Parse(reader["GuildPoogie3Skill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; - - hashSet.Add(data); } } } @@ -8254,32 +8491,36 @@ private HashSet GetAllBingo(SQLiteConnection conn) } } - return hashSet; + return last; } - private HashSet GetAllMezFes(SQLiteConnection conn) + private QuestsDiva GetLastQuestsDiva(SQLiteConnection conn) { - HashSet hashSet = new (); + QuestsDiva last = new(); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand("SELECT * FROM MezFes", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM QuestsDiva ORDER BY QuestsDivaID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - MezFes data = new () + last = new QuestsDiva { - MezFesID = long.Parse(reader["MezFesID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"].ToString(), - MezFesMinigameID = long.Parse(reader["MezFesMinigameID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Score = long.Parse(reader["Score"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestsDivaID = long.Parse(reader["QuestsDivaID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaSongBuffOn = long.Parse(reader["DivaSongBuffOn"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemRedSkill = long.Parse(reader["DivaPrayerGemRedSkill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemRedLevel = long.Parse(reader["DivaPrayerGemRedLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemYellowSkill = long.Parse(reader["DivaPrayerGemYellowSkill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemYellowLevel = long.Parse(reader["DivaPrayerGemYellowLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemGreenSkill = long.Parse(reader["DivaPrayerGemGreenSkill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemGreenLevel = long.Parse(reader["DivaPrayerGemGreenLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemBlueSkill = long.Parse(reader["DivaPrayerGemBlueSkill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemBlueLevel = long.Parse(reader["DivaPrayerGemBlueLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; - - hashSet.Add(data); } } } @@ -8292,68 +8533,48 @@ private HashSet GetAllMezFes(SQLiteConnection conn) } } - return hashSet; + return last; } - private HashSet GetAllQuests(SQLiteConnection conn) + private QuestsHalk GetLastQuestsHalk(SQLiteConnection conn) { - HashSet quests = new (); + QuestsHalk last = new(); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand("SELECT * FROM Quests", conn)) + using (var cmd = new SQLiteCommand("SELECT * FROM QuestsHalk ORDER BY QuestsHalkID DESC LIMIT 1", conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - var quest = new Quest + last = new QuestsHalk { - QuestHash = reader["QuestHash"].ToString(), - CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"].ToString(), + QuestsHalkID = long.Parse(reader["QuestsHalkID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkOn = long.Parse(reader["HalkOn"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkPotEffectOn = long.Parse(reader["HalkPotEffectOn"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkFullness = long.Parse(reader["HalkFullness"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkLevel = long.Parse(reader["HalkLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkIntimacy = long.Parse(reader["HalkIntimacy"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkHealth = long.Parse(reader["HalkHealth"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkAttack = long.Parse(reader["HalkAttack"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkDefense = long.Parse(reader["HalkDefense"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkIntellect = long.Parse(reader["HalkIntellect"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkSkill1 = long.Parse(reader["HalkSkill1"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkSkill2 = long.Parse(reader["HalkSkill2"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkSkill3 = long.Parse(reader["HalkSkill3"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkElementNone = long.Parse(reader["HalkElementNone"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkFire = long.Parse(reader["HalkFire"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkThunder = long.Parse(reader["HalkThunder"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkWater = long.Parse(reader["HalkWater"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkIce = long.Parse(reader["HalkIce"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkDragon = long.Parse(reader["HalkDragon"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkSleep = long.Parse(reader["HalkSleep"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkParalysis = long.Parse(reader["HalkParalysis"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkPoison = long.Parse(reader["HalkPoison"]?.ToString() ?? "0", CultureInfo.InvariantCulture), RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - TimeLeft = long.Parse(reader["TimeLeft"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - FinalTimeValue = long.Parse(reader["FinalTimeValue"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - FinalTimeDisplay = reader["FinalTimeDisplay"].ToString(), - ObjectiveImage = reader["ObjectiveImage"].ToString(), - ObjectiveTypeID = long.Parse(reader["ObjectiveTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ObjectiveQuantity = long.Parse(reader["ObjectiveQuantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - StarGrade = long.Parse(reader["StarGrade"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RankName = reader["RankName"].ToString(), - ObjectiveName = reader["ObjectiveName"].ToString(), - Date = DateTime.Parse(reader["Date"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - YouTubeID = reader["YouTubeID"].ToString(), - AttackBuffDictionary = reader["AttackBuffDictionary"].ToString(), - HitCountDictionary = reader["HitCountDictionary"].ToString(), - HitsPerSecondDictionary = reader["HitsPerSecondDictionary"].ToString(), - DamageDealtDictionary = reader["DamageDealtDictionary"].ToString(), - DamagePerSecondDictionary = reader["DamagePerSecondDictionary"].ToString(), - AreaChangesDictionary = reader["AreaChangesDictionary"].ToString(), - CartsDictionary = reader["CartsDictionary"].ToString(), - - Monster1HPDictionary = reader["Monster1HPDictionary"].ToString(), - Monster2HPDictionary = reader["Monster2HPDictionary"].ToString(), - Monster3HPDictionary = reader["Monster3HPDictionary"].ToString(), - Monster4HPDictionary = reader["Monster4HPDictionary"].ToString(), - - HitsTakenBlockedDictionary = reader["HitsTakenBlockedDictionary"].ToString(), - HitsTakenBlockedPerSecondDictionary = reader["HitsTakenBlockedPerSecondDictionary"].ToString(), - PlayerHPDictionary = reader["PlayerHPDictionary"].ToString(), - PlayerStaminaDictionary = reader["PlayerStaminaDictionary"].ToString(), - KeyStrokesDictionary = reader["KeyStrokesDictionary"].ToString(), - MouseInputDictionary = reader["MouseInputDictionary"].ToString(), - GamepadInputDictionary = reader["GamepadInputDictionary"].ToString(), - - ActionsPerMinuteDictionary = reader["ActionsPerMinuteDictionary"].ToString(), - OverlayModeDictionary = reader["OverlayModeDictionary"].ToString(), - ActualOverlayMode = reader["ActualOverlayMode"].ToString(), - PartySize = long.Parse(reader["PartySize"]?.ToString() ?? "0", CultureInfo.InvariantCulture), }; - - quests.Add(quest); } } } @@ -8366,66 +8587,71 @@ private HashSet GetAllQuests(SQLiteConnection conn) } } - return quests; + return last; } - public StyleRankSkills GetStyleRankSkills(long runID) + /// + /// Loads the database data into hash sets. This is used to avoid querying the database when checking for achievement unlocks. + /// When inserting into these hashsets, the database must also be updated, and viceversa. + /// + /// The save icon grid. + public void LoadDatabaseDataIntoHashSets(Grid saveIconGrid, DataLoader dataLoader) { - var styleRankSkills = new StyleRankSkills(); + dataLoader.Model.ShowSaveIcon = true; + if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get style rank skills. dataSource: {0}", this.dataSource); - return styleRankSkills; + Logger.Fatal(CultureInfo.InvariantCulture, "Cannot run LoadDatabaseDataIntoHashSets. dataSource: {0}", this.dataSource); + LoggingService.WriteCrashLog(new Exception("Cannot run LoadDatabaseDataIntoHashSets.")); } - // Use a SQL query to retrieve the StyleRankSkills data for the specific RunID from the database - using (var conn = new SQLiteConnection(this.dataSource)) + try { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + using (var conn = new SQLiteConnection(this.dataSource)) { - try - { - using (var cmd = new SQLiteCommand("SELECT StyleRankSkillsID, RunID, StyleRankSkill1ID, StyleRankSkill2ID, CreatedAt, CreatedBy FROM StyleRankSkills WHERE RunID = @runID", conn)) - { - cmd.Parameters.AddWithValue("@runID", runID); - - using (var reader = cmd.ExecuteReader()) - { - if (reader.Read()) - { - styleRankSkills.StyleRankSkillsID = long.Parse(reader["StyleRankSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); - styleRankSkills.RunID = runID; - styleRankSkills.StyleRankSkill1ID = long.Parse(reader["StyleRankSkill1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); - styleRankSkills.StyleRankSkill2ID = long.Parse(reader["StyleRankSkill2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); - styleRankSkills.CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - styleRankSkills.CreatedBy = reader["CreatedBy"]?.ToString() ?? "0"; - } - } - } + conn.Open(); + this.AllQuests = this.GetAllQuests(conn); + this.AllMezFes = this.GetAllMezFes(conn); + this.AllBingo = this.GetAllBingo(conn); + this.AllZenithGauntlets = this.GetAllZenithGauntlets(conn); + this.AllSolsticeGauntlets = this.GetAllSolsticeGauntlets(conn); + this.AllMusouGauntlets = this.GetAllMusouGauntlets(conn); + this.AllPersonalBestAttempts = this.GetAllPersonalBestAttempts(conn); + this.AllPlayerGear = this.GetAllPlayerGear(conn); + this.AllActiveSkills = this.GetAllActiveSkills(conn); + this.AllZenithSkills = this.GetAllZenithSkills(conn); + this.AllStyleRankSkills = this.GetAllStyleRankSkills(conn); + this.AllQuestAttempts = this.GetAllQuestAttempts(conn); + this.AllGachaCards = this.GetAllGachaCards(conn); + this.AllPlayerInventories = this.GetAllPlayerInventories(conn); + this.AllQuestsToggleMode = this.GetAllQuestsToggleMode(conn); + this.AllQuestsActiveFeature = this.GetAllQuestsActiveFeature(conn); + this.AllQuestsGuildPoogie = this.GetAllQuestsGuildPoogie(conn); + this.AllQuestsDiva = this.GetAllQuestsDiva(conn); + this.AllQuestsHalk = this.GetAllQuestsHalk(conn); - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); - } } } + catch (Exception ex) + { + LoggingService.WriteCrashLog(ex); + } - return styleRankSkills; + dataLoader.Model.ShowSaveIcon = false; } - public AutomaticSkills GetAutomaticSkills(long runID) + + + public RoadDureSkills GetRoadDureSkills(long runID) { - var automaticSkills = new AutomaticSkills(); + var roadDureSkills = new RoadDureSkills(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get automatic skills. dataSource: {0}", this.dataSource); - return automaticSkills; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get road/dure skills. dataSource: {0}", this.dataSource); + return roadDureSkills; } - // Use a SQL query to retrieve the AutomaticSkills data for the specific RunID from the database + // Use a SQL query to retrieve the RoadDureSkills data for the specific RunID from the database using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); @@ -8433,7 +8659,7 @@ public AutomaticSkills GetAutomaticSkills(long runID) { try { - using (var cmd = new SQLiteCommand("SELECT AutomaticSkillsID, RunID, AutomaticSkill1ID, AutomaticSkill2ID, AutomaticSkill3ID, AutomaticSkill4ID, AutomaticSkill5ID, AutomaticSkill6ID, CreatedAt, CreatedBy FROM AutomaticSkills WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT RoadDureSkillsID, RunID, RoadDureSkill1ID, RoadDureSkill1Level, RoadDureSkill2ID, RoadDureSkill2Level, RoadDureSkill3ID, RoadDureSkill3Level, RoadDureSkill4ID, RoadDureSkill4Level, RoadDureSkill5ID, RoadDureSkill5Level, RoadDureSkill6ID, RoadDureSkill6Level, RoadDureSkill7ID, RoadDureSkill7Level, RoadDureSkill8ID, RoadDureSkill8Level, RoadDureSkill9ID, RoadDureSkill9Level, RoadDureSkill10ID, RoadDureSkill10Level, RoadDureSkill11ID, RoadDureSkill11Level, RoadDureSkill12ID, RoadDureSkill12Level, RoadDureSkill13ID, RoadDureSkill13Level, RoadDureSkill14ID, RoadDureSkill14Level, RoadDureSkill15ID, RoadDureSkill15Level, RoadDureSkill16ID, RoadDureSkill16Level, CreatedAt, CreatedBy FROM RoadDureSkills WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); @@ -8441,15 +8667,16 @@ public AutomaticSkills GetAutomaticSkills(long runID) { if (reader.Read()) { - automaticSkills.AutomaticSkillsID = long.Parse(reader["AutomaticSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); - automaticSkills.RunID = runID; - for (var i = 1; i <= 6; i++) + roadDureSkills.RoadDureSkillsID = long.Parse(reader["RoadDureSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); + roadDureSkills.RunID = runID; + for (var i = 1; i <= 16; i++) { - automaticSkills.GetType().GetProperty("AutomaticSkill" + i + "ID").SetValue(automaticSkills, long.Parse(reader["AutomaticSkill" + i + "ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture)); + roadDureSkills.GetType().GetProperty("RoadDureSkill" + i + "ID").SetValue(roadDureSkills, long.Parse(reader["RoadDureSkill" + i + "ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture)); + roadDureSkills.GetType().GetProperty("RoadDureSkill" + i + "Level").SetValue(roadDureSkills, long.Parse(reader["RoadDureSkill" + i + "Level"]?.ToString() ?? "0", CultureInfo.InvariantCulture)); } - automaticSkills.CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - automaticSkills.CreatedBy = reader["CreatedBy"]?.ToString() ?? "0"; + roadDureSkills.CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + roadDureSkills.CreatedBy = reader["CreatedBy"]?.ToString() ?? "0"; } } } @@ -8463,854 +8690,961 @@ public AutomaticSkills GetAutomaticSkills(long runID) } } - return automaticSkills; + return roadDureSkills; } - public ZenithSkills GetZenithSkills(long runID) + private HashSet GetAllPlayerInventories(SQLiteConnection conn) { - var zenithSkills = new ZenithSkills(); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get zenith skills. dataSource: {0}", this.dataSource); - return zenithSkills; - } - - // Use a SQL query to retrieve the ZenithSkills data for the specific RunID from the database - using (var conn = new SQLiteConnection(this.dataSource)) + HashSet hashSet = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM PlayerInventory", conn)) { - using (var cmd = new SQLiteCommand("SELECT ZenithSkillsID, RunID, ZenithSkill1ID, ZenithSkill2ID, ZenithSkill3ID, ZenithSkill4ID, ZenithSkill5ID, ZenithSkill6ID, ZenithSkill7ID, CreatedAt, CreatedBy FROM ZenithSkills WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - using (var reader = cmd.ExecuteReader()) + while (reader.Read()) { - if (reader.Read()) + PlayerInventory data = new () { - zenithSkills.ZenithSkillsID = long.Parse(reader["ZenithSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); - zenithSkills.RunID = runID; - for (var i = 1; i <= 7; i++) - { - zenithSkills.GetType().GetProperty("ZenithSkill" + i + "ID").SetValue(zenithSkills, long.Parse(reader["ZenithSkill" + i + "ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture)); - } + PlayerInventoryID = long.Parse(reader["PlayerInventoryID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"].ToString(), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item1ID = long.Parse(reader["Item1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item1Quantity = long.Parse(reader["Item1Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item2ID = long.Parse(reader["Item2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item2Quantity = long.Parse(reader["Item2Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item3ID = long.Parse(reader["Item3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item3Quantity = long.Parse(reader["Item3Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item4ID = long.Parse(reader["Item4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item4Quantity = long.Parse(reader["Item4Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item5ID = long.Parse(reader["Item5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item5Quantity = long.Parse(reader["Item5Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item6ID = long.Parse(reader["Item6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item6Quantity = long.Parse(reader["Item6Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item7ID = long.Parse(reader["Item7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item7Quantity = long.Parse(reader["Item7Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item8ID = long.Parse(reader["Item8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item8Quantity = long.Parse(reader["Item8Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item9ID = long.Parse(reader["Item9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item9Quantity = long.Parse(reader["Item9Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item10ID = long.Parse(reader["Item10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item10Quantity = long.Parse(reader["Item10Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item11ID = long.Parse(reader["Item11ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item11Quantity = long.Parse(reader["Item11Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item12ID = long.Parse(reader["Item12ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item12Quantity = long.Parse(reader["Item12Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item13ID = long.Parse(reader["Item13ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item13Quantity = long.Parse(reader["Item13Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item14ID = long.Parse(reader["Item14ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item14Quantity = long.Parse(reader["Item14Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item15ID = long.Parse(reader["Item15ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item15Quantity = long.Parse(reader["Item15Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item16ID = long.Parse(reader["Item16ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item16Quantity = long.Parse(reader["Item16Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item17ID = long.Parse(reader["Item17ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item17Quantity = long.Parse(reader["Item17Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item18ID = long.Parse(reader["Item18ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item18Quantity = long.Parse(reader["Item18Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item19ID = long.Parse(reader["Item19ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item19Quantity = long.Parse(reader["Item19Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item20ID = long.Parse(reader["Item20ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Item20Quantity = long.Parse(reader["Item20Quantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - zenithSkills.CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - zenithSkills.CreatedBy = reader["CreatedBy"]?.ToString() ?? "0"; - } + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return zenithSkills; + return hashSet; } - public CaravanSkills GetCaravanSkills(long runID) + private HashSet GetAllQuestsToggleMode(SQLiteConnection conn) { - var caravanSkills = new CaravanSkills(); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get caravan skills. dataSource: {0}", this.dataSource); - return caravanSkills; - } - - // Use a SQL query to retrieve the CaravanSkills data for the specific RunID from the database - using (var conn = new SQLiteConnection(this.dataSource)) + HashSet hashSet = new(); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM QuestsToggleMode", conn)) { - using (var cmd = new SQLiteCommand("SELECT CaravanSkillsID, RunID, CaravanSkill1ID, CaravanSkill2ID, CaravanSkill3ID, CreatedAt, CreatedBy FROM CaravanSkills WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - using (var reader = cmd.ExecuteReader()) + while (reader.Read()) { - if (reader.Read()) + QuestsToggleMode data = new() { - caravanSkills.CaravanSkillsID = long.Parse(reader["CaravanSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); - caravanSkills.RunID = runID; - for (var i = 1; i <= 3; i++) - { - caravanSkills.GetType().GetProperty("CaravanSkill" + i + "ID").SetValue(caravanSkills, long.Parse(reader["CaravanSkill" + i + "ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture)); - } + QuestsToggleModeID = long.Parse(reader["QuestsToggleModeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestToggleMode = long.Parse(reader["QuestToggleMode"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - caravanSkills.CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - caravanSkills.CreatedBy = reader["CreatedBy"]?.ToString() ?? "0"; - } + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return caravanSkills; + return hashSet; } - public Dictionary GetAttackBuffDictionary(long runID) + private HashSet GetAllQuestsActiveFeature(SQLiteConnection conn) { - Dictionary attackBuffDictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get attack buff dictionary. dataSource: {0}", this.dataSource); - return attackBuffDictionary; - } + HashSet hashSet = new HashSet(); - using (var conn = new SQLiteConnection(this.dataSource)) + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM QuestsActiveFeature", conn)) { - using (var cmd = new SQLiteCommand("SELECT AttackBuffDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>((string)result); - if (obj == null) + QuestsActiveFeature data = new QuestsActiveFeature { - return attackBuffDictionary; - } + QuestsActiveFeatureID = long.Parse(reader["QuestsActiveFeatureID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveFeature = long.Parse(reader["ActiveFeature"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - attackBuffDictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return attackBuffDictionary; + return hashSet; } - public Dictionary GetHitCountDictionary(long runID) + private HashSet GetAllQuestsGuildPoogie(SQLiteConnection conn) { - Dictionary dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get hit count dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } + HashSet hashSet = new HashSet(); - using (var conn = new SQLiteConnection(this.dataSource)) + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM QuestsGuildPoogie", conn)) { - using (var cmd = new SQLiteCommand("SELECT HitCountDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>((string)result); - if (obj == null) + QuestsGuildPoogie data = new QuestsGuildPoogie { - return dictionary; - } + QuestsGuildPoogieID = long.Parse(reader["QuestsGuildPoogieID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GuildPoogie1Skill = long.Parse(reader["GuildPoogie1Skill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GuildPoogie2Skill = long.Parse(reader["GuildPoogie2Skill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GuildPoogie3Skill = long.Parse(reader["GuildPoogie3Skill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary GetHitsPerSecondDictionary(long runID) + private HashSet GetAllQuestsDiva(SQLiteConnection conn) { - Dictionary dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get hits per second dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } + HashSet hashSet = new HashSet(); - using (var conn = new SQLiteConnection(this.dataSource)) + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM QuestsDiva", conn)) { - using (var cmd = new SQLiteCommand("SELECT HitsPerSecondDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>((string)result); - if (obj == null) + QuestsDiva data = new QuestsDiva { - return dictionary; - } + QuestsDivaID = long.Parse(reader["QuestsDivaID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaSongBuffOn = long.Parse(reader["DivaSongBuffOn"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemRedSkill = long.Parse(reader["DivaPrayerGemRedSkill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemRedLevel = long.Parse(reader["DivaPrayerGemRedLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemYellowSkill = long.Parse(reader["DivaPrayerGemYellowSkill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemYellowLevel = long.Parse(reader["DivaPrayerGemYellowLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemGreenSkill = long.Parse(reader["DivaPrayerGemGreenSkill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemGreenLevel = long.Parse(reader["DivaPrayerGemGreenLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemBlueSkill = long.Parse(reader["DivaPrayerGemBlueSkill"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaPrayerGemBlueLevel = long.Parse(reader["DivaPrayerGemBlueLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary GetDamageDealtDictionary(long runID) + private HashSet GetAllQuestsHalk(SQLiteConnection conn) { - Dictionary dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get damage dealt dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } + HashSet hashSet = new HashSet(); - using (var conn = new SQLiteConnection(this.dataSource)) + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM QuestsHalk", conn)) { - using (var cmd = new SQLiteCommand("SELECT DamageDealtDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>((string)result); - if (obj == null) + QuestsHalk data = new QuestsHalk { - return dictionary; - } + QuestsHalkID = long.Parse(reader["QuestsHalkID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkOn = long.Parse(reader["HalkOn"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkPotEffectOn = long.Parse(reader["HalkPotEffectOn"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkFullness = long.Parse(reader["HalkFullness"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkLevel = long.Parse(reader["HalkLevel"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkIntimacy = long.Parse(reader["HalkIntimacy"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkHealth = long.Parse(reader["HalkHealth"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkAttack = long.Parse(reader["HalkAttack"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkDefense = long.Parse(reader["HalkDefense"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkIntellect = long.Parse(reader["HalkIntellect"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkSkill1 = long.Parse(reader["HalkSkill1"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkSkill2 = long.Parse(reader["HalkSkill2"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkSkill3 = long.Parse(reader["HalkSkill3"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkElementNone = long.Parse(reader["HalkElementNone"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkFire = long.Parse(reader["HalkFire"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkThunder = long.Parse(reader["HalkThunder"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkWater = long.Parse(reader["HalkWater"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkIce = long.Parse(reader["HalkIce"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkDragon = long.Parse(reader["HalkDragon"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkSleep = long.Parse(reader["HalkSleep"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkParalysis = long.Parse(reader["HalkParalysis"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HalkPoison = long.Parse(reader["HalkPoison"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary GetDamagePerSecondDictionary(long runID) - { - Dictionary dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get damage per second dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } - using (var conn = new SQLiteConnection(this.dataSource)) + private HashSet GetAllGachaCards(SQLiteConnection conn) + { + HashSet hashSet = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM GachaCardInventory", conn)) { - using (var cmd = new SQLiteCommand("SELECT DamagePerSecondDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>((string)result); - if (obj == null) + GachaCardInventory data = new () { - return dictionary; - } + GachaCardInventoryID = long.Parse(reader["GachaCardInventoryID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GachaCardID = long.Parse(reader["GachaCardID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary GetAreaChangesDictionary(long runID) + private HashSet GetAllQuestAttempts(SQLiteConnection conn) { - Dictionary dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get area changes dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } - - using (var conn = new SQLiteConnection(this.dataSource)) + HashSet hashSet = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM QuestAttempts", conn)) { - using (var cmd = new SQLiteCommand("SELECT AreaChangesDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>((string)result); - if (obj == null) + QuestAttempts data = new () { - return dictionary; - } + QuestAttemptsID = long.Parse(reader["QuestAttemptsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponTypeID = long.Parse(reader["WeaponTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActualOverlayMode = reader["ActualOverlayMode"]?.ToString() ?? "0", + Attempts = long.Parse(reader["Attempts"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary GetCartsDictionary(long runID) + private HashSet GetAllPlayerGear(SQLiteConnection conn) { - Dictionary dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get carts dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } - - using (var conn = new SQLiteConnection(this.dataSource)) + HashSet hashSet = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM PlayerGear", conn)) { - using (var cmd = new SQLiteCommand("SELECT CartsDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>((string)result); - if (obj == null) + PlayerGear data = new () { - return dictionary; - } + PlayerGearHash = reader["PlayerGearHash"]?.ToString() ?? "0", + CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"]?.ToString() ?? "0", + PlayerGearID = long.Parse(reader["PlayerGearID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + PlayerID = long.Parse(reader["PlayerID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GearName = reader["GearName"]?.ToString() ?? "0", + StyleID = long.Parse(reader["StyleID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponIconID = long.Parse(reader["WeaponIconID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponClassID = long.Parse(reader["WeaponClassID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponTypeID = long.Parse(reader["WeaponTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + BlademasterWeaponID = reader["BlademasterWeaponID"] == DBNull.Value ? null : long.Parse(reader["BlademasterWeaponID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GunnerWeaponID = reader["GunnerWeaponID"] == DBNull.Value ? null : long.Parse(reader["GunnerWeaponID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponSlot1 = reader["WeaponSlot1"]?.ToString() ?? "0", + WeaponSlot2 = reader["WeaponSlot2"]?.ToString() ?? "0", + WeaponSlot3 = reader["WeaponSlot3"]?.ToString() ?? "0", - dictionary = obj; + HeadID = long.Parse(reader["HeadID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HeadSlot1ID = long.Parse(reader["HeadSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HeadSlot2ID = long.Parse(reader["HeadSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HeadSlot3ID = long.Parse(reader["HeadSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + + ChestID = long.Parse(reader["ChestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ChestSlot1ID = long.Parse(reader["ChestSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ChestSlot2ID = long.Parse(reader["ChestSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ChestSlot3ID = long.Parse(reader["ChestSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + + ArmsID = long.Parse(reader["ArmsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ArmsSlot1ID = long.Parse(reader["ArmsSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ArmsSlot2ID = long.Parse(reader["ArmsSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ArmsSlot3ID = long.Parse(reader["ArmsSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + + WaistID = long.Parse(reader["WaistID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WaistSlot1ID = long.Parse(reader["WaistSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WaistSlot2ID = long.Parse(reader["WaistSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WaistSlot3ID = long.Parse(reader["WaistSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + + LegsID = long.Parse(reader["LegsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + LegsSlot1ID = long.Parse(reader["LegsSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + LegsSlot2ID = long.Parse(reader["LegsSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + LegsSlot3ID = long.Parse(reader["LegsSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + + Cuff1ID = long.Parse(reader["Cuff1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Cuff2ID = long.Parse(reader["Cuff2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ZenithSkillsID = long.Parse(reader["ZenithSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + AutomaticSkillsID = long.Parse(reader["AutomaticSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkillsID = long.Parse(reader["ActiveSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + CaravanSkillsID = long.Parse(reader["CaravanSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaSkillID = long.Parse(reader["DivaSkillID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GuildFoodID = long.Parse(reader["GuildFoodID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + StyleRankSkillsID = long.Parse(reader["StyleRankSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + PlayerInventoryID = long.Parse(reader["PlayerInventoryID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + AmmoPouchID = long.Parse(reader["AmmoPouchID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + PartnyaBagID = long.Parse(reader["PartnyaBagID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + PoogieItemID = long.Parse(reader["PoogieItemID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RoadDureSkillsID = long.Parse(reader["RoadDureSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + PlayerInventoryDictionary = reader["PlayerInventoryDictionary"].ToString(), + PlayerAmmoPouchDictionary = reader["PlayerAmmoPouchDictionary"].ToString(), + PartnyaBagDictionary = reader["PartnyaBagDictionary"].ToString(), + }; + + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary> GetMonster1HPDictionary(long runID) + private HashSet GetAllActiveSkills(SQLiteConnection conn) { - Dictionary> dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 HP dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } - - using (var conn = new SQLiteConnection(this.dataSource)) + HashSet hashSet = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM ActiveSkills", conn)) { - using (var cmd = new SQLiteCommand("SELECT Monster1HPDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>>((string)result); - if (obj == null) + ActiveSkills data = new () { - return dictionary; - } + ActiveSkillsID = long.Parse(reader["ActiveSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"]?.ToString() ?? string.Empty, + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill1ID = long.Parse(reader["ActiveSkill1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill2ID = long.Parse(reader["ActiveSkill2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill3ID = long.Parse(reader["ActiveSkill3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill4ID = long.Parse(reader["ActiveSkill4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill5ID = long.Parse(reader["ActiveSkill5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill6ID = long.Parse(reader["ActiveSkill6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill7ID = long.Parse(reader["ActiveSkill7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill8ID = long.Parse(reader["ActiveSkill8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill9ID = long.Parse(reader["ActiveSkill9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill10ID = long.Parse(reader["ActiveSkill10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill11ID = long.Parse(reader["ActiveSkill11ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill12ID = long.Parse(reader["ActiveSkill12ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill13ID = long.Parse(reader["ActiveSkill13ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill14ID = long.Parse(reader["ActiveSkill14ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill15ID = long.Parse(reader["ActiveSkill15ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill16ID = long.Parse(reader["ActiveSkill16ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill17ID = long.Parse(reader["ActiveSkill17ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill18ID = long.Parse(reader["ActiveSkill18ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkill19ID = long.Parse(reader["ActiveSkill19ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary> GetMonster2HPDictionary(long runID) + private HashSet GetAllZenithSkills(SQLiteConnection conn) { - Dictionary> dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 2 HP dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } - - using (var conn = new SQLiteConnection(this.dataSource)) + HashSet hashSet = new(); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM ZenithSkills", conn)) { - using (var cmd = new SQLiteCommand("SELECT Monster2HPDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>>((string)result); - if (obj == null) + ZenithSkills data = new() { - return dictionary; - } + ZenithSkillsID = long.Parse(reader["ZenithSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"]?.ToString() ?? string.Empty, + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ZenithSkill1ID = long.Parse(reader["ZenithSkill1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ZenithSkill2ID = long.Parse(reader["ZenithSkill2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ZenithSkill3ID = long.Parse(reader["ZenithSkill3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ZenithSkill4ID = long.Parse(reader["ZenithSkill4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ZenithSkill5ID = long.Parse(reader["ZenithSkill5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ZenithSkill6ID = long.Parse(reader["ZenithSkill6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ZenithSkill7ID = long.Parse(reader["ZenithSkill7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary> GetMonster3HPDictionary(long runID) + private HashSet GetAllStyleRankSkills(SQLiteConnection conn) { - Dictionary> dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 3 HP dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } - - using (var conn = new SQLiteConnection(this.dataSource)) + HashSet hashSet = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM StyleRankSkills", conn)) { - using (var cmd = new SQLiteCommand("SELECT Monster3HPDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>>((string)result); - if (obj == null) + StyleRankSkills data = new () { - return dictionary; - } + StyleRankSkillsID = long.Parse(reader["StyleRankSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"]?.ToString() ?? "0", + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + StyleRankSkill1ID = long.Parse(reader["StyleRankSkill1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + StyleRankSkill2ID = long.Parse(reader["StyleRankSkill2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary> GetMonster4HPDictionary(long runID) + private HashSet GetAllPersonalBestAttempts(SQLiteConnection conn) { - Dictionary> dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 4 HP dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } - - using (var conn = new SQLiteConnection(this.dataSource)) + HashSet hashSet = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM PersonalBestAttempts", conn)) { - using (var cmd = new SQLiteCommand("SELECT Monster4HPDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>>((string)result); - if (obj == null) + PersonalBestAttempts data = new () { - return dictionary; - } + PersonalBestAttemptsID = long.Parse(reader["PersonalBestAttemptsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponTypeID = long.Parse(reader["WeaponTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActualOverlayMode = reader["ActualOverlayMode"]?.ToString() ?? "0", + Attempts = long.Parse(reader["Attempts"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary> GetMonster1AttackMultiplierDictionary(long runID) + private HashSet GetAllMusouGauntlets(SQLiteConnection conn) { - Dictionary> dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 attack multiplier dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } - - using (var conn = new SQLiteConnection(this.dataSource)) + HashSet hashSet = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM MusouGauntlets", conn)) { - using (var cmd = new SQLiteCommand("SELECT Monster1AttackMultiplierDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>>((string)result); - if (obj == null) + MusouGauntlet data = new () { - return dictionary; - } + MusouGauntletID = long.Parse(reader["MusouGauntletID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponType = reader["WeaponType"]?.ToString() ?? "0", + Category = reader["Category"]?.ToString() ?? "0", + TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", + Run1ID = long.Parse(reader["Run1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run2ID = long.Parse(reader["Run2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run3ID = long.Parse(reader["Run3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run4ID = long.Parse(reader["Run4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run5ID = long.Parse(reader["Run5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run6ID = long.Parse(reader["Run6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run7ID = long.Parse(reader["Run7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run8ID = long.Parse(reader["Run8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run9ID = long.Parse(reader["Run9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run10ID = long.Parse(reader["Run10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary> GetMonster1DefenseRateDictionary(long runID) + private HashSet GetAllSolsticeGauntlets(SQLiteConnection conn) { - Dictionary> dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 defense rate dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } - - using (var conn = new SQLiteConnection(this.dataSource)) + HashSet hashSet = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM SolsticeGauntlets", conn)) { - using (var cmd = new SQLiteCommand("SELECT Monster1DefenseRateDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>>((string)result); - if (obj == null) + SolsticeGauntlet data = new () { - return dictionary; - } + SolsticeGauntletID = long.Parse(reader["SolsticeGauntletID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponType = reader["WeaponType"]?.ToString() ?? "0", + Category = reader["Category"]?.ToString() ?? "0", + TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", + Run1ID = long.Parse(reader["Run1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run2ID = long.Parse(reader["Run2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run3ID = long.Parse(reader["Run3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run4ID = long.Parse(reader["Run4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run5ID = long.Parse(reader["Run5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run6ID = long.Parse(reader["Run6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary> GetMonster1PoisonThresholdDictionary(long runID) + private HashSet GetAllZenithGauntlets(SQLiteConnection conn) { - var dictionary = new Dictionary>(); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 poison threshold dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } - - using (var conn = new SQLiteConnection(this.dataSource)) + HashSet hashSet = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand(@"SELECT * FROM ZenithGauntlets", conn)) { - using (var cmd = new SQLiteCommand("SELECT Monster1PoisonThresholdDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>>((string)result); - if (obj == null) + ZenithGauntlet data = new () { - return dictionary; - } + ZenithGauntletID = long.Parse(reader["ZenithGauntletID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponType = reader["WeaponType"]?.ToString() ?? "0", + Category = reader["Category"]?.ToString() ?? "0", + TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", + Run1ID = long.Parse(reader["Run1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run2ID = long.Parse(reader["Run2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run3ID = long.Parse(reader["Run3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run4ID = long.Parse(reader["Run4ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run5ID = long.Parse(reader["Run5ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run6ID = long.Parse(reader["Run6ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run7ID = long.Parse(reader["Run7ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run8ID = long.Parse(reader["Run8ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run9ID = long.Parse(reader["Run9ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run10ID = long.Parse(reader["Run10ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run11ID = long.Parse(reader["Run11ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run12ID = long.Parse(reader["Run12ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run13ID = long.Parse(reader["Run13ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run14ID = long.Parse(reader["Run14ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run15ID = long.Parse(reader["Run15ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run16ID = long.Parse(reader["Run16ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run17ID = long.Parse(reader["Run17ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run18ID = long.Parse(reader["Run18ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run19ID = long.Parse(reader["Run19ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run20ID = long.Parse(reader["Run20ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run21ID = long.Parse(reader["Run21ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run22ID = long.Parse(reader["Run22ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Run23ID = long.Parse(reader["Run23ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } - } - } - return dictionary; + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + + return hashSet; } - public Dictionary> GetMonster1SleepThresholdDictionary(long runID) + private HashSet GetAllBingo(SQLiteConnection conn) { - Dictionary> dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 sleep threshold dictionary. dataSource: {0}", this.dataSource); - return dictionary; - } - - using (var conn = new SQLiteConnection(this.dataSource)) + HashSet hashSet = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand("SELECT * FROM Bingo", conn)) { - using (var cmd = new SQLiteCommand("SELECT Monster1SleepThresholdDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>>((string)result); - if (obj == null) + Bingo data = new () { - return dictionary; - } + BingoID = long.Parse(reader["BingoID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"]?.ToString() ?? "0", + Difficulty = (Difficulty)long.Parse(reader["Difficulty"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + MonsterList = JsonConvert.DeserializeObject>(reader["MonsterList"]?.ToString() ?? "{}") ?? new List { }, + WeaponType = reader["WeaponType"]?.ToString() ?? "0", + Category = ChallengeService.ConvertToBingoGauntletCategory(reader.GetInt64(reader.GetOrdinal("Category"))), + TotalFramesElapsed = long.Parse(reader["TotalFramesElapsed"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + TotalTimeElapsed = reader["TotalTimeElapsed"]?.ToString() ?? "0", + Score = long.Parse(reader["Score"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - dictionary = obj; + hashSet.Add(data); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return hashSet; } - public Dictionary> GetMonster1ParalysisThresholdDictionary(long runID) + private HashSet GetAllMezFes(SQLiteConnection conn) { - Dictionary> dictionary = new (); - if (string.IsNullOrEmpty(this.dataSource)) + HashSet hashSet = new (); + using (var transaction = conn.BeginTransaction()) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 paralysis threshold dictionary. dataSource: {0}", this.dataSource); - return dictionary; + try + { + using (var cmd = new SQLiteCommand("SELECT * FROM MezFes", conn)) + { + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + MezFes data = new () + { + MezFesID = long.Parse(reader["MezFesID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"].ToString(), + MezFesMinigameID = long.Parse(reader["MezFesMinigameID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Score = long.Parse(reader["Score"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; + + hashSet.Add(data); + } + } + } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } } - using (var conn = new SQLiteConnection(this.dataSource)) + return hashSet; + } + + private HashSet GetAllQuests(SQLiteConnection conn) + { + HashSet quests = new (); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand("SELECT * FROM Quests", conn)) { - using (var cmd = new SQLiteCommand("SELECT Monster1ParalysisThresholdDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var reader = cmd.ExecuteReader()) { - cmd.Parameters.AddWithValue("@runID", runID); - - var result = cmd.ExecuteScalar(); - - if (result != null) + while (reader.Read()) { - var obj = JsonConvert.DeserializeObject>>((string)result); - if (obj == null) + var quest = new Quest { - return dictionary; - } + QuestHash = reader["QuestHash"].ToString(), + CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"].ToString(), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + TimeLeft = long.Parse(reader["TimeLeft"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + FinalTimeValue = long.Parse(reader["FinalTimeValue"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + FinalTimeDisplay = reader["FinalTimeDisplay"].ToString(), + ObjectiveImage = reader["ObjectiveImage"].ToString(), + ObjectiveTypeID = long.Parse(reader["ObjectiveTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ObjectiveQuantity = long.Parse(reader["ObjectiveQuantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + StarGrade = long.Parse(reader["StarGrade"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RankName = reader["RankName"].ToString(), + ObjectiveName = reader["ObjectiveName"].ToString(), + Date = DateTime.Parse(reader["Date"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + YouTubeID = reader["YouTubeID"].ToString(), + AttackBuffDictionary = reader["AttackBuffDictionary"].ToString(), + HitCountDictionary = reader["HitCountDictionary"].ToString(), + HitsPerSecondDictionary = reader["HitsPerSecondDictionary"].ToString(), + DamageDealtDictionary = reader["DamageDealtDictionary"].ToString(), + DamagePerSecondDictionary = reader["DamagePerSecondDictionary"].ToString(), + AreaChangesDictionary = reader["AreaChangesDictionary"].ToString(), + CartsDictionary = reader["CartsDictionary"].ToString(), - dictionary = obj; + Monster1HPDictionary = reader["Monster1HPDictionary"].ToString(), + Monster2HPDictionary = reader["Monster2HPDictionary"].ToString(), + Monster3HPDictionary = reader["Monster3HPDictionary"].ToString(), + Monster4HPDictionary = reader["Monster4HPDictionary"].ToString(), + + HitsTakenBlockedDictionary = reader["HitsTakenBlockedDictionary"].ToString(), + HitsTakenBlockedPerSecondDictionary = reader["HitsTakenBlockedPerSecondDictionary"].ToString(), + PlayerHPDictionary = reader["PlayerHPDictionary"].ToString(), + PlayerStaminaDictionary = reader["PlayerStaminaDictionary"].ToString(), + KeyStrokesDictionary = reader["KeyStrokesDictionary"].ToString(), + MouseInputDictionary = reader["MouseInputDictionary"].ToString(), + GamepadInputDictionary = reader["GamepadInputDictionary"].ToString(), + + ActionsPerMinuteDictionary = reader["ActionsPerMinuteDictionary"].ToString(), + OverlayModeDictionary = reader["OverlayModeDictionary"].ToString(), + ActualOverlayMode = reader["ActualOverlayMode"].ToString(), + PartySize = long.Parse(reader["PartySize"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; + + quests.Add(quest); } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } - return dictionary; + return quests; } - public Dictionary> GetMonster1BlastThresholdDictionary(long runID) + public StyleRankSkills GetStyleRankSkills(long runID) { - Dictionary> dictionary = new (); + var styleRankSkills = new StyleRankSkills(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 blast threshold dictionary. dataSource: {0}", this.dataSource); - return dictionary; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get style rank skills. dataSource: {0}", this.dataSource); + return styleRankSkills; } + // Use a SQL query to retrieve the StyleRankSkills data for the specific RunID from the database using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); @@ -9318,21 +9652,21 @@ public Dictionary> GetMonster1BlastThresholdDictionary { try { - using (var cmd = new SQLiteCommand("SELECT Monster1BlastThresholdDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT StyleRankSkillsID, RunID, StyleRankSkill1ID, StyleRankSkill2ID, CreatedAt, CreatedBy FROM StyleRankSkills WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); - var result = cmd.ExecuteScalar(); - - if (result != null) + using (var reader = cmd.ExecuteReader()) { - var obj = JsonConvert.DeserializeObject>>((string)result); - if (obj == null) + if (reader.Read()) { - return dictionary; + styleRankSkills.StyleRankSkillsID = long.Parse(reader["StyleRankSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); + styleRankSkills.RunID = runID; + styleRankSkills.StyleRankSkill1ID = long.Parse(reader["StyleRankSkill1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); + styleRankSkills.StyleRankSkill2ID = long.Parse(reader["StyleRankSkill2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); + styleRankSkills.CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + styleRankSkills.CreatedBy = reader["CreatedBy"]?.ToString() ?? "0"; } - - dictionary = obj; } } @@ -9345,18 +9679,19 @@ public Dictionary> GetMonster1BlastThresholdDictionary } } - return dictionary; + return styleRankSkills; } - public Dictionary> GetMonster1StunThresholdDictionary(long runID) + public AutomaticSkills GetAutomaticSkills(long runID) { - Dictionary> dictionary = new (); + var automaticSkills = new AutomaticSkills(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 stun threshold dictionary. dataSource: {0}", this.dataSource); - return dictionary; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get automatic skills. dataSource: {0}", this.dataSource); + return automaticSkills; } + // Use a SQL query to retrieve the AutomaticSkills data for the specific RunID from the database using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); @@ -9364,21 +9699,24 @@ public Dictionary> GetMonster1StunThresholdDictionary( { try { - using (var cmd = new SQLiteCommand("SELECT Monster1StunThresholdDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT AutomaticSkillsID, RunID, AutomaticSkill1ID, AutomaticSkill2ID, AutomaticSkill3ID, AutomaticSkill4ID, AutomaticSkill5ID, AutomaticSkill6ID, CreatedAt, CreatedBy FROM AutomaticSkills WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); - var result = cmd.ExecuteScalar(); - - if (result != null) + using (var reader = cmd.ExecuteReader()) { - var obj = JsonConvert.DeserializeObject>>((string)result); - if (obj == null) + if (reader.Read()) { - return dictionary; - } + automaticSkills.AutomaticSkillsID = long.Parse(reader["AutomaticSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); + automaticSkills.RunID = runID; + for (var i = 1; i <= 6; i++) + { + automaticSkills.GetType().GetProperty("AutomaticSkill" + i + "ID").SetValue(automaticSkills, long.Parse(reader["AutomaticSkill" + i + "ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture)); + } - dictionary = obj; + automaticSkills.CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + automaticSkills.CreatedBy = reader["CreatedBy"]?.ToString() ?? "0"; + } } } @@ -9391,18 +9729,19 @@ public Dictionary> GetMonster1StunThresholdDictionary( } } - return dictionary; + return automaticSkills; } - public Dictionary>> GetPlayerInventoryDictionary(long runID) + public ZenithSkills GetZenithSkills(long runID) { - Dictionary>> dictionary = new (); + var zenithSkills = new ZenithSkills(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player inventory dictionary. dataSource: {0}", this.dataSource); - return dictionary; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get zenith skills. dataSource: {0}", this.dataSource); + return zenithSkills; } + // Use a SQL query to retrieve the ZenithSkills data for the specific RunID from the database using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); @@ -9410,21 +9749,74 @@ public Dictionary>> GetPlayerInventoryDictionary( { try { - using (var cmd = new SQLiteCommand("SELECT PlayerInventoryDictionary FROM PlayerGear WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT ZenithSkillsID, RunID, ZenithSkill1ID, ZenithSkill2ID, ZenithSkill3ID, ZenithSkill4ID, ZenithSkill5ID, ZenithSkill6ID, ZenithSkill7ID, CreatedAt, CreatedBy FROM ZenithSkills WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); - var result = cmd.ExecuteScalar(); - - if (result != null) + using (var reader = cmd.ExecuteReader()) { - var obj = JsonConvert.DeserializeObject>>>((string)result); - if (obj == null) + if (reader.Read()) { - return dictionary; + zenithSkills.ZenithSkillsID = long.Parse(reader["ZenithSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); + zenithSkills.RunID = runID; + for (var i = 1; i <= 7; i++) + { + zenithSkills.GetType().GetProperty("ZenithSkill" + i + "ID").SetValue(zenithSkills, long.Parse(reader["ZenithSkill" + i + "ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture)); + } + + zenithSkills.CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + zenithSkills.CreatedBy = reader["CreatedBy"]?.ToString() ?? "0"; } + } + } - dictionary = obj; + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } + + return zenithSkills; + } + + public CaravanSkills GetCaravanSkills(long runID) + { + var caravanSkills = new CaravanSkills(); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get caravan skills. dataSource: {0}", this.dataSource); + return caravanSkills; + } + + // Use a SQL query to retrieve the CaravanSkills data for the specific RunID from the database + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try + { + using (var cmd = new SQLiteCommand("SELECT CaravanSkillsID, RunID, CaravanSkill1ID, CaravanSkill2ID, CaravanSkill3ID, CreatedAt, CreatedBy FROM CaravanSkills WHERE RunID = @runID", conn)) + { + cmd.Parameters.AddWithValue("@runID", runID); + + using (var reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + caravanSkills.CaravanSkillsID = long.Parse(reader["CaravanSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); + caravanSkills.RunID = runID; + for (var i = 1; i <= 3; i++) + { + caravanSkills.GetType().GetProperty("CaravanSkill" + i + "ID").SetValue(caravanSkills, long.Parse(reader["CaravanSkill" + i + "ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture)); + } + + caravanSkills.CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + caravanSkills.CreatedBy = reader["CreatedBy"]?.ToString() ?? "0"; + } } } @@ -9437,16 +9829,16 @@ public Dictionary>> GetPlayerInventoryDictionary( } } - return dictionary; + return caravanSkills; } - public Dictionary>> GetAmmoDictionary(long runID) + public Dictionary GetAttackBuffDictionary(long runID) { - Dictionary>> dictionary = new (); + Dictionary attackBuffDictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get ammo dictionary. dataSource: {0}", this.dataSource); - return dictionary; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get attack buff dictionary. dataSource: {0}", this.dataSource); + return attackBuffDictionary; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -9456,7 +9848,7 @@ public Dictionary>> GetAmmoDictionary(long runID) { try { - using (var cmd = new SQLiteCommand("SELECT PlayerAmmoPouchDictionary FROM PlayerGear WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT AttackBuffDictionary FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); @@ -9464,13 +9856,13 @@ public Dictionary>> GetAmmoDictionary(long runID) if (result != null) { - var obj = JsonConvert.DeserializeObject>>>((string)result); + var obj = JsonConvert.DeserializeObject>((string)result); if (obj == null) { - return dictionary; + return attackBuffDictionary; } - dictionary = obj; + attackBuffDictionary = obj; } } @@ -9483,15 +9875,15 @@ public Dictionary>> GetAmmoDictionary(long runID) } } - return dictionary; + return attackBuffDictionary; } - public Dictionary>> GetPartnyaBagDictionary(long runID) + public Dictionary GetHitCountDictionary(long runID) { - Dictionary>> dictionary = new (); + Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get partnya bag dictionary. dataSource: {0}", this.dataSource); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get hit count dictionary. dataSource: {0}", this.dataSource); return dictionary; } @@ -9502,7 +9894,7 @@ public Dictionary>> GetPartnyaBagDictionary(long { try { - using (var cmd = new SQLiteCommand("SELECT PartnyaBagDictionary FROM PlayerGear WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT HitCountDictionary FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); @@ -9510,7 +9902,7 @@ public Dictionary>> GetPartnyaBagDictionary(long if (result != null) { - var obj = JsonConvert.DeserializeObject>>>((string)result); + var obj = JsonConvert.DeserializeObject>((string)result); if (obj == null) { return dictionary; @@ -9532,12 +9924,12 @@ public Dictionary>> GetPartnyaBagDictionary(long return dictionary; } - public Dictionary> GetHitsTakenBlockedDictionary(long runID) + public Dictionary GetHitsPerSecondDictionary(long runID) { - Dictionary> dictionary = new (); + Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get hits taken/blocked dictionary. dataSource: {0}", this.dataSource); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get hits per second dictionary. dataSource: {0}", this.dataSource); return dictionary; } @@ -9548,7 +9940,7 @@ public Dictionary> GetHitsTakenBlockedDictionary(long { try { - using (var cmd = new SQLiteCommand("SELECT HitsTakenBlockedDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT HitsPerSecondDictionary FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); @@ -9556,7 +9948,7 @@ public Dictionary> GetHitsTakenBlockedDictionary(long if (result != null) { - var obj = JsonConvert.DeserializeObject>>((string)result); + var obj = JsonConvert.DeserializeObject>((string)result); if (obj == null) { return dictionary; @@ -9578,12 +9970,12 @@ public Dictionary> GetHitsTakenBlockedDictionary(long return dictionary; } - public Dictionary GetHitsTakenBlockedPerSecondDictionary(long runID) + public Dictionary GetDamageDealtDictionary(long runID) { - Dictionary dictionary = new (); + Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get hits taken/blocked per second dictionary. dataSource: {0}", this.dataSource); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get damage dealt dictionary. dataSource: {0}", this.dataSource); return dictionary; } @@ -9594,7 +9986,7 @@ public Dictionary GetHitsTakenBlockedPerSecondDictionary(long runID { try { - using (var cmd = new SQLiteCommand("SELECT HitsTakenBlockedPerSecondDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT DamageDealtDictionary FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); @@ -9602,7 +9994,7 @@ public Dictionary GetHitsTakenBlockedPerSecondDictionary(long runID if (result != null) { - var obj = JsonConvert.DeserializeObject>((string)result); + var obj = JsonConvert.DeserializeObject>((string)result); if (obj == null) { return dictionary; @@ -9624,12 +10016,12 @@ public Dictionary GetHitsTakenBlockedPerSecondDictionary(long runID return dictionary; } - public Dictionary GetPlayerHPDictionary(long runID) + public Dictionary GetDamagePerSecondDictionary(long runID) { - Dictionary dictionary = new (); + Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player HP dictionary. dataSource: {0}", this.dataSource); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get damage per second dictionary. dataSource: {0}", this.dataSource); return dictionary; } @@ -9640,7 +10032,7 @@ public Dictionary GetPlayerHPDictionary(long runID) { try { - using (var cmd = new SQLiteCommand("SELECT PlayerHPDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT DamagePerSecondDictionary FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); @@ -9648,7 +10040,7 @@ public Dictionary GetPlayerHPDictionary(long runID) if (result != null) { - var obj = JsonConvert.DeserializeObject>((string)result); + var obj = JsonConvert.DeserializeObject>((string)result); if (obj == null) { return dictionary; @@ -9670,12 +10062,12 @@ public Dictionary GetPlayerHPDictionary(long runID) return dictionary; } - public Dictionary GetPlayerStaminaDictionary(long runID) + public Dictionary GetAreaChangesDictionary(long runID) { Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player stamina dictionary. dataSource: {0}", this.dataSource); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get area changes dictionary. dataSource: {0}", this.dataSource); return dictionary; } @@ -9686,7 +10078,7 @@ public Dictionary GetPlayerStaminaDictionary(long runID) { try { - using (var cmd = new SQLiteCommand("SELECT PlayerStaminaDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT AreaChangesDictionary FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); @@ -9716,12 +10108,12 @@ public Dictionary GetPlayerStaminaDictionary(long runID) return dictionary; } - public Dictionary GetKeystrokesDictionary(long runID) + public Dictionary GetCartsDictionary(long runID) { - Dictionary dictionary = new (); + Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get keystrokes dictionary. dataSource: {0}", this.dataSource); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get carts dictionary. dataSource: {0}", this.dataSource); return dictionary; } @@ -9732,7 +10124,7 @@ public Dictionary GetKeystrokesDictionary(long runID) { try { - using (var cmd = new SQLiteCommand("SELECT KeystrokesDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT CartsDictionary FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); @@ -9740,7 +10132,7 @@ public Dictionary GetKeystrokesDictionary(long runID) if (result != null) { - var obj = JsonConvert.DeserializeObject>((string)result); + var obj = JsonConvert.DeserializeObject>((string)result); if (obj == null) { return dictionary; @@ -9762,12 +10154,12 @@ public Dictionary GetKeystrokesDictionary(long runID) return dictionary; } - public Dictionary GetMouseInputDictionary(long runID) + public Dictionary> GetMonster1HPDictionary(long runID) { - Dictionary dictionary = new (); + Dictionary> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get mouse input dictionary. dataSource: {0}", this.dataSource); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 HP dictionary. dataSource: {0}", this.dataSource); return dictionary; } @@ -9778,7 +10170,7 @@ public Dictionary GetMouseInputDictionary(long runID) { try { - using (var cmd = new SQLiteCommand("SELECT MouseInputDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT Monster1HPDictionary FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); @@ -9786,7 +10178,7 @@ public Dictionary GetMouseInputDictionary(long runID) if (result != null) { - var obj = JsonConvert.DeserializeObject>((string)result); + var obj = JsonConvert.DeserializeObject>>((string)result); if (obj == null) { return dictionary; @@ -9808,12 +10200,12 @@ public Dictionary GetMouseInputDictionary(long runID) return dictionary; } - public Dictionary GetGamepadInputDictionary(long runID) + public Dictionary> GetMonster2HPDictionary(long runID) { - Dictionary dictionary = new (); + Dictionary> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get gamepad input dictionary. dataSource: {0}", this.dataSource); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 2 HP dictionary. dataSource: {0}", this.dataSource); return dictionary; } @@ -9824,7 +10216,7 @@ public Dictionary GetGamepadInputDictionary(long runID) { try { - using (var cmd = new SQLiteCommand("SELECT GamepadInputDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT Monster2HPDictionary FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); @@ -9832,7 +10224,7 @@ public Dictionary GetGamepadInputDictionary(long runID) if (result != null) { - var obj = JsonConvert.DeserializeObject>((string)result); + var obj = JsonConvert.DeserializeObject>>((string)result); if (obj == null) { return dictionary; @@ -9854,12 +10246,12 @@ public Dictionary GetGamepadInputDictionary(long runID) return dictionary; } - public Dictionary GetActionsPerMinuteDictionary(long runID) + public Dictionary> GetMonster3HPDictionary(long runID) { - Dictionary dictionary = new (); + Dictionary> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get actions per minute dictionary. dataSource: {0}", this.dataSource); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 3 HP dictionary. dataSource: {0}", this.dataSource); return dictionary; } @@ -9870,7 +10262,7 @@ public Dictionary GetActionsPerMinuteDictionary(long runID) { try { - using (var cmd = new SQLiteCommand("SELECT ActionsPerMinuteDictionary FROM Quests WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT Monster3HPDictionary FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); @@ -9878,7 +10270,7 @@ public Dictionary GetActionsPerMinuteDictionary(long runID) if (result != null) { - var obj = JsonConvert.DeserializeObject>((string)result); + var obj = JsonConvert.DeserializeObject>>((string)result); if (obj == null) { return dictionary; @@ -9900,13 +10292,13 @@ public Dictionary GetActionsPerMinuteDictionary(long runID) return dictionary; } - public Dictionary GetMostCommonCategory() + public Dictionary> GetMonster4HPDictionary(long runID) { - Dictionary fieldCounts = new (); + Dictionary> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common category. dataSource: {0}", this.dataSource); - return fieldCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 4 HP dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -9916,25 +10308,21 @@ public Dictionary GetMostCommonCategory() { try { - var sql = - @"SELECT - ActualOverlayMode, - COUNT(*) as count - FROM - Quests - GROUP BY - ActualOverlayMode - ORDER BY count DESC"; - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand("SELECT Monster4HPDictionary FROM Quests WHERE RunID = @runID", conn)) { - using (var reader = cmd.ExecuteReader()) + cmd.Parameters.AddWithValue("@runID", runID); + + var result = cmd.ExecuteScalar(); + + if (result != null) { - while (reader.Read()) + var obj = JsonConvert.DeserializeObject>>((string)result); + if (obj == null) { - var field = reader.GetString(0); - var count = reader.GetInt32(1); - fieldCounts.Add(field, count); + return dictionary; } + + dictionary = obj; } } @@ -9947,19 +10335,18 @@ GROUP BY } } - return fieldCounts; + return dictionary; } - public ActiveSkills GetActiveSkills(long runID) + public Dictionary> GetMonster1AttackMultiplierDictionary(long runID) { - var activeSkills = new ActiveSkills(); + Dictionary> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get active skills. dataSource: {0}", this.dataSource); - return activeSkills; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 attack multiplier dictionary. dataSource: {0}", this.dataSource); + return dictionary; } - // Use a SQL query to retrieve the ActiveSkills for the specific RunID from the database using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); @@ -9967,23 +10354,21 @@ public ActiveSkills GetActiveSkills(long runID) { try { - using (var cmd = new SQLiteCommand("SELECT ActiveSkill1ID, ActiveSkill2ID, ActiveSkill3ID, ActiveSkill4ID, ActiveSkill5ID, ActiveSkill6ID, ActiveSkill7ID, ActiveSkill8ID, ActiveSkill9ID, ActiveSkill10ID, ActiveSkill11ID, ActiveSkill12ID, ActiveSkill13ID, ActiveSkill14ID, ActiveSkill15ID, ActiveSkill16ID, ActiveSkill17ID, ActiveSkill18ID, ActiveSkill19ID, CreatedAt, CreatedBy FROM ActiveSkills WHERE RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT Monster1AttackMultiplierDictionary FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); - using (var reader = cmd.ExecuteReader()) + var result = cmd.ExecuteScalar(); + + if (result != null) { - if (reader.Read()) + var obj = JsonConvert.DeserializeObject>>((string)result); + if (obj == null) { - activeSkills.RunID = runID; - for (var i = 1; i <= 19; i++) - { - activeSkills.GetType().GetProperty("ActiveSkill" + i + "ID").SetValue(activeSkills, long.Parse(reader["ActiveSkill" + i + "ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture)); - } - - activeSkills.CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - activeSkills.CreatedBy = reader["CreatedBy"].ToString(); + return dictionary; } + + dictionary = obj; } } @@ -9996,19 +10381,18 @@ public ActiveSkills GetActiveSkills(long runID) } } - return activeSkills; + return dictionary; } - public PlayerGear GetPlayerGear(long runID) + public Dictionary> GetMonster1DefenseRateDictionary(long runID) { - var gearUsed = new PlayerGear(); + Dictionary> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player gear. dataSource: {0}", this.dataSource); - return gearUsed; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 defense rate dictionary. dataSource: {0}", this.dataSource); + return dictionary; } - // Use a SQL query to retrieve the gear used for the specific RunID from the database using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); @@ -10016,83 +10400,67 @@ public PlayerGear GetPlayerGear(long runID) { try { - using (var cmd = new SQLiteCommand( - @"SELECT - * - FROM - PlayerGear - WHERE - RunID = @runID", conn)) + using (var cmd = new SQLiteCommand("SELECT Monster1DefenseRateDictionary FROM Quests WHERE RunID = @runID", conn)) { cmd.Parameters.AddWithValue("@runID", runID); - using (var reader = cmd.ExecuteReader()) + var result = cmd.ExecuteScalar(); + + if (result != null) { - if (reader.Read()) + var obj = JsonConvert.DeserializeObject>>((string)result); + if (obj == null) { - gearUsed = new PlayerGear() - { - PlayerGearHash = reader["PlayerGearHash"]?.ToString() ?? "0", - CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"]?.ToString() ?? "0", - PlayerGearID = long.Parse(reader["PlayerGearID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - PlayerID = long.Parse(reader["PlayerID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - GearName = reader["GearName"]?.ToString() ?? "0", - StyleID = long.Parse(reader["StyleID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponIconID = long.Parse(reader["WeaponIconID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponClassID = long.Parse(reader["WeaponClassID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponTypeID = long.Parse(reader["WeaponTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - BlademasterWeaponID = reader["BlademasterWeaponID"] == DBNull.Value ? null : long.Parse(reader["BlademasterWeaponID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - GunnerWeaponID = reader["GunnerWeaponID"] == DBNull.Value ? null : long.Parse(reader["GunnerWeaponID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WeaponSlot1 = reader["WeaponSlot1"]?.ToString() ?? "0", - WeaponSlot2 = reader["WeaponSlot2"]?.ToString() ?? "0", - WeaponSlot3 = reader["WeaponSlot3"]?.ToString() ?? "0", + return dictionary; + } - HeadID = long.Parse(reader["HeadID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - HeadSlot1ID = long.Parse(reader["HeadSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - HeadSlot2ID = long.Parse(reader["HeadSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - HeadSlot3ID = long.Parse(reader["HeadSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + dictionary = obj; + } + } - ChestID = long.Parse(reader["ChestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ChestSlot1ID = long.Parse(reader["ChestSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ChestSlot2ID = long.Parse(reader["ChestSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ChestSlot3ID = long.Parse(reader["ChestSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } - ArmsID = long.Parse(reader["ArmsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ArmsSlot1ID = long.Parse(reader["ArmsSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ArmsSlot2ID = long.Parse(reader["ArmsSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ArmsSlot3ID = long.Parse(reader["ArmsSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + return dictionary; + } - WaistID = long.Parse(reader["WaistID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WaistSlot1ID = long.Parse(reader["WaistSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WaistSlot2ID = long.Parse(reader["WaistSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - WaistSlot3ID = long.Parse(reader["WaistSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + public Dictionary> GetMonster1PoisonThresholdDictionary(long runID) + { + var dictionary = new Dictionary>(); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 poison threshold dictionary. dataSource: {0}", this.dataSource); + return dictionary; + } - LegsID = long.Parse(reader["LegsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - LegsSlot1ID = long.Parse(reader["LegsSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - LegsSlot2ID = long.Parse(reader["LegsSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - LegsSlot3ID = long.Parse(reader["LegsSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try + { + using (var cmd = new SQLiteCommand("SELECT Monster1PoisonThresholdDictionary FROM Quests WHERE RunID = @runID", conn)) + { + cmd.Parameters.AddWithValue("@runID", runID); - Cuff1ID = long.Parse(reader["Cuff1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - Cuff2ID = long.Parse(reader["Cuff2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ZenithSkillsID = long.Parse(reader["ZenithSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - AutomaticSkillsID = long.Parse(reader["AutomaticSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ActiveSkillsID = long.Parse(reader["ActiveSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - CaravanSkillsID = long.Parse(reader["CaravanSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - DivaSkillID = long.Parse(reader["DivaSkillID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - GuildFoodID = long.Parse(reader["GuildFoodID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - StyleRankSkillsID = long.Parse(reader["StyleRankSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - PlayerInventoryID = long.Parse(reader["PlayerInventoryID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - AmmoPouchID = long.Parse(reader["AmmoPouchID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - PartnyaBagID = long.Parse(reader["PartnyaBagID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - PoogieItemID = long.Parse(reader["PoogieItemID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RoadDureSkillsID = long.Parse(reader["RoadDureSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - PlayerInventoryDictionary = reader["PlayerInventoryDictionary"].ToString(), - PlayerAmmoPouchDictionary = reader["PlayerAmmoPouchDictionary"].ToString(), - PartnyaBagDictionary = reader["PartnyaBagDictionary"].ToString(), - }; + var result = cmd.ExecuteScalar(); + + if (result != null) + { + var obj = JsonConvert.DeserializeObject>>((string)result); + if (obj == null) + { + return dictionary; } + + dictionary = obj; } } @@ -10105,153 +10473,62 @@ public PlayerGear GetPlayerGear(long runID) } } - return gearUsed; + return dictionary; } - // TODO add check if there are runs for the quest id? - public List GetFastestRuns(ConfigWindow configWindow, string weaponName = "All Weapons") + public Dictionary> GetMonster1SleepThresholdDictionary(long runID) { - var fastestRuns = new List(); + Dictionary> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get fastest runs. dataSource: {0}", this.dataSource); - return fastestRuns; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 sleep threshold dictionary. dataSource: {0}", this.dataSource); + return dictionary; } - if (long.TryParse(configWindow.QuestIDTextBox.Text.Trim(), NumberStyles.Number, CultureInfo.InvariantCulture, out var questID)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var conn = new SQLiteConnection(this.dataSource)) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + try { - try + using (var cmd = new SQLiteCommand("SELECT Monster1SleepThresholdDictionary FROM Quests WHERE RunID = @runID", conn)) { - var sql = string.Empty; - var weaponTypeID = 0; + cmd.Parameters.AddWithValue("@runID", runID); - if (weaponName == "All Weapons") - { - sql = @"SELECT - ObjectiveImage, - qn.QuestNameName, - q.RunID, - QuestID, - YoutubeID, - FinalTimeDisplay, - Date, - ActualOverlayMode, - PartySize - FROM - Quests q - JOIN - QuestName qn ON q.QuestID = qn.QuestNameID - WHERE - q.QuestID = @questID - AND q.PartySize = 1 - AND q.ActualOverlayMode = @SelectedOverlayMode - ORDER BY - FinalTimeValue ASC - LIMIT 20"; - } - else - { - sql = @"SELECT - ObjectiveImage, - qn.QuestNameName, - q.RunID, - QuestID, - YoutubeID, - FinalTimeDisplay, - Date, - ActualOverlayMode, - PartySize, - pg.WeaponTypeID - FROM - Quests q - JOIN - QuestName qn ON q.QuestID = qn.QuestNameID - JOIN - PlayerGear pg ON q.RunID = pg.RunID - WHERE - q.QuestID = @questID - AND q.PartySize = 1 - AND q.ActualOverlayMode = @SelectedOverlayMode - AND pg.WeaponTypeID = @SelectedWeaponTypeID - ORDER BY - FinalTimeValue ASC - LIMIT 20"; - weaponTypeID = WeaponType.IDName.FirstOrDefault(x => x.Value == weaponName).Key; - } + var result = cmd.ExecuteScalar(); - using (var cmd = new SQLiteCommand(sql, conn)) + if (result != null) { - var selectedOverlayMode = ((ComboBoxItem)configWindow.OverlayModeComboBox.SelectedItem).Content.ToString(); - - // idk if this is needed - if (string.IsNullOrEmpty(selectedOverlayMode)) - { - selectedOverlayMode = "Standard"; - } - - cmd.Parameters.AddWithValue("@questID", questID); - cmd.Parameters.AddWithValue("@SelectedOverlayMode", selectedOverlayMode); - - if (weaponName != "All Weapons") + var obj = JsonConvert.DeserializeObject>>((string)result); + if (obj == null) { - cmd.Parameters.AddWithValue("@SelectedWeaponTypeID", weaponTypeID); + return dictionary; } - using (var reader = cmd.ExecuteReader()) - { - if (reader == null || !reader.HasRows) - { - return fastestRuns; - } - - if (reader.HasRows) - { - while (reader.Read()) - { - fastestRuns.Add(new FastestRun - { - ObjectiveImage = (string)reader["ObjectiveImage"], - QuestName = (string)reader["QuestNameName"], - RunID = (long)reader["RunID"], - QuestID = (long)reader["QuestID"], - YoutubeID = (string)reader["YoutubeID"], - FinalTimeDisplay = (string)reader["FinalTimeDisplay"], - Date = DateTime.Parse((string)reader["Date"], CultureInfo.InvariantCulture), - }); - } - } - } + dictionary = obj; } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } } - return fastestRuns; + return dictionary; } - /// - /// Get a list of all achievements where the completion date is set or not by the player. - /// - /// - public List GetPlayerAchievements() + public Dictionary> GetMonster1ParalysisThresholdDictionary(long runID) { - var achievements = new List(); + Dictionary> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player achievements collection. dataSource: {0}", this.dataSource); - return achievements; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 paralysis threshold dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -10261,51 +10538,21 @@ public List GetPlayerAchievements() { try { - var sql = @"SELECT - pa.CompletionDate, - aa.AchievementID, - Title, - Description, - Rank, - Objective, - Image, - IsSecret, - Hint - FROM - AllAchievements aa - LEFT JOIN - PlayerAchievements pa ON aa.AchievementID = pa.AchievementID - ORDER BY - aa.AchievementID ASC"; - - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand("SELECT Monster1ParalysisThresholdDictionary FROM Quests WHERE RunID = @runID", conn)) { - using (var reader = cmd.ExecuteReader()) + cmd.Parameters.AddWithValue("@runID", runID); + + var result = cmd.ExecuteScalar(); + + if (result != null) { - if (reader == null || !reader.HasRows) + var obj = JsonConvert.DeserializeObject>>((string)result); + if (obj == null) { - return achievements; + return dictionary; } - if (reader.HasRows) - { - while (reader.Read()) - { - achievements.Add(new Achievement - { - CompletionDate = reader.IsDBNull(reader.GetOrdinal("CompletionDate")) - ? DateTime.UnixEpoch - : DateTime.Parse(reader.GetString(reader.GetOrdinal("CompletionDate")), CultureInfo.InvariantCulture), - Title = reader.GetString(reader.GetOrdinal("Title")), - Description = reader.GetString(reader.GetOrdinal("Description")), - Rank = AchievementService.ConvertToAchievementRank(reader.GetInt64(reader.GetOrdinal("Rank"))), - Image = reader.GetString(reader.GetOrdinal("Image")), - Objective = reader.GetString(reader.GetOrdinal("Objective")), - IsSecret = reader.GetBoolean(reader.GetOrdinal("IsSecret")), - Hint = reader.GetString(reader.GetOrdinal("Hint")), - }); - } - } + dictionary = obj; } } @@ -10318,20 +10565,16 @@ ORDER BY } } - return achievements; + return dictionary; } - /// - /// Get a list of all challenges where the unlock date is set or not by the player. - /// - /// - public ReadOnlyDictionary GetPlayerChallenges() + public Dictionary> GetMonster1BlastThresholdDictionary(long runID) { - var data = Challenges.IDChallenge; + Dictionary> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player challenges collection. dataSource: {0}", this.dataSource); - return data; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 blast threshold dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -10341,28 +10584,21 @@ public ReadOnlyDictionary GetPlayerChallenges() { try { - var sql = @"SELECT * FROM PlayerChallenges ORDER BY ChallengeID ASC"; - - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand("SELECT Monster1BlastThresholdDictionary FROM Quests WHERE RunID = @runID", conn)) { - using (var reader = cmd.ExecuteReader()) + cmd.Parameters.AddWithValue("@runID", runID); + + var result = cmd.ExecuteScalar(); + + if (result != null) { - if (reader == null || !reader.HasRows) + var obj = JsonConvert.DeserializeObject>>((string)result); + if (obj == null) { - return data; + return dictionary; } - if (reader.HasRows) - { - while (reader.Read()) - { - var challengeID = reader.GetInt32(reader.GetOrdinal("ChallengeID")); - data[challengeID].UnlockDate = reader.IsDBNull(reader.GetOrdinal("UnlockDate")) -? DateTime.UnixEpoch -: DateTime.Parse(reader.GetString(reader.GetOrdinal("UnlockDate")), CultureInfo.InvariantCulture); - break; - } - } + dictionary = obj; } } @@ -10375,16 +10611,16 @@ public ReadOnlyDictionary GetPlayerChallenges() } } - return data; + return dictionary; } - public ObservableCollection GetRecentRuns() + public Dictionary> GetMonster1StunThresholdDictionary(long runID) { - var recentRuns = new ObservableCollection(); + Dictionary> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get recent runs. dataSource: {0}", this.dataSource); - return recentRuns; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster 1 stun threshold dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -10394,51 +10630,21 @@ public ObservableCollection GetRecentRuns() { try { - using (var cmd = new SQLiteCommand( - @"SELECT - ObjectiveImage, - qn.QuestNameName, - RunID, - QuestID, - YoutubeID, - FinalTimeDisplay, - Date, - ActualOverlayMode, - PartySize - FROM - Quests q - JOIN - QuestName qn ON q.QuestID = qn.QuestNameID - ORDER BY - Date DESC - LIMIT 10", conn)) + using (var cmd = new SQLiteCommand("SELECT Monster1StunThresholdDictionary FROM Quests WHERE RunID = @runID", conn)) { - using (var reader = cmd.ExecuteReader()) + cmd.Parameters.AddWithValue("@runID", runID); + + var result = cmd.ExecuteScalar(); + + if (result != null) { - if (reader == null || !reader.HasRows) + var obj = JsonConvert.DeserializeObject>>((string)result); + if (obj == null) { - return recentRuns; + return dictionary; } - if (reader.HasRows) - { - while (reader.Read()) - { - var recentRun = new RecentRuns - { - ObjectiveImage = (string)reader["ObjectiveImage"], - QuestName = (string)reader["QuestNameName"], - RunID = (long)reader["RunID"], - QuestID = (long)reader["QuestID"], - YoutubeID = (string)reader["YoutubeID"], - FinalTimeDisplay = (string)reader["FinalTimeDisplay"], - Date = DateTime.Parse((string)reader["Date"], CultureInfo.InvariantCulture), - ActualOverlayMode = (string)reader["ActualOverlayMode"], - PartySize = (long)reader["PartySize"], - }; - recentRuns.Add(recentRun); - } - } + dictionary = obj; } } @@ -10451,21 +10657,16 @@ Date DESC } } - return recentRuns; + return dictionary; } - public List GetCalendarRuns(DateTime? selectedDate) + public Dictionary>> GetPlayerInventoryDictionary(long runID) { - var recentRuns = new List(); + Dictionary>> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get calendar runs. dataSource: {0}", this.dataSource); - return recentRuns; - } - - if (selectedDate == null) - { - return recentRuns; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player inventory dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -10475,53 +10676,21 @@ public List GetCalendarRuns(DateTime? selectedDate) { try { - using (var cmd = new SQLiteCommand( - @"SELECT - ObjectiveImage, - qn.QuestNameName, - RunID, - QuestID, - YoutubeID, - FinalTimeDisplay, - Date, - ActualOverlayMode, - PartySize - FROM - Quests q - JOIN - QuestName qn ON q.QuestID = qn.QuestNameID - WHERE - DATE(Date) = DATE(@SelectedDate) - ORDER BY - RunID ASC", conn)) + using (var cmd = new SQLiteCommand("SELECT PlayerInventoryDictionary FROM PlayerGear WHERE RunID = @runID", conn)) { - cmd.Parameters.AddWithValue("@SelectedDate", selectedDate.Value.Date); - using (var reader = cmd.ExecuteReader()) + cmd.Parameters.AddWithValue("@runID", runID); + + var result = cmd.ExecuteScalar(); + + if (result != null) { - if (reader == null || !reader.HasRows) + var obj = JsonConvert.DeserializeObject>>>((string)result); + if (obj == null) { - return recentRuns; + return dictionary; } - if (reader.HasRows) - { - while (reader.Read()) - { - var recentRun = new RecentRuns - { - ObjectiveImage = (string)reader["ObjectiveImage"], - QuestName = (string)reader["QuestNameName"], - RunID = (long)reader["RunID"], - QuestID = (long)reader["QuestID"], - YoutubeID = (string)reader["YoutubeID"], - FinalTimeDisplay = (string)reader["FinalTimeDisplay"], - Date = DateTime.Parse((string)reader["Date"], CultureInfo.InvariantCulture), - ActualOverlayMode = (string)reader["ActualOverlayMode"], - PartySize = (long)reader["PartySize"], - }; - recentRuns.Add(recentRun); - } - } + dictionary = obj; } } @@ -10534,97 +10703,43 @@ ORDER BY } } - return recentRuns; + return dictionary; } - public List CalculateTotalWeaponUsage(ConfigWindow configWindow, DataLoader dataLoader, bool isByQuestID = false) + public Dictionary>> GetAmmoDictionary(long runID) { - var weaponUsageData = new List(); + Dictionary>> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot calculate total weapon usage. dataSource: {0}", this.dataSource); - return weaponUsageData; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get ammo dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); - using (var transaction = conn.BeginTransaction()) { try { - var sql = string.Empty; - long questID = 0; - - if (isByQuestID) - { - questID = long.Parse(configWindow.QuestIDTextBox.Text.Trim(), CultureInfo.InvariantCulture); - sql = @"SELECT - WeaponTypeID, - StyleID, - COUNT(*) AS RunCount - FROM - PlayerGear - JOIN Quests ON PlayerGear.RunID = Quests.RunID - WHERE - Quests.QuestID = @QuestID - GROUP BY - WeaponTypeID, StyleID"; - } - else + using (var cmd = new SQLiteCommand("SELECT PlayerAmmoPouchDictionary FROM PlayerGear WHERE RunID = @runID", conn)) { - sql = @"SELECT - WeaponTypeID, - StyleID, - COUNT(*) AS RunCount - FROM - PlayerGear - GROUP BY - WeaponTypeID, StyleID"; - } + cmd.Parameters.AddWithValue("@runID", runID); - using (var cmd = new SQLiteCommand(sql, conn)) - { - if (isByQuestID) - { - cmd.Parameters.AddWithValue("@QuestID", questID); - } + var result = cmd.ExecuteScalar(); - using (var reader = cmd.ExecuteReader()) + if (result != null) { - if (!reader.HasRows) + var obj = JsonConvert.DeserializeObject>>>((string)result); + if (obj == null) { - // MessageBox.Show(string.Format(CultureInfo.InvariantCulture, "Runs not found. Please use the Quest ID option in Settings and go into a quest in order to view the ID needed to search. You may also not have completed any runs for the selected Quest ID or for the selected category.\n\nQuest ID: {0}\nOverlay Mode: {1}\n{2}", questID, selectedOverlayMode, reader.ToString()), Messages.ErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); - return weaponUsageData; + return dictionary; } - else - { - while (reader.Read()) - { - var weaponTypeID = (long)reader["WeaponTypeID"]; - var styleID = (long)reader["StyleID"]; - var runCount = (long)reader["RunCount"]; - - var weaponType = string.Empty; - var style = string.Empty; - lock (dataLoader.Model.WeaponUsageSync) - { - // Use the weaponTypeID, styleID, and runCount values to populate your - // LiveChart graph - // use a switch statement or a lookup table to convert the - // weaponTypeID and styleID to their corresponding string names - weaponType = WeaponType.IDName[int.Parse(weaponTypeID.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture)]; - style = WeaponStyle.IDName[int.Parse(styleID.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture)]; - weaponUsageData.Add(new WeaponUsage(weaponType, style, (int)runCount)); - } - } - } + dictionary = obj; } } - // Commit the transaction transaction.Commit(); } catch (Exception ex) @@ -10634,213 +10749,43 @@ GROUP BY } } - return weaponUsageData; + return dictionary; } - public void QuestIDButtonClick(object sender, RoutedEventArgs e, ConfigWindow configWindow) + public Dictionary>> GetPartnyaBagDictionary(long runID) { + Dictionary>> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot process quest ID button click. dataSource: {0}", this.dataSource); - return; - } - - // Execute query with only Quest ID - var questID = int.Parse(configWindow.QuestIDTextBox.Text, CultureInfo.InvariantCulture); - var selectedOverlayMode = ((ComboBoxItem)configWindow.OverlayModeComboBox.SelectedItem).Content.ToString(); - - // idk if this is needed - if (string.IsNullOrEmpty(selectedOverlayMode)) - { - selectedOverlayMode = "Standard"; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get partnya bag dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); - using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand( - @"SELECT - q.ObjectiveImage, - q.ObjectiveTypeID, - q.ObjectiveQuantity, - q.ObjectiveName, - qn.QuestNameName - FROM - Quests q - INNER JOIN - QuestName qn ON q.QuestID = qn.QuestNameID - WHERE - q.QuestID = @QuestID - AND q.PartySize = 1 - AND q.ActualOverlayMode = @SelectedOverlayMode", conn)) + using (var cmd = new SQLiteCommand("SELECT PartnyaBagDictionary FROM PlayerGear WHERE RunID = @runID", conn)) { - cmd.Parameters.AddWithValue("@QuestID", questID); - cmd.Parameters.AddWithValue("@SelectedOverlayMode", selectedOverlayMode); - - using (var reader = cmd.ExecuteReader()) - { - if (!reader.HasRows) - { - var message = string.Format(CultureInfo.InvariantCulture, "Quest ID not found. Please use the Quest ID option in Settings and go into a quest in order to view the ID needed to search. You may also not have completed any runs for the selected Quest ID or for the selected category.\n\nQuest ID: {0}\nOverlay Mode: {1}\n{2}", questID, selectedOverlayMode, reader.ToString()); - var snackbar = new Snackbar(configWindow.ConfigWindowSnackBarPresenter) - { - Style = (Style)configWindow.FindResource("CatppuccinMochaSnackBar"), - Title = Messages.ErrorTitle, - Content = message, - Icon = new SymbolIcon(SymbolRegular.ErrorCircle24), - Appearance = ControlAppearance.Danger, - Timeout = this.SnackbarTimeOut, - }; - snackbar.Show(); - return; - } - else - { - reader.Read(); - configWindow.SelectedQuestObjectiveImage.Source = new BitmapImage(new Uri(reader["ObjectiveImage"]?.ToString() ?? "0")); - var questName = reader["QuestNameName"].ToString(); - configWindow.SelectedQuestNameTextBlock.Text = questName == string.Empty ? string.Format(CultureInfo.InvariantCulture, "{0} {1}", Messages.CustomQuestName, questID) : questName; - configWindow.SelectedQuestObjectiveTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", ObjectiveType.IDName[int.Parse(reader["ObjectiveTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture)], reader["ObjectiveQuantity"], reader["ObjectiveName"]); - configWindow.CurrentTimeTextBlock.Text = DateTime.UtcNow.ToString(CultureInfo.InvariantCulture); - } - } - } + cmd.Parameters.AddWithValue("@runID", runID); - using (var cmd = new SQLiteCommand( - @"SELECT - pg.WeaponTypeID, - MIN(q.FinalTimeValue) as BestTime, - q.RunID - FROM - Quests q - JOIN - PlayerGear pg ON q.RunID = pg.RunID - WHERE - q.QuestID = @QuestID - AND q.PartySize = 1 - AND q.ActualOverlayMode = @SelectedOverlayMode - GROUP BY - pg.WeaponTypeID - ", conn)) - { - cmd.Parameters.AddWithValue("@QuestID", questID); - cmd.Parameters.AddWithValue("@SelectedOverlayMode", selectedOverlayMode); + var result = cmd.ExecuteScalar(); - using (var reader = cmd.ExecuteReader()) + if (result != null) { - if (!reader.HasRows) + var obj = JsonConvert.DeserializeObject>>>((string)result); + if (obj == null) { - var message = string.Format(CultureInfo.InvariantCulture, "Quest ID not found. Please use the Quest ID option in Settings and go into a quest in order to view the ID needed to search. You may also not have completed any runs for the selected Quest ID or for the selected category.\n\nQuest ID: {0}\nOverlay Mode: {1}\n{2}", questID, selectedOverlayMode, reader.ToString()); - var snackbar = new Snackbar(configWindow.ConfigWindowSnackBarPresenter) - { - Style = (Style)configWindow.FindResource("CatppuccinMochaSnackBar"), - Title = Messages.ErrorTitle, - Content = message, - Icon = new SymbolIcon(SymbolRegular.ErrorCircle24), - Appearance = ControlAppearance.Danger, - Timeout = this.SnackbarTimeOut, - }; - snackbar.Show(); - return; + return dictionary; } - while (reader.Read()) - { - WeaponType.IDName.TryGetValue(Convert.ToInt32(reader["WeaponTypeID"], CultureInfo.InvariantCulture), out var weaponType); - - int bestTime; - if (reader["BestTime"] == DBNull.Value) - { - bestTime = -1; - } - else - { - bestTime = int.Parse(reader["BestTime"]?.ToString() ?? "0", CultureInfo.InvariantCulture); - } - - int runID; - if (reader["RunID"] == DBNull.Value) - { - runID = -1; - } - else - { - runID = int.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); - } - - switch (weaponType) - { - case "Sword and Shield": - configWindow.SwordAndShieldBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.SwordAndShieldRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Great Sword": - configWindow.GreatSwordBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.GreatSwordRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Dual Swords": - configWindow.DualSwordsBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.DualSwordsRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Long Sword": - configWindow.LongSwordBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.LongSwordRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Lance": - configWindow.LanceBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.LanceRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Gunlance": - configWindow.GunlanceBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.GunlanceRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Hammer": - configWindow.HammerBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.HammerRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Hunting Horn": - configWindow.HuntingHornBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.HuntingHornRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Tonfa": - configWindow.TonfaBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.TonfaRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Switch Axe F": - configWindow.SwitchAxeFBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.SwitchAxeFRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Magnet Spike": - configWindow.MagnetSpikeBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.MagnetSpikeRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Light Bowgun": - configWindow.LightBowgunBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.LightBowgunRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Heavy Bowgun": - configWindow.HeavyBowgunBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.HeavyBowgunRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - case "Bow": - configWindow.BowBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); - configWindow.BowRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); - break; - default: - break; - - // Add more cases for other weapon types - } - } + dictionary = obj; } } - // Commit the transaction transaction.Commit(); } catch (Exception ex) @@ -10849,15 +10794,17 @@ GROUP BY } } } + + return dictionary; } - public Dictionary GetMostQuestCompletions() + public Dictionary> GetHitsTakenBlockedDictionary(long runID) { - Dictionary questCompletions = new (); + Dictionary> dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most quest completions. dataSource: {0}", this.dataSource); - return questCompletions; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get hits taken/blocked dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -10867,25 +10814,21 @@ public Dictionary GetMostQuestCompletions() { try { - var sql = - @"SELECT - QuestID, - COUNT(*) as completions - FROM - Quests - GROUP BY - QuestID - ORDER BY completions DESC"; - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand("SELECT HitsTakenBlockedDictionary FROM Quests WHERE RunID = @runID", conn)) { - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) + cmd.Parameters.AddWithValue("@runID", runID); + + var result = cmd.ExecuteScalar(); + + if (result != null) + { + var obj = JsonConvert.DeserializeObject>>((string)result); + if (obj == null) { - var questID = reader.GetInt32(0); - var completions = reader.GetInt32(1); - questCompletions.Add(questID, completions); + return dictionary; } + + dictionary = obj; } } @@ -10898,16 +10841,16 @@ GROUP BY } } - return questCompletions; + return dictionary; } - public string GetQuestCompletions(long questID, string actualOverlayMode, int weaponTypeID) + public Dictionary GetHitsTakenBlockedPerSecondDictionary(long runID) { - var questCompletions = "0"; + Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quest completions. dataSource: {0}", this.dataSource); - return questCompletions; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get hits taken/blocked per second dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -10917,29 +10860,21 @@ public string GetQuestCompletions(long questID, string actualOverlayMode, int we { try { - var sql = - @"SELECT - COUNT(*) AS CompletionCount - FROM - Quests q - JOIN - PlayerGear pg ON q.RunID = pg.RunID - WHERE - q.QuestID = @QuestID - AND q.ActualOverlayMode = @ActualOverlayMode - AND pg.WeaponTypeID = @WeaponTypeID"; - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand("SELECT HitsTakenBlockedPerSecondDictionary FROM Quests WHERE RunID = @runID", conn)) { - cmd.Parameters.AddWithValue("@QuestID", questID); - cmd.Parameters.AddWithValue("@ActualOverlayMode", actualOverlayMode); - cmd.Parameters.AddWithValue("@WeaponTypeID", weaponTypeID); + cmd.Parameters.AddWithValue("@runID", runID); - using (var reader = cmd.ExecuteReader()) + var result = cmd.ExecuteScalar(); + + if (result != null) { - if (reader.Read()) + var obj = JsonConvert.DeserializeObject>((string)result); + if (obj == null) { - questCompletions = Convert.ToInt64(reader["CompletionCount"], CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture); + return dictionary; } + + dictionary = obj; } } @@ -10952,48 +10887,40 @@ Quests q } } - return questCompletions; + return dictionary; } - public async Task GetQuestCompletionsAsync(long questID, string actualOverlayMode, int weaponTypeID) + public Dictionary GetPlayerHPDictionary(long runID) { - var questCompletions = "0"; + Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quest completions. dataSource: {0}", this.dataSource); - return questCompletions; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player HP dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) { - await conn.OpenAsync(); + conn.Open(); using (var transaction = conn.BeginTransaction()) { try { - var sql = - @"SELECT - COUNT(*) AS CompletionCount - FROM - Quests q - JOIN - PlayerGear pg ON q.RunID = pg.RunID - WHERE - q.QuestID = @QuestID - AND q.ActualOverlayMode = @ActualOverlayMode - AND pg.WeaponTypeID = @WeaponTypeID"; - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand("SELECT PlayerHPDictionary FROM Quests WHERE RunID = @runID", conn)) { - cmd.Parameters.AddWithValue("@QuestID", questID); - cmd.Parameters.AddWithValue("@ActualOverlayMode", actualOverlayMode); - cmd.Parameters.AddWithValue("@WeaponTypeID", weaponTypeID); + cmd.Parameters.AddWithValue("@runID", runID); - using (var reader = await cmd.ExecuteReaderAsync()) + var result = cmd.ExecuteScalar(); + + if (result != null) { - if (reader is SQLiteDataReader sqliteReader && await sqliteReader.ReadAsync()) + var obj = JsonConvert.DeserializeObject>((string)result); + if (obj == null) { - questCompletions = Convert.ToInt64(sqliteReader["CompletionCount"], CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture); + return dictionary; } + + dictionary = obj; } } @@ -11006,46 +10933,40 @@ Quests q } } - return questCompletions; + return dictionary; } - public async Task GetQuestAttemptsAsync(long questID, string actualOverlayMode, int weaponTypeID) + public Dictionary GetPlayerStaminaDictionary(long runID) { - var questAttempts = "0"; + Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quest attempts. dataSource: {0}", this.dataSource); - return questAttempts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player stamina dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) { - await conn.OpenAsync(); + conn.Open(); using (var transaction = conn.BeginTransaction()) { try { - var sql = - @"SELECT - Attempts - FROM - QuestAttempts qa - WHERE - qa.QuestID = @QuestID - AND qa.ActualOverlayMode = @ActualOverlayMode - AND qa.WeaponTypeID = @WeaponTypeID"; - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand("SELECT PlayerStaminaDictionary FROM Quests WHERE RunID = @runID", conn)) { - cmd.Parameters.AddWithValue("@QuestID", questID); - cmd.Parameters.AddWithValue("@ActualOverlayMode", actualOverlayMode); - cmd.Parameters.AddWithValue("@WeaponTypeID", weaponTypeID); + cmd.Parameters.AddWithValue("@runID", runID); - using (var reader = await cmd.ExecuteReaderAsync()) + var result = cmd.ExecuteScalar(); + + if (result != null) { - if (reader is SQLiteDataReader sqliteReader && await sqliteReader.ReadAsync()) + var obj = JsonConvert.DeserializeObject>((string)result); + if (obj == null) { - questAttempts = Convert.ToInt64(sqliteReader["Attempts"], CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture); + return dictionary; } + + dictionary = obj; } } @@ -11058,79 +10979,90 @@ QuestAttempts qa } } - return questAttempts; + return dictionary; } - public async Task GetQuestAttemptsPerPersonalBestAsync(long questID, int weaponTypeID, string actualOverlayMode) + public Dictionary GetKeystrokesDictionary(long runID) { - var attemptsPerPersonalBest = 0.0; + Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quest attempts per personal best. dataSource: {0}", this.dataSource); - return attemptsPerPersonalBest; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get keystrokes dictionary. dataSource: {0}", this.dataSource); + return dictionary; } - var questAttempts = await GetQuestAttemptsAsync(questID, actualOverlayMode, weaponTypeID); - var personalBestCount = await GetPersonalBestsCountAsync(questID, weaponTypeID, actualOverlayMode); - if (personalBestCount > 0) + using (var conn = new SQLiteConnection(this.dataSource)) { - attemptsPerPersonalBest = double.Parse(questAttempts, CultureInfo.InvariantCulture) / personalBestCount; + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try + { + using (var cmd = new SQLiteCommand("SELECT KeystrokesDictionary FROM Quests WHERE RunID = @runID", conn)) + { + cmd.Parameters.AddWithValue("@runID", runID); + + var result = cmd.ExecuteScalar(); + + if (result != null) + { + var obj = JsonConvert.DeserializeObject>((string)result); + if (obj == null) + { + return dictionary; + } + + dictionary = obj; + } + } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } } - return attemptsPerPersonalBest; + return dictionary; } - public async Task GetPersonalBestsCountAsync(long questID, int weaponTypeID, string category) + public Dictionary GetMouseInputDictionary(long runID) { - int personalBestCount = 0; - long? previousBestTime = null; + Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get personal bests count. dataSource: {0}", this.dataSource); - return personalBestCount; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get mouse input dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) { - await conn.OpenAsync().ConfigureAwait(false); + conn.Open(); using (var transaction = conn.BeginTransaction()) { try { - using (var cmd = new SQLiteCommand( - @"SELECT - q.RunID, q.FinalTimeValue - FROM - Quests q - JOIN - PlayerGear pg ON q.RunID = pg.RunID - WHERE - q.QuestID = @questID AND - q.ActualOverlayMode = @category AND - pg.WeaponTypeID = @weaponTypeID - ORDER BY q.RunID ASC", conn)) + using (var cmd = new SQLiteCommand("SELECT MouseInputDictionary FROM Quests WHERE RunID = @runID", conn)) { - cmd.Parameters.AddWithValue("@questID", questID); - cmd.Parameters.AddWithValue("@weaponTypeID", weaponTypeID); - cmd.Parameters.AddWithValue("@category", category); + cmd.Parameters.AddWithValue("@runID", runID); - using (var reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false)) + var result = cmd.ExecuteScalar(); + + if (result != null) { - while (await reader.ReadAsync().ConfigureAwait(false)) + var obj = JsonConvert.DeserializeObject>((string)result); + if (obj == null) { - var finalTimeValue = reader.GetInt64(reader.GetOrdinal("FinalTimeValue")); - - // If this is the first run or the time is less than the previous best, it's a new personal best. - if (!previousBestTime.HasValue || finalTimeValue < previousBestTime.Value) - { - personalBestCount++; - previousBestTime = finalTimeValue; - } + return dictionary; } + + dictionary = obj; } } - await transaction.CommitAsync().ConfigureAwait(false); + transaction.Commit(); } catch (Exception ex) { @@ -11139,20 +11071,18 @@ Quests q } } - return personalBestCount; + return dictionary; } - public List GetQuests(long questID, string weaponName, string actualOverlayMode) + public Dictionary GetGamepadInputDictionary(long runID) { - List quests = new(); + Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quests. dataSource: {0}", this.dataSource); - return quests; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get gamepad input dictionary. dataSource: {0}", this.dataSource); + return dictionary; } - var weaponTypeID = EZlion.Mapper.WeaponType.IDName.FirstOrDefault(x => x.Value == weaponName).Key; - using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); @@ -11160,72 +11090,21 @@ public List GetQuests(long questID, string weaponName, string actualOverl { try { - var sql = - @"SELECT - * - FROM - Quests q - JOIN - PlayerGear pg ON q.RunID = pg.RunID - WHERE - q.QuestID = @QuestID - AND q.ActualOverlayMode = @ActualOverlayMode - AND pg.WeaponTypeID = @WeaponTypeID - ORDER BY RunID ASC"; - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand("SELECT GamepadInputDictionary FROM Quests WHERE RunID = @runID", conn)) { - cmd.Parameters.AddWithValue("@QuestID", questID); - cmd.Parameters.AddWithValue("@ActualOverlayMode", actualOverlayMode); - cmd.Parameters.AddWithValue("@WeaponTypeID", weaponTypeID); + cmd.Parameters.AddWithValue("@runID", runID); - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - var quest = new Quest - { - QuestHash = reader["QuestHash"].ToString(), - CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - CreatedBy = reader["CreatedBy"].ToString(), - RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - TimeLeft = long.Parse(reader["TimeLeft"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - FinalTimeValue = long.Parse(reader["FinalTimeValue"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - FinalTimeDisplay = reader["FinalTimeDisplay"].ToString(), - ObjectiveImage = reader["ObjectiveImage"].ToString(), - ObjectiveTypeID = long.Parse(reader["ObjectiveTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - ObjectiveQuantity = long.Parse(reader["ObjectiveQuantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - StarGrade = long.Parse(reader["StarGrade"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - RankName = reader["RankName"].ToString(), - ObjectiveName = reader["ObjectiveName"].ToString(), - Date = DateTime.Parse(reader["Date"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), - YouTubeID = reader["YouTubeID"].ToString(), - AttackBuffDictionary = reader["AttackBuffDictionary"].ToString(), - HitCountDictionary = reader["HitCountDictionary"].ToString(), - HitsPerSecondDictionary = reader["HitsPerSecondDictionary"].ToString(), - DamageDealtDictionary = reader["DamageDealtDictionary"].ToString(), - DamagePerSecondDictionary = reader["DamagePerSecondDictionary"].ToString(), - AreaChangesDictionary = reader["AreaChangesDictionary"].ToString(), - CartsDictionary = reader["CartsDictionary"].ToString(), - Monster1HPDictionary = reader["Monster1HPDictionary"].ToString(), - Monster2HPDictionary = reader["Monster2HPDictionary"].ToString(), - Monster3HPDictionary = reader["Monster3HPDictionary"].ToString(), - Monster4HPDictionary = reader["Monster4HPDictionary"].ToString(), - HitsTakenBlockedDictionary = reader["HitsTakenBlockedDictionary"].ToString(), - HitsTakenBlockedPerSecondDictionary = reader["HitsTakenBlockedPerSecondDictionary"].ToString(), - PlayerHPDictionary = reader["PlayerHPDictionary"].ToString(), - PlayerStaminaDictionary = reader["PlayerStaminaDictionary"].ToString(), - KeyStrokesDictionary = reader["KeyStrokesDictionary"].ToString(), - MouseInputDictionary = reader["MouseInputDictionary"].ToString(), - GamepadInputDictionary = reader["GamepadInputDictionary"].ToString(), - ActionsPerMinuteDictionary = reader["ActionsPerMinuteDictionary"].ToString(), - OverlayModeDictionary = reader["OverlayModeDictionary"].ToString(), - ActualOverlayMode = reader["ActualOverlayMode"].ToString(), - PartySize = long.Parse(reader["PartySize"]?.ToString() ?? "0", CultureInfo.InvariantCulture), - }; + var result = cmd.ExecuteScalar(); - quests.Add(quest); + if (result != null) + { + var obj = JsonConvert.DeserializeObject>((string)result); + if (obj == null) + { + return dictionary; } + + dictionary = obj; } } @@ -11238,16 +11117,16 @@ Quests q } } - return quests; + return dictionary; } - public Dictionary GetMostCommonObjectiveTypes() + public Dictionary GetActionsPerMinuteDictionary(long runID) { - Dictionary objectiveCounts = new (); + Dictionary dictionary = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common objective types. dataSource: {0}", this.dataSource); - return objectiveCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get actions per minute dictionary. dataSource: {0}", this.dataSource); + return dictionary; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -11257,26 +11136,21 @@ public Dictionary GetMostCommonObjectiveTypes() { try { - var sql = - @"SELECT - ObjectiveTypeID, - COUNT(*) as count - FROM - Quests - GROUP BY - ObjectiveTypeID - ORDER BY count DESC"; - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand("SELECT ActionsPerMinuteDictionary FROM Quests WHERE RunID = @runID", conn)) { - using (var reader = cmd.ExecuteReader()) + cmd.Parameters.AddWithValue("@runID", runID); + + var result = cmd.ExecuteScalar(); + + if (result != null) { - while (reader.Read()) + var obj = JsonConvert.DeserializeObject>((string)result); + if (obj == null) { - var objectiveTypeID = reader.GetInt32(0); - var objectiveTypeName = ObjectiveType.IDName[objectiveTypeID]; - var count = reader.GetInt32(1); - objectiveCounts.Add(objectiveTypeName, count); + return dictionary; } + + dictionary = obj; } } @@ -11289,15 +11163,15 @@ GROUP BY } } - return objectiveCounts; + return dictionary; } - public Dictionary GetMostCommonStarGrades() + public Dictionary GetMostCommonCategory() { - Dictionary fieldCounts = new (); + Dictionary fieldCounts = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common star grades. dataSource: {0}", this.dataSource); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common category. dataSource: {0}", this.dataSource); return fieldCounts; } @@ -11310,12 +11184,12 @@ public Dictionary GetMostCommonStarGrades() { var sql = @"SELECT - StarGrade, + ActualOverlayMode, COUNT(*) as count FROM Quests GROUP BY - StarGrade + ActualOverlayMode ORDER BY count DESC"; using (var cmd = new SQLiteCommand(sql, conn)) { @@ -11323,7 +11197,7 @@ GROUP BY { while (reader.Read()) { - var field = reader.GetInt32(0); + var field = reader.GetString(0); var count = reader.GetInt32(1); fieldCounts.Add(field, count); } @@ -11342,15 +11216,16 @@ GROUP BY return fieldCounts; } - public Dictionary GetMostCommonHeadPieces() + public ActiveSkills GetActiveSkills(long runID) { - Dictionary fieldCounts = new (); + var activeSkills = new ActiveSkills(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common head pieces. dataSource: {0}", this.dataSource); - return fieldCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get active skills. dataSource: {0}", this.dataSource); + return activeSkills; } + // Use a SQL query to retrieve the ActiveSkills for the specific RunID from the database using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); @@ -11358,25 +11233,22 @@ public Dictionary GetMostCommonHeadPieces() { try { - var sql = - @"SELECT - HeadID, - COUNT(*) as count - FROM - PlayerGear - GROUP BY - HeadID - ORDER BY count DESC"; - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand("SELECT ActiveSkill1ID, ActiveSkill2ID, ActiveSkill3ID, ActiveSkill4ID, ActiveSkill5ID, ActiveSkill6ID, ActiveSkill7ID, ActiveSkill8ID, ActiveSkill9ID, ActiveSkill10ID, ActiveSkill11ID, ActiveSkill12ID, ActiveSkill13ID, ActiveSkill14ID, ActiveSkill15ID, ActiveSkill16ID, ActiveSkill17ID, ActiveSkill18ID, ActiveSkill19ID, CreatedAt, CreatedBy FROM ActiveSkills WHERE RunID = @runID", conn)) { + cmd.Parameters.AddWithValue("@runID", runID); + using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - var field = reader.GetInt32(0); - var count = reader.GetInt32(1); - var pieceName = ArmorHead.IDName[field]; - fieldCounts.Add(pieceName, count); + activeSkills.RunID = runID; + for (var i = 1; i <= 19; i++) + { + activeSkills.GetType().GetProperty("ActiveSkill" + i + "ID").SetValue(activeSkills, long.Parse(reader["ActiveSkill" + i + "ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture)); + } + + activeSkills.CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + activeSkills.CreatedBy = reader["CreatedBy"].ToString(); } } } @@ -11390,18 +11262,19 @@ GROUP BY } } - return fieldCounts; + return activeSkills; } - public Dictionary GetMostCommonChestPieces() + public PlayerGear GetPlayerGear(long runID) { - Dictionary fieldCounts = new (); + var gearUsed = new PlayerGear(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common chest pieces. dataSource: {0}", this.dataSource); - return fieldCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player gear. dataSource: {0}", this.dataSource); + return gearUsed; } + // Use a SQL query to retrieve the gear used for the specific RunID from the database using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); @@ -11409,27 +11282,82 @@ public Dictionary GetMostCommonChestPieces() { try { - var sql = + using (var cmd = new SQLiteCommand( @"SELECT - ChestID, - COUNT(*) as count - FROM - PlayerGear - GROUP BY - ChestID - ORDER BY count DESC"; - using (var cmd = new SQLiteCommand(sql, conn)) + * + FROM + PlayerGear + WHERE + RunID = @runID", conn)) { + cmd.Parameters.AddWithValue("@runID", runID); + using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - var field = reader.GetInt32(0); - var count = reader.GetInt32(1); - var pieceName = ArmorChest.IDName[field]; - if (fieldCounts.ContainsKey(pieceName)) - continue; - fieldCounts.Add(pieceName, count); + gearUsed = new PlayerGear() + { + PlayerGearHash = reader["PlayerGearHash"]?.ToString() ?? "0", + CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"]?.ToString() ?? "0", + PlayerGearID = long.Parse(reader["PlayerGearID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + PlayerID = long.Parse(reader["PlayerID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GearName = reader["GearName"]?.ToString() ?? "0", + StyleID = long.Parse(reader["StyleID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponIconID = long.Parse(reader["WeaponIconID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponClassID = long.Parse(reader["WeaponClassID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponTypeID = long.Parse(reader["WeaponTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + BlademasterWeaponID = reader["BlademasterWeaponID"] == DBNull.Value ? null : long.Parse(reader["BlademasterWeaponID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GunnerWeaponID = reader["GunnerWeaponID"] == DBNull.Value ? null : long.Parse(reader["GunnerWeaponID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WeaponSlot1 = reader["WeaponSlot1"]?.ToString() ?? "0", + WeaponSlot2 = reader["WeaponSlot2"]?.ToString() ?? "0", + WeaponSlot3 = reader["WeaponSlot3"]?.ToString() ?? "0", + + HeadID = long.Parse(reader["HeadID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HeadSlot1ID = long.Parse(reader["HeadSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HeadSlot2ID = long.Parse(reader["HeadSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + HeadSlot3ID = long.Parse(reader["HeadSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + + ChestID = long.Parse(reader["ChestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ChestSlot1ID = long.Parse(reader["ChestSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ChestSlot2ID = long.Parse(reader["ChestSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ChestSlot3ID = long.Parse(reader["ChestSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + + ArmsID = long.Parse(reader["ArmsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ArmsSlot1ID = long.Parse(reader["ArmsSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ArmsSlot2ID = long.Parse(reader["ArmsSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ArmsSlot3ID = long.Parse(reader["ArmsSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + + WaistID = long.Parse(reader["WaistID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WaistSlot1ID = long.Parse(reader["WaistSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WaistSlot2ID = long.Parse(reader["WaistSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + WaistSlot3ID = long.Parse(reader["WaistSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + + LegsID = long.Parse(reader["LegsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + LegsSlot1ID = long.Parse(reader["LegsSlot1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + LegsSlot2ID = long.Parse(reader["LegsSlot2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + LegsSlot3ID = long.Parse(reader["LegsSlot3ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + + Cuff1ID = long.Parse(reader["Cuff1ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + Cuff2ID = long.Parse(reader["Cuff2ID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ZenithSkillsID = long.Parse(reader["ZenithSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + AutomaticSkillsID = long.Parse(reader["AutomaticSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ActiveSkillsID = long.Parse(reader["ActiveSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + CaravanSkillsID = long.Parse(reader["CaravanSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + DivaSkillID = long.Parse(reader["DivaSkillID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + GuildFoodID = long.Parse(reader["GuildFoodID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + StyleRankSkillsID = long.Parse(reader["StyleRankSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + PlayerInventoryID = long.Parse(reader["PlayerInventoryID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + AmmoPouchID = long.Parse(reader["AmmoPouchID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + PartnyaBagID = long.Parse(reader["PartnyaBagID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + PoogieItemID = long.Parse(reader["PoogieItemID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RoadDureSkillsID = long.Parse(reader["RoadDureSkillsID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + PlayerInventoryDictionary = reader["PlayerInventoryDictionary"].ToString(), + PlayerAmmoPouchDictionary = reader["PlayerAmmoPouchDictionary"].ToString(), + PartnyaBagDictionary = reader["PartnyaBagDictionary"].ToString(), + }; } } } @@ -11443,67 +11371,155 @@ GROUP BY } } - return fieldCounts; + return gearUsed; } - public Dictionary GetMostCommonArmsPieces() + // TODO add check if there are runs for the quest id? + public List GetFastestRuns(ConfigWindow configWindow, string weaponName = "All Weapons") { - Dictionary fieldCounts = new (); + var fastestRuns = new List(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common arms pieces. dataSource: {0}", this.dataSource); - return fieldCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get fastest runs. dataSource: {0}", this.dataSource); + return fastestRuns; } - using (var conn = new SQLiteConnection(this.dataSource)) + if (long.TryParse(configWindow.QuestIDTextBox.Text.Trim(), NumberStyles.Number, CultureInfo.InvariantCulture, out var questID)) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + using (var conn = new SQLiteConnection(this.dataSource)) { - try + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - var sql = - @"SELECT - ArmsID, - COUNT(*) as count - FROM - PlayerGear - GROUP BY - ArmsID - ORDER BY count DESC"; - using (var cmd = new SQLiteCommand(sql, conn)) + try { - using (var reader = cmd.ExecuteReader()) + var sql = string.Empty; + var weaponTypeID = 0; + + if (weaponName == "All Weapons") { - while (reader.Read()) + sql = @"SELECT + ObjectiveImage, + qn.QuestNameName, + q.RunID, + QuestID, + YouTubeID, + FinalTimeDisplay, + Date, + ActualOverlayMode, + PartySize + FROM + Quests q + JOIN + QuestName qn ON q.QuestID = qn.QuestNameID + WHERE + q.QuestID = @questID + AND q.PartySize = 1 + AND q.ActualOverlayMode = @SelectedOverlayMode + ORDER BY + FinalTimeValue ASC + LIMIT 20"; + } + else + { + sql = @"SELECT + ObjectiveImage, + qn.QuestNameName, + q.RunID, + QuestID, + YouTubeID, + FinalTimeDisplay, + Date, + ActualOverlayMode, + PartySize, + pg.WeaponTypeID + FROM + Quests q + JOIN + QuestName qn ON q.QuestID = qn.QuestNameID + JOIN + PlayerGear pg ON q.RunID = pg.RunID + WHERE + q.QuestID = @questID + AND q.PartySize = 1 + AND q.ActualOverlayMode = @SelectedOverlayMode + AND pg.WeaponTypeID = @SelectedWeaponTypeID + ORDER BY + FinalTimeValue ASC + LIMIT 20"; + weaponTypeID = WeaponType.IDName.FirstOrDefault(x => x.Value == weaponName).Key; + } + + using (var cmd = new SQLiteCommand(sql, conn)) + { + var selectedOverlayMode = ((ComboBoxItem)configWindow.OverlayModeComboBox.SelectedItem).Content.ToString(); + + // idk if this is needed + if (string.IsNullOrEmpty(selectedOverlayMode)) { - var field = reader.GetInt32(0); - var count = reader.GetInt32(1); - var pieceName = ArmorArms.IDName[field]; - fieldCounts.Add(pieceName, count); + selectedOverlayMode = "Standard"; + } + + cmd.Parameters.AddWithValue("@questID", questID); + cmd.Parameters.AddWithValue("@SelectedOverlayMode", selectedOverlayMode); + + if (weaponName != "All Weapons") + { + cmd.Parameters.AddWithValue("@SelectedWeaponTypeID", weaponTypeID); + } + + using (var reader = cmd.ExecuteReader()) + { + if (reader == null || !reader.HasRows) + { + return fastestRuns; + } + + if (reader.HasRows) + { + while (reader.Read()) + { + fastestRuns.Add(new FastestRun + { + ObjectiveImage = (string)reader["ObjectiveImage"], + QuestName = (string)reader["QuestNameName"], + RunID = (long)reader["RunID"], + QuestID = (long)reader["QuestID"], + YouTubeID = (string)reader["YouTubeID"], + FinalTimeDisplay = (string)reader["FinalTimeDisplay"], + Date = DateTime.Parse((string)reader["Date"], CultureInfo.InvariantCulture), + }); + } + } } } - } - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } } } } - return fieldCounts; + return fastestRuns; } - public Dictionary GetMostCommonWaistPieces() + /// + /// TODO: Filter by run buffs and overlay mode speedrun. Speedruns only. + /// + /// + /// + /// + public List GetUneditedYouTubeLinkRuns() { - Dictionary fieldCounts = new (); + var runs = new List(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common waist pieces. dataSource: {0}", this.dataSource); - return fieldCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get unedited youtube link runs. dataSource: {0}", this.dataSource); + return runs; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -11513,25 +11529,51 @@ public Dictionary GetMostCommonWaistPieces() { try { - var sql = - @"SELECT - WaistID, - COUNT(*) as count - FROM - PlayerGear - GROUP BY - WaistID - ORDER BY count DESC"; + var sql = @"SELECT + ObjectiveImage, + qn.QuestNameName, + q.RunID, + QuestID, + YouTubeID, + FinalTimeDisplay, + Date, + ActualOverlayMode, + PartySize + FROM + Quests q + JOIN + QuestName qn ON q.QuestID = qn.QuestNameID + WHERE + q.PartySize = 1 + AND q.YouTubeID = 'dQw4w9WgXcQ' + AND q.ActualOverlayMode != 'Standard' + ORDER BY + q.RunID DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader == null || !reader.HasRows) { - var field = reader.GetInt32(0); - var count = reader.GetInt32(1); - var pieceName = ArmorWaist.IDName[field]; - fieldCounts.Add(pieceName, count); + return runs; + } + + if (reader.HasRows) + { + while (reader.Read()) + { + runs.Add(new FastestRun + { + ObjectiveImage = (string)reader["ObjectiveImage"], + QuestName = (string)reader["QuestNameName"], + RunID = (long)reader["RunID"], + QuestID = (long)reader["QuestID"], + YouTubeID = (string)reader["YouTubeID"], + FinalTimeDisplay = (string)reader["FinalTimeDisplay"], + Date = DateTime.Parse((string)reader["Date"], CultureInfo.InvariantCulture), + }); + } } } } @@ -11545,16 +11587,20 @@ GROUP BY } } - return fieldCounts; + return runs; } - public Dictionary GetMostCommonLegsPieces() + /// + /// Get a list of all achievements where the completion date is set or not by the player. + /// + /// + public List GetPlayerAchievements() { - Dictionary fieldCounts = new (); + var achievements = new List(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common legs pieces. dataSource: {0}", this.dataSource); - return fieldCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player achievements collection. dataSource: {0}", this.dataSource); + return achievements; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -11564,25 +11610,50 @@ public Dictionary GetMostCommonLegsPieces() { try { - var sql = - @"SELECT - LegsID, - COUNT(*) as count - FROM - PlayerGear - GROUP BY - LegsID - ORDER BY count DESC"; + var sql = @"SELECT + pa.CompletionDate, + aa.AchievementID, + Title, + Description, + Rank, + Objective, + Image, + IsSecret, + Hint + FROM + AllAchievements aa + LEFT JOIN + PlayerAchievements pa ON aa.AchievementID = pa.AchievementID + ORDER BY + aa.AchievementID ASC"; + using (var cmd = new SQLiteCommand(sql, conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader == null || !reader.HasRows) { - var field = reader.GetInt32(0); - var count = reader.GetInt32(1); - var pieceName = ArmorLegs.IDName[field]; - fieldCounts.Add(pieceName, count); + return achievements; + } + + if (reader.HasRows) + { + while (reader.Read()) + { + achievements.Add(new Achievement + { + CompletionDate = reader.IsDBNull(reader.GetOrdinal("CompletionDate")) + ? DateTime.UnixEpoch + : DateTime.Parse(reader.GetString(reader.GetOrdinal("CompletionDate")), CultureInfo.InvariantCulture), + Title = reader.GetString(reader.GetOrdinal("Title")), + Description = reader.GetString(reader.GetOrdinal("Description")), + Rank = AchievementService.ConvertToAchievementRank(reader.GetInt64(reader.GetOrdinal("Rank"))), + Image = reader.GetString(reader.GetOrdinal("Image")), + Objective = reader.GetString(reader.GetOrdinal("Objective")), + IsSecret = reader.GetBoolean(reader.GetOrdinal("IsSecret")), + Hint = reader.GetString(reader.GetOrdinal("Hint")), + }); + } } } } @@ -11596,16 +11667,20 @@ GROUP BY } } - return fieldCounts; + return achievements; } - public Dictionary GetMostCommonDivaSkill() + /// + /// Get a list of all challenges where the unlock date is set or not by the player. + /// + /// + public ReadOnlyDictionary GetPlayerChallenges() { - Dictionary fieldCounts = new (); + var data = Challenges.IDChallenge; if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common diva skill. dataSource: {0}", this.dataSource); - return fieldCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get player challenges collection. dataSource: {0}", this.dataSource); + return data; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -11615,25 +11690,27 @@ public Dictionary GetMostCommonDivaSkill() { try { - var sql = - @"SELECT - DivaSkillID, - COUNT(*) as count - FROM - PlayerGear - GROUP BY - DivaSkillID - ORDER BY count DESC"; + var sql = @"SELECT * FROM PlayerChallenges ORDER BY ChallengeID ASC"; + using (var cmd = new SQLiteCommand(sql, conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader == null || !reader.HasRows) { - var field = reader.GetInt32(0); - var count = reader.GetInt32(1); - var name = SkillDiva.IDName[field]; - fieldCounts.Add(name, count); + return data; + } + + if (reader.HasRows) + { + while (reader.Read()) + { + var challengeID = reader.GetInt32(reader.GetOrdinal("ChallengeID")); + data[challengeID].UnlockDate = reader.IsDBNull(reader.GetOrdinal("UnlockDate")) +? DateTime.UnixEpoch +: DateTime.Parse(reader.GetString(reader.GetOrdinal("UnlockDate")), CultureInfo.InvariantCulture); + break; + } } } } @@ -11647,16 +11724,16 @@ GROUP BY } } - return fieldCounts; + return data; } - public Dictionary GetMostCommonGuildFood() + public ObservableCollection GetRecentRuns() { - Dictionary fieldCounts = new (); + var recentRuns = new ObservableCollection(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common guild food. dataSource: {0}", this.dataSource); - return fieldCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get recent runs. dataSource: {0}", this.dataSource); + return recentRuns; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -11666,25 +11743,50 @@ public Dictionary GetMostCommonGuildFood() { try { - var sql = - @"SELECT - GuildFoodID, - COUNT(*) as count - FROM - PlayerGear - GROUP BY - GuildFoodID - ORDER BY count DESC"; - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand( + @"SELECT + ObjectiveImage, + qn.QuestNameName, + RunID, + QuestID, + YouTubeID, + FinalTimeDisplay, + Date, + ActualOverlayMode, + PartySize + FROM + Quests q + JOIN + QuestName qn ON q.QuestID = qn.QuestNameID + ORDER BY + Date DESC + LIMIT 10", conn)) { using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader == null || !reader.HasRows) { - var field = reader.GetInt32(0); - var count = reader.GetInt32(1); - var name = SkillArmor.IDName[field]; - fieldCounts.Add(name, count); + return recentRuns; + } + + if (reader.HasRows) + { + while (reader.Read()) + { + var recentRun = new RecentRuns + { + ObjectiveImage = (string)reader["ObjectiveImage"], + QuestName = (string)reader["QuestNameName"], + RunID = (long)reader["RunID"], + QuestID = (long)reader["QuestID"], + YouTubeID = (string)reader["YouTubeID"], + FinalTimeDisplay = (string)reader["FinalTimeDisplay"], + Date = DateTime.Parse((string)reader["Date"], CultureInfo.InvariantCulture), + ActualOverlayMode = (string)reader["ActualOverlayMode"], + PartySize = (long)reader["PartySize"], + }; + recentRuns.Add(recentRun); + } } } } @@ -11698,16 +11800,21 @@ GROUP BY } } - return fieldCounts; + return recentRuns; } - public Dictionary GetMostCommonRankBands() + public List GetCalendarRuns(DateTime? selectedDate) { - Dictionary fieldCounts = new (); + var recentRuns = new List(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common rank bands. dataSource: {0}", this.dataSource); - return fieldCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get calendar runs. dataSource: {0}", this.dataSource); + return recentRuns; + } + + if (selectedDate == null) + { + return recentRuns; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -11717,24 +11824,52 @@ public Dictionary GetMostCommonRankBands() { try { - var sql = - @"SELECT - RankName, - COUNT(*) as count + using (var cmd = new SQLiteCommand( + @"SELECT + ObjectiveImage, + qn.QuestNameName, + RunID, + QuestID, + YouTubeID, + FinalTimeDisplay, + Date, + ActualOverlayMode, + PartySize FROM - Quests - GROUP BY - RankName - ORDER BY count DESC"; - using (var cmd = new SQLiteCommand(sql, conn)) + Quests q + JOIN + QuestName qn ON q.QuestID = qn.QuestNameID + WHERE + DATE(Date) = DATE(@SelectedDate) + ORDER BY + RunID ASC", conn)) { + cmd.Parameters.AddWithValue("@SelectedDate", selectedDate.Value.Date); using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader == null || !reader.HasRows) { - var field = reader.GetString(0); - var count = reader.GetInt32(1); - fieldCounts.Add(field, count); + return recentRuns; + } + + if (reader.HasRows) + { + while (reader.Read()) + { + var recentRun = new RecentRuns + { + ObjectiveImage = (string)reader["ObjectiveImage"], + QuestName = (string)reader["QuestNameName"], + RunID = (long)reader["RunID"], + QuestID = (long)reader["QuestID"], + YouTubeID = (string)reader["YouTubeID"], + FinalTimeDisplay = (string)reader["FinalTimeDisplay"], + Date = DateTime.Parse((string)reader["Date"], CultureInfo.InvariantCulture), + ActualOverlayMode = (string)reader["ActualOverlayMode"], + PartySize = (long)reader["PartySize"], + }; + recentRuns.Add(recentRun); + } } } } @@ -11748,47 +11883,97 @@ GROUP BY } } - return fieldCounts; + return recentRuns; } - public Dictionary GetMostCommonObjectives() + public List CalculateTotalWeaponUsage(ConfigWindow configWindow, DataLoader dataLoader, bool isByQuestID = false) { - Dictionary fieldCounts = new (); + var weaponUsageData = new List(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common objectives. dataSource: {0}", this.dataSource); - return fieldCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot calculate total weapon usage. dataSource: {0}", this.dataSource); + return weaponUsageData; } using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); + using (var transaction = conn.BeginTransaction()) { try { - var sql = - @"SELECT - ObjectiveName, - COUNT(*) as count - FROM - Quests - GROUP BY - ObjectiveName - ORDER BY count DESC"; + var sql = string.Empty; + long questID = 0; + + if (isByQuestID) + { + questID = long.Parse(configWindow.QuestIDTextBox.Text.Trim(), CultureInfo.InvariantCulture); + sql = @"SELECT + WeaponTypeID, + StyleID, + COUNT(*) AS RunCount + FROM + PlayerGear + JOIN Quests ON PlayerGear.RunID = Quests.RunID + WHERE + Quests.QuestID = @QuestID + GROUP BY + WeaponTypeID, StyleID"; + } + else + { + sql = @"SELECT + WeaponTypeID, + StyleID, + COUNT(*) AS RunCount + FROM + PlayerGear + GROUP BY + WeaponTypeID, StyleID"; + } + using (var cmd = new SQLiteCommand(sql, conn)) { + if (isByQuestID) + { + cmd.Parameters.AddWithValue("@QuestID", questID); + } + using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (!reader.HasRows) { - var field = reader.GetString(0); - var count = reader.GetInt32(1); - fieldCounts.Add(field, count); + // MessageBox.Show(string.Format(CultureInfo.InvariantCulture, "Runs not found. Please use the Quest ID option in Settings and go into a quest in order to view the ID needed to search. You may also not have completed any runs for the selected Quest ID or for the selected category.\n\nQuest ID: {0}\nOverlay Mode: {1}\n{2}", questID, selectedOverlayMode, reader.ToString()), Messages.ErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); + return weaponUsageData; + } + else + { + while (reader.Read()) + { + var weaponTypeID = (long)reader["WeaponTypeID"]; + var styleID = (long)reader["StyleID"]; + var runCount = (long)reader["RunCount"]; + + var weaponType = string.Empty; + var style = string.Empty; + + lock (dataLoader.Model.WeaponUsageSync) + { + // Use the weaponTypeID, styleID, and runCount values to populate your + // LiveChart graph + // use a switch statement or a lookup table to convert the + // weaponTypeID and styleID to their corresponding string names + weaponType = WeaponType.IDName[int.Parse(weaponTypeID.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture)]; + style = WeaponStyle.IDName[int.Parse(styleID.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture)]; + weaponUsageData.Add(new WeaponUsage(weaponType, style, (int)runCount)); + } + } } } } + // Commit the transaction transaction.Commit(); } catch (Exception ex) @@ -11798,47 +11983,213 @@ GROUP BY } } - return fieldCounts; + return weaponUsageData; } - public Dictionary GetMostCommonSetNames() + public void QuestIDButtonClick(object sender, RoutedEventArgs e, ConfigWindow configWindow) { - Dictionary fieldCounts = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common set names. dataSource: {0}", this.dataSource); - return fieldCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot process quest ID button click. dataSource: {0}", this.dataSource); + return; + } + + // Execute query with only Quest ID + var questID = int.Parse(configWindow.QuestIDTextBox.Text, CultureInfo.InvariantCulture); + var selectedOverlayMode = ((ComboBoxItem)configWindow.OverlayModeComboBox.SelectedItem).Content.ToString(); + + // idk if this is needed + if (string.IsNullOrEmpty(selectedOverlayMode)) + { + selectedOverlayMode = "Standard"; } using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); + using (var transaction = conn.BeginTransaction()) { try { - var sql = - @"SELECT - GearName, - COUNT(*) as count - FROM - PlayerGear - GROUP BY - GearName - ORDER BY count DESC"; - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand( + @"SELECT + q.ObjectiveImage, + q.ObjectiveTypeID, + q.ObjectiveQuantity, + q.ObjectiveName, + qn.QuestNameName + FROM + Quests q + INNER JOIN + QuestName qn ON q.QuestID = qn.QuestNameID + WHERE + q.QuestID = @QuestID + AND q.PartySize = 1 + AND q.ActualOverlayMode = @SelectedOverlayMode", conn)) + { + cmd.Parameters.AddWithValue("@QuestID", questID); + cmd.Parameters.AddWithValue("@SelectedOverlayMode", selectedOverlayMode); + + using (var reader = cmd.ExecuteReader()) + { + if (!reader.HasRows) + { + var message = string.Format(CultureInfo.InvariantCulture, "Quest ID not found. Please use the Quest ID option in Settings and go into a quest in order to view the ID needed to search. You may also not have completed any runs for the selected Quest ID or for the selected category.\n\nQuest ID: {0}\nOverlay Mode: {1}\n{2}", questID, selectedOverlayMode, reader.ToString()); + var snackbar = new Snackbar(configWindow.ConfigWindowSnackBarPresenter) + { + Style = (Style)configWindow.FindResource("CatppuccinMochaSnackBar"), + Title = Messages.ErrorTitle, + Content = message, + Icon = new SymbolIcon(SymbolRegular.ErrorCircle24), + Appearance = ControlAppearance.Danger, + Timeout = this.SnackbarTimeOut, + }; + snackbar.Show(); + return; + } + else + { + reader.Read(); + configWindow.SelectedQuestObjectiveImage.Source = new BitmapImage(new Uri(reader["ObjectiveImage"]?.ToString() ?? "0")); + var questName = reader["QuestNameName"].ToString(); + configWindow.SelectedQuestNameTextBlock.Text = questName == string.Empty ? string.Format(CultureInfo.InvariantCulture, "{0} {1}", Messages.CustomQuestName, questID) : questName; + configWindow.SelectedQuestObjectiveTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", ObjectiveType.IDName[int.Parse(reader["ObjectiveTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture)], reader["ObjectiveQuantity"], reader["ObjectiveName"]); + configWindow.CurrentTimeTextBlock.Text = DateTime.UtcNow.ToString(CultureInfo.InvariantCulture); + } + } + } + + using (var cmd = new SQLiteCommand( + @"SELECT + pg.WeaponTypeID, + MIN(q.FinalTimeValue) as BestTime, + q.RunID + FROM + Quests q + JOIN + PlayerGear pg ON q.RunID = pg.RunID + WHERE + q.QuestID = @QuestID + AND q.PartySize = 1 + AND q.ActualOverlayMode = @SelectedOverlayMode + GROUP BY + pg.WeaponTypeID + ", conn)) { + cmd.Parameters.AddWithValue("@QuestID", questID); + cmd.Parameters.AddWithValue("@SelectedOverlayMode", selectedOverlayMode); + using (var reader = cmd.ExecuteReader()) { + if (!reader.HasRows) + { + var message = string.Format(CultureInfo.InvariantCulture, "Quest ID not found. Please use the Quest ID option in Settings and go into a quest in order to view the ID needed to search. You may also not have completed any runs for the selected Quest ID or for the selected category.\n\nQuest ID: {0}\nOverlay Mode: {1}\n{2}", questID, selectedOverlayMode, reader.ToString()); + var snackbar = new Snackbar(configWindow.ConfigWindowSnackBarPresenter) + { + Style = (Style)configWindow.FindResource("CatppuccinMochaSnackBar"), + Title = Messages.ErrorTitle, + Content = message, + Icon = new SymbolIcon(SymbolRegular.ErrorCircle24), + Appearance = ControlAppearance.Danger, + Timeout = this.SnackbarTimeOut, + }; + snackbar.Show(); + return; + } + while (reader.Read()) { - var field = reader.GetString(0); - var count = reader.GetInt32(1); - fieldCounts.Add(field, count); + WeaponType.IDName.TryGetValue(Convert.ToInt32(reader["WeaponTypeID"], CultureInfo.InvariantCulture), out var weaponType); + + int bestTime; + if (reader["BestTime"] == DBNull.Value) + { + bestTime = -1; + } + else + { + bestTime = int.Parse(reader["BestTime"]?.ToString() ?? "0", CultureInfo.InvariantCulture); + } + + int runID; + if (reader["RunID"] == DBNull.Value) + { + runID = -1; + } + else + { + runID = int.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture); + } + + switch (weaponType) + { + case "Sword and Shield": + configWindow.SwordAndShieldBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.SwordAndShieldRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Great Sword": + configWindow.GreatSwordBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.GreatSwordRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Dual Swords": + configWindow.DualSwordsBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.DualSwordsRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Long Sword": + configWindow.LongSwordBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.LongSwordRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Lance": + configWindow.LanceBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.LanceRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Gunlance": + configWindow.GunlanceBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.GunlanceRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Hammer": + configWindow.HammerBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.HammerRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Hunting Horn": + configWindow.HuntingHornBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.HuntingHornRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Tonfa": + configWindow.TonfaBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.TonfaRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Switch Axe F": + configWindow.SwitchAxeFBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.SwitchAxeFRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Magnet Spike": + configWindow.MagnetSpikeBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.MagnetSpikeRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Light Bowgun": + configWindow.LightBowgunBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.LightBowgunRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Heavy Bowgun": + configWindow.HeavyBowgunBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.HeavyBowgunRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + case "Bow": + configWindow.BowBestTimeTextBlock.Text = TimeService.GetMinutesSecondsMillisecondsFromFrames(bestTime); + configWindow.BowRunIDTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "Run ID: {0}", runID); + break; + default: + break; + + // Add more cases for other weapon types + } } } } + // Commit the transaction transaction.Commit(); } catch (Exception ex) @@ -11847,17 +12198,15 @@ GROUP BY } } } - - return fieldCounts; } - public Dictionary GetQuestsCompletedByDate() + public Dictionary GetMostQuestCompletions() { - Dictionary questsCompletedByDate = new (); + Dictionary questCompletions = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quests completed by date. dataSource: {0}", this.dataSource); - return questsCompletedByDate; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most quest completions. dataSource: {0}", this.dataSource); + return questCompletions; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -11869,22 +12218,22 @@ public Dictionary GetQuestsCompletedByDate() { var sql = @"SELECT - DATE(CreatedAt) as DateOnly, - COUNT(*) as completions - FROM - Quests - GROUP BY - DateOnly - ORDER BY DateOnly ASC"; + QuestID, + COUNT(*) as completions + FROM + Quests + GROUP BY + QuestID + ORDER BY completions DESC"; using (var cmd = new SQLiteCommand(sql, conn)) { using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { - var date = reader.GetDateTime(0); + var questID = reader.GetInt32(0); var completions = reader.GetInt32(1); - questsCompletedByDate.Add(date, completions); + questCompletions.Add(questID, completions); } } } @@ -11898,16 +12247,16 @@ GROUP BY } } - return questsCompletedByDate; + return questCompletions; } - public Dictionary GetMostCommonWeaponNames() + public string GetQuestCompletions(long questID, string actualOverlayMode, int weaponTypeID, long partySize) { - Dictionary weaponCounts = new (); + var questCompletions = "0"; if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common weapon names. dataSource: {0}", this.dataSource); - return weaponCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quest completions. dataSource: {0}", this.dataSource); + return questCompletions; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -11919,37 +12268,28 @@ public Dictionary GetMostCommonWeaponNames() { var sql = @"SELECT - WeaponClassID, - COALESCE(BlademasterWeaponID, GunnerWeaponID) as WeaponID, - COUNT(*) as Frequency - FROM - PlayerGear - GROUP BY - WeaponID - ORDER BY Frequency DESC"; + COUNT(*) AS CompletionCount + FROM + Quests q + JOIN + PlayerGear pg ON q.RunID = pg.RunID + WHERE + q.QuestID = @QuestID + AND q.ActualOverlayMode = @ActualOverlayMode + AND pg.WeaponTypeID = @WeaponTypeID + AND q.PartySize = @PartySize"; using (var cmd = new SQLiteCommand(sql, conn)) { + cmd.Parameters.AddWithValue("@QuestID", questID); + cmd.Parameters.AddWithValue("@ActualOverlayMode", actualOverlayMode); + cmd.Parameters.AddWithValue("@WeaponTypeID", weaponTypeID); + cmd.Parameters.AddWithValue("@PartySize", partySize); + using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) + if (reader.Read()) { - var weaponClassID = reader.GetInt32(0); - var weaponID = reader.GetInt32(1); - var frequency = reader.GetInt32(2); - var weaponName = string.Empty; - if (WeaponBlademaster.IDName.ContainsKey(weaponID) && WeaponClass.IDName[weaponClassID] == "Blademaster") - { - weaponName = WeaponBlademaster.IDName[weaponID]; - } - else if (WeaponGunner.IDName.ContainsKey(weaponID) && WeaponClass.IDName[weaponClassID] == "Gunner") - { - weaponName = WeaponGunner.IDName[weaponID]; - } - - if (!string.IsNullOrEmpty(weaponName)) - { - weaponCounts[weaponName] = frequency; - } + questCompletions = Convert.ToInt64(reader["CompletionCount"], CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture); } } } @@ -11963,59 +12303,47 @@ GROUP BY } } - return weaponCounts; + return questCompletions; } - public Dictionary GetMostCommonStyleRankSkills() + public async Task GetQuestCompletionsAsync(long questID, string actualOverlayMode, int weaponTypeID) { - Dictionary skillCounts = new (); + var questCompletions = "0"; if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common style rank skills. dataSource: {0}", this.dataSource); - return skillCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quest completions. dataSource: {0}", this.dataSource); + return questCompletions; } using (var conn = new SQLiteConnection(this.dataSource)) { - conn.Open(); + await conn.OpenAsync(); using (var transaction = conn.BeginTransaction()) { try { var sql = @"SELECT - StyleRankSkill1ID, - COUNT(*) as count - FROM - StyleRankSkills - GROUP BY - StyleRankSkill1ID - UNION - SELECT - StyleRankSkill2ID, - COUNT(*) as count - FROM - StyleRankSkills - GROUP BY - StyleRankSkill2ID - ORDER BY count DESC"; + COUNT(*) AS CompletionCount + FROM + Quests q + JOIN + PlayerGear pg ON q.RunID = pg.RunID + WHERE + q.QuestID = @QuestID + AND q.ActualOverlayMode = @ActualOverlayMode + AND pg.WeaponTypeID = @WeaponTypeID"; using (var cmd = new SQLiteCommand(sql, conn)) { - using (var reader = cmd.ExecuteReader()) + cmd.Parameters.AddWithValue("@QuestID", questID); + cmd.Parameters.AddWithValue("@ActualOverlayMode", actualOverlayMode); + cmd.Parameters.AddWithValue("@WeaponTypeID", weaponTypeID); + + using (var reader = await cmd.ExecuteReaderAsync()) { - while (reader.Read()) + if (reader is SQLiteDataReader sqliteReader && await sqliteReader.ReadAsync()) { - var skillID = reader.GetInt32(0); - var count = reader.GetInt32(1); - var skillName = SkillStyleRank.IDName[skillID]; - if (!skillCounts.ContainsKey(skillName)) - { - skillCounts.Add(skillName, count); - } - else - { - skillCounts[skillName] += count; - } + questCompletions = Convert.ToInt64(sqliteReader["CompletionCount"], CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture); } } } @@ -12029,67 +12357,45 @@ GROUP BY } } - return skillCounts; + return questCompletions; } - public Dictionary GetMostCommonCaravanSkills() + public async Task GetQuestAttemptsAsync(long questID, string actualOverlayMode, int weaponTypeID) { - Dictionary skillCounts = new (); + var questAttempts = "0"; if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common caravan skills. dataSource: {0}", this.dataSource); - return skillCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quest attempts. dataSource: {0}", this.dataSource); + return questAttempts; } using (var conn = new SQLiteConnection(this.dataSource)) { - conn.Open(); + await conn.OpenAsync(); using (var transaction = conn.BeginTransaction()) { try { var sql = @"SELECT - CaravanSkill1ID, - COUNT(*) as count - FROM - CaravanSkills - GROUP BY - CaravanSkill1ID - UNION - SELECT - CaravanSkill2ID, - COUNT(*) as count - FROM - CaravanSkills - GROUP BY - CaravanSkill2ID - UNION - SELECT - CaravanSkill3ID, - COUNT(*) as count - FROM - CaravanSkills - GROUP BY - CaravanSkill3ID - ORDER BY count DESC"; + Attempts + FROM + QuestAttempts qa + WHERE + qa.QuestID = @QuestID + AND qa.ActualOverlayMode = @ActualOverlayMode + AND qa.WeaponTypeID = @WeaponTypeID"; using (var cmd = new SQLiteCommand(sql, conn)) { - using (var reader = cmd.ExecuteReader()) + cmd.Parameters.AddWithValue("@QuestID", questID); + cmd.Parameters.AddWithValue("@ActualOverlayMode", actualOverlayMode); + cmd.Parameters.AddWithValue("@WeaponTypeID", weaponTypeID); + + using (var reader = await cmd.ExecuteReaderAsync()) { - while (reader.Read()) + if (reader is SQLiteDataReader sqliteReader && await sqliteReader.ReadAsync()) { - var skillID = reader.GetInt32(0); - var count = reader.GetInt32(1); - var skillName = SkillCaravan.IDName[skillID]; - if (!skillCounts.ContainsKey(skillName)) - { - skillCounts.Add(skillName, count); - } - else - { - skillCounts[skillName] += count; - } + questAttempts = Convert.ToInt64(sqliteReader["Attempts"], CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture); } } } @@ -12103,48 +12409,97 @@ GROUP BY } } - return skillCounts; + return questAttempts; } - public Dictionary GetMostCommonPartySize() + public async Task GetQuestAttemptsPerPersonalBestAsync(long questID, int weaponTypeID, string actualOverlayMode) { - Dictionary fieldCounts = new (); + var attemptsPerPersonalBest = 0.0; if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common party size. dataSource: {0}", this.dataSource); - return fieldCounts; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quest attempts per personal best. dataSource: {0}", this.dataSource); + return attemptsPerPersonalBest; + } + + var questAttempts = await GetQuestAttemptsAsync(questID, actualOverlayMode, weaponTypeID); + var personalBestCount = await GetPersonalBestsCountAsync(questID, weaponTypeID, actualOverlayMode); + if (personalBestCount > 0) + { + attemptsPerPersonalBest = double.Parse(questAttempts, CultureInfo.InvariantCulture) / personalBestCount; + } + + return attemptsPerPersonalBest; + } + + public double GetQuestAttemptsPerPersonalBest(long questID, int weaponTypeID, string actualOverlayMode, string questAttempts, long partySize) + { + var attemptsPerPersonalBest = 0.0; + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quest attempts per personal best. dataSource: {0}", this.dataSource); + return attemptsPerPersonalBest; + } + + var personalBestCount = GetPersonalBestsCount(questID, weaponTypeID, actualOverlayMode, partySize); + if (personalBestCount > 0) + { + attemptsPerPersonalBest = double.Parse(questAttempts, CultureInfo.InvariantCulture) / personalBestCount; + } + + return attemptsPerPersonalBest; + } + + public async Task GetPersonalBestsCountAsync(long questID, int weaponTypeID, string category) + { + int personalBestCount = 0; + long? previousBestTime = null; + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get personal bests count. dataSource: {0}", this.dataSource); + return personalBestCount; } using (var conn = new SQLiteConnection(this.dataSource)) { - conn.Open(); + await conn.OpenAsync().ConfigureAwait(false); using (var transaction = conn.BeginTransaction()) { try { - var sql = - @"SELECT - PartySize, - COUNT(*) as count - FROM - Quests - GROUP BY - PartySize - ORDER BY count DESC"; - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand( + @"SELECT + q.RunID, q.FinalTimeValue + FROM + Quests q + JOIN + PlayerGear pg ON q.RunID = pg.RunID + WHERE + q.QuestID = @questID AND + q.ActualOverlayMode = @category AND + pg.WeaponTypeID = @weaponTypeID + ORDER BY q.RunID ASC", conn)) { - using (var reader = cmd.ExecuteReader()) + cmd.Parameters.AddWithValue("@questID", questID); + cmd.Parameters.AddWithValue("@weaponTypeID", weaponTypeID); + cmd.Parameters.AddWithValue("@category", category); + + using (var reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false)) { - while (reader.Read()) + while (await reader.ReadAsync().ConfigureAwait(false)) { - var field = reader.GetInt32(0); - var count = reader.GetInt32(1); - fieldCounts.Add(field, count); + var finalTimeValue = reader.GetInt64(reader.GetOrdinal("FinalTimeValue")); + + // If this is the first run or the time is less than the previous best, it's a new personal best. + if (!previousBestTime.HasValue || finalTimeValue < previousBestTime.Value) + { + personalBestCount++; + previousBestTime = finalTimeValue; + } } } } - transaction.Commit(); + await transaction.CommitAsync().ConfigureAwait(false); } catch (Exception ex) { @@ -12153,20 +12508,17 @@ GROUP BY } } - return fieldCounts; + return personalBestCount; } - /// - /// Gets the total time spent in quests in descending order. - /// - /// - public Dictionary GetTotalTimeSpentInQuests() + public int GetPersonalBestsCount(long questID, int weaponTypeID, string category, long partySize) { - Dictionary questTimeSpent = new (); + int personalBestCount = 0; + long? previousBestTime = null; if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get total time spent in quests. dataSource: {0}", this.dataSource); - return questTimeSpent; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get personal bests count. dataSource: {0}", this.dataSource); + return personalBestCount; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -12176,26 +12528,39 @@ public Dictionary GetTotalTimeSpentInQuests() { try { - var sql = - @"SELECT - QuestID, - TOTAL(FinalTimeValue) as timeSpent - FROM - Quests - GROUP BY - QuestID - ORDER BY timeSpent DESC"; - using (var cmd = new SQLiteCommand(sql, conn)) + using (var cmd = new SQLiteCommand( + @"SELECT + q.RunID, q.FinalTimeValue + FROM + Quests q + JOIN + PlayerGear pg ON q.RunID = pg.RunID + WHERE + q.QuestID = @questID AND + q.ActualOverlayMode = @category AND + pg.WeaponTypeID = @weaponTypeID AND + q.PartySize = @partySize + ORDER BY q.RunID ASC", conn)) { + cmd.Parameters.AddWithValue("@questID", questID); + cmd.Parameters.AddWithValue("@weaponTypeID", weaponTypeID); + cmd.Parameters.AddWithValue("@category", category); + cmd.Parameters.AddWithValue("@partySize", partySize); + using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { - var questID = reader.GetInt32(0); - var timeSpent = Convert.ToInt32(reader.GetDouble(1)); - questTimeSpent.Add(questID, timeSpent); - } - } + var finalTimeValue = reader.GetInt64(reader.GetOrdinal("FinalTimeValue")); + + // If this is the first run or the time is less than the previous best, it's a new personal best. + if (!previousBestTime.HasValue || finalTimeValue < previousBestTime.Value) + { + personalBestCount++; + previousBestTime = finalTimeValue; + } + } + } } transaction.Commit(); @@ -12207,23 +12572,20 @@ GROUP BY } } - return questTimeSpent; + return personalBestCount; } - public void InsertMezFesMinigameScore(DataLoader dataLoader, int previousMezFesArea, int previousMezFesScore) + public List GetQuests(long questID, string weaponName, string actualOverlayMode) { - if (!MezFesMinigames.ID.ContainsKey(previousMezFesArea) || previousMezFesScore <= 0) - { - Logger.Error(CultureInfo.InvariantCulture, "Wrong MezFes area or empty score. Area ID: {0}, Score: {1}", previousMezFesArea, previousMezFesScore); - return; - } - + List quests = new(); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot insert MezFes minigame score. dataSource: {0}", this.dataSource); - return; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quests. dataSource: {0}", this.dataSource); + return quests; } + var weaponTypeID = EZlion.Mapper.WeaponType.IDName.FirstOrDefault(x => x.Value == weaponName).Key; + using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); @@ -12231,28 +12593,73 @@ public void InsertMezFesMinigameScore(DataLoader dataLoader, int previousMezFesA { try { - var sql = @"INSERT INTO MezFes ( - CreatedAt, - CreatedBy, - MezFesMinigameID, - Score - ) VALUES ( - @CreatedAt, - @CreatedBy, - @MezFesMinigameID, - @Score)"; - + var sql = + @"SELECT + * + FROM + Quests q + JOIN + PlayerGear pg ON q.RunID = pg.RunID + WHERE + q.QuestID = @QuestID + AND q.ActualOverlayMode = @ActualOverlayMode + AND pg.WeaponTypeID = @WeaponTypeID + ORDER BY RunID ASC"; using (var cmd = new SQLiteCommand(sql, conn)) { - var createdAt = DateTime.UtcNow; - var createdBy = ViewModels.Windows.AddressModel.GetFullCurrentProgramVersion(); + cmd.Parameters.AddWithValue("@QuestID", questID); + cmd.Parameters.AddWithValue("@ActualOverlayMode", actualOverlayMode); + cmd.Parameters.AddWithValue("@WeaponTypeID", weaponTypeID); - cmd.Parameters.AddWithValue("@CreatedAt", createdAt); - cmd.Parameters.AddWithValue("@CreatedBy", createdBy); - cmd.Parameters.AddWithValue("@MezFesMinigameID", previousMezFesArea); - cmd.Parameters.AddWithValue("@Score", previousMezFesScore); + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var quest = new Quest + { + QuestHash = reader["QuestHash"].ToString(), + CreatedAt = DateTime.Parse(reader["CreatedAt"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + CreatedBy = reader["CreatedBy"].ToString(), + RunID = long.Parse(reader["RunID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + QuestID = long.Parse(reader["QuestID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + TimeLeft = long.Parse(reader["TimeLeft"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + FinalTimeValue = long.Parse(reader["FinalTimeValue"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + FinalTimeDisplay = reader["FinalTimeDisplay"].ToString(), + ObjectiveImage = reader["ObjectiveImage"].ToString(), + ObjectiveTypeID = long.Parse(reader["ObjectiveTypeID"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + ObjectiveQuantity = long.Parse(reader["ObjectiveQuantity"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + StarGrade = long.Parse(reader["StarGrade"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + RankName = reader["RankName"].ToString(), + ObjectiveName = reader["ObjectiveName"].ToString(), + Date = DateTime.Parse(reader["Date"]?.ToString() ?? DateTime.UnixEpoch.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture), + YouTubeID = reader["YouTubeID"].ToString(), + AttackBuffDictionary = reader["AttackBuffDictionary"].ToString(), + HitCountDictionary = reader["HitCountDictionary"].ToString(), + HitsPerSecondDictionary = reader["HitsPerSecondDictionary"].ToString(), + DamageDealtDictionary = reader["DamageDealtDictionary"].ToString(), + DamagePerSecondDictionary = reader["DamagePerSecondDictionary"].ToString(), + AreaChangesDictionary = reader["AreaChangesDictionary"].ToString(), + CartsDictionary = reader["CartsDictionary"].ToString(), + Monster1HPDictionary = reader["Monster1HPDictionary"].ToString(), + Monster2HPDictionary = reader["Monster2HPDictionary"].ToString(), + Monster3HPDictionary = reader["Monster3HPDictionary"].ToString(), + Monster4HPDictionary = reader["Monster4HPDictionary"].ToString(), + HitsTakenBlockedDictionary = reader["HitsTakenBlockedDictionary"].ToString(), + HitsTakenBlockedPerSecondDictionary = reader["HitsTakenBlockedPerSecondDictionary"].ToString(), + PlayerHPDictionary = reader["PlayerHPDictionary"].ToString(), + PlayerStaminaDictionary = reader["PlayerStaminaDictionary"].ToString(), + KeyStrokesDictionary = reader["KeyStrokesDictionary"].ToString(), + MouseInputDictionary = reader["MouseInputDictionary"].ToString(), + GamepadInputDictionary = reader["GamepadInputDictionary"].ToString(), + ActionsPerMinuteDictionary = reader["ActionsPerMinuteDictionary"].ToString(), + OverlayModeDictionary = reader["OverlayModeDictionary"].ToString(), + ActualOverlayMode = reader["ActualOverlayMode"].ToString(), + PartySize = long.Parse(reader["PartySize"]?.ToString() ?? "0", CultureInfo.InvariantCulture), + }; - cmd.ExecuteNonQuery(); + quests.Add(quest); + } + } } transaction.Commit(); @@ -12263,40 +12670,45 @@ public void InsertMezFesMinigameScore(DataLoader dataLoader, int previousMezFesA } } } + + return quests; } - /// - /// Gets the total quest time elapsed in frames. - /// - /// The connection. - /// - public long GetTotalQuestTimeElapsed() + public Dictionary GetMostCommonObjectiveTypes() { - long totalTimeElapsed = 0; - + Dictionary objectiveCounts = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get total frames elapsed in quests. dataSource: {0}", this.dataSource); - return totalTimeElapsed; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common objective types. dataSource: {0}", this.dataSource); + return objectiveCounts; } using (var conn = new SQLiteConnection(this.dataSource)) { conn.Open(); - using (var transaction = conn.BeginTransaction()) { try { - var query = @"SELECT TOTAL(FinalTimeValue) AS TotalTimeElapsed FROM Quests"; - - using (var cmd = new SQLiteCommand(query, conn)) + var sql = + @"SELECT + ObjectiveTypeID, + COUNT(*) as count + FROM + Quests + GROUP BY + ObjectiveTypeID + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { using (var reader = cmd.ExecuteReader()) { - if (reader.Read()) + while (reader.Read()) { - totalTimeElapsed = Convert.ToInt64(reader["TotalTimeElapsed"], CultureInfo.InvariantCulture); + var objectiveTypeID = reader.GetInt32(0); + var objectiveTypeName = ObjectiveType.IDName[objectiveTypeID]; + var count = reader.GetInt32(1); + objectiveCounts.Add(objectiveTypeName, count); } } } @@ -12310,16 +12722,16 @@ public long GetTotalQuestTimeElapsed() } } - return totalTimeElapsed; + return objectiveCounts; } - public QuestCompendium GetQuestCompendium() + public Dictionary GetMostCommonStarGrades() { - var questCompendium = new QuestCompendium(); + Dictionary fieldCounts = new (); if (string.IsNullOrEmpty(this.dataSource)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quest compendium. dataSource: {0}", this.dataSource); - return questCompendium; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common star grades. dataSource: {0}", this.dataSource); + return fieldCounts; } using (var conn = new SQLiteConnection(this.dataSource)) @@ -12329,212 +12741,234 @@ public QuestCompendium GetQuestCompendium() { try { - // TODO i guess this works? - (questCompendium.MostCompletedQuestRuns, questCompendium.MostCompletedQuestRunsQuestID) = GetMostCompletedQuestRun(conn); - questCompendium.MostCompletedQuestRunsAttempted = GetMostCompletedQuestRunsAttempted(conn, questCompendium.MostCompletedQuestRunsQuestID); - (questCompendium.MostAttemptedQuestRuns, questCompendium.MostAttemptedQuestRunsQuestID) = GetMostAttemptedQuestRun(conn); - questCompendium.MostAttemptedQuestRunsCompleted = GetMostAttemptedQuestRunsCompleted(conn, questCompendium.MostAttemptedQuestRunsQuestID); - questCompendium.TotalQuestsCompleted = GetTableRowCount("RunID", "Quests", conn); - questCompendium.TotalQuestsAttempted = GetSumValue("Attempts", "QuestAttempts", conn); - questCompendium.QuestCompletionTimeElapsedAverage = GetAvgValue("FinalTimeValue", "Quests", conn); - questCompendium.QuestCompletionTimeElapsedMedian = GetMedianValue("FinalTimeValue", "Quests", conn); - - var query = @"SELECT TOTAL(FinalTimeValue) AS TotalTimeElapsed - FROM Quests - "; - - using (var cmd = new SQLiteCommand(query, conn)) + var sql = + @"SELECT + StarGrade, + COUNT(*) as count + FROM + Quests + GROUP BY + StarGrade + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { using (var reader = cmd.ExecuteReader()) { - if (reader.Read()) + while (reader.Read()) { - var value = Convert.ToInt64(reader["TotalTimeElapsed"], CultureInfo.InvariantCulture); - - questCompendium.TotalTimeElapsedQuests = value; + var field = reader.GetInt32(0); + var count = reader.GetInt32(1); + fieldCounts.Add(field, count); } } } - // Initialize a list to hold the final cart values - var finalCartValues = new List(); + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } - // Query to get the last cart value from each quest entry - query = @"SELECT CartsDictionary FROM Quests WHERE CartsDictionary IS NOT NULL"; + return fieldCounts; + } - using (var cmd = new SQLiteCommand(query, conn)) + public Dictionary GetMostCommonHeadPieces() + { + Dictionary fieldCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common head pieces. dataSource: {0}", this.dataSource); + return fieldCounts; + } + + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try + { + var sql = + @"SELECT + HeadID, + COUNT(*) as count + FROM + PlayerGear + GROUP BY + HeadID + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { - var cartsDictionaryJson = reader.GetString(0); - if (!string.IsNullOrEmpty(cartsDictionaryJson) && cartsDictionaryJson != "{}") - { - // Deserialize the carts dictionary JSON string - var cartsDictionary = JsonConvert.DeserializeObject>(cartsDictionaryJson); - - if (cartsDictionary != null) - { - // Get the last cart value from the dictionary - var finalCartValue = cartsDictionary.Values.LastOrDefault(); - - // Add the final cart value to the list - finalCartValues.Add(finalCartValue); - } - } + var field = reader.GetInt32(0); + var count = reader.GetInt32(1); + var pieceName = ArmorHead.IDName[field]; + fieldCounts.Add(pieceName, count); } } } - // Calculate the average and median final cart values - if (finalCartValues.Count > 0) - { - questCompendium.TotalCartsInQuestAverage = finalCartValues.Average(); - questCompendium.TotalCartsInQuestMedian = CalculateMedianOfList(finalCartValues); - } - else - { - // No quest entries with non-empty carts dictionaries found - questCompendium.TotalCartsInQuestAverage = 0; - questCompendium.TotalCartsInQuestMedian = 0; - } - - questCompendium.TotalCartsInQuest = finalCartValues.Sum(); + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } - // Initialize dictionary to hold the total carts for each quest ID - Dictionary questTotalCarts = new (); + return fieldCounts; + } - // Query to get carts dictionary for all quests with non-empty carts dictionary - query = @"SELECT QuestId, CartsDictionary FROM Quests WHERE CartsDictionary IS NOT NULL AND CartsDictionary != '{}'"; + public Dictionary GetMostCommonChestPieces() + { + Dictionary fieldCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common chest pieces. dataSource: {0}", this.dataSource); + return fieldCounts; + } - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try + { + var sql = + @"SELECT + ChestID, + COUNT(*) as count + FROM + PlayerGear + GROUP BY + ChestID + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { - var questId = Convert.ToInt32(reader["QuestId"], CultureInfo.InvariantCulture); - var cartsDictionaryJson = reader.GetString(1); - - // Deserialize carts dictionary JSON string - var cartsDictionary = JsonConvert.DeserializeObject>(cartsDictionaryJson); - - if (cartsDictionary != null) - { - // Add total carts for this quest to the questTotalCarts dictionary - var totalCarts = cartsDictionary.Values.Sum(); - questTotalCarts[questId] = totalCarts; - } + var field = reader.GetInt32(0); + var count = reader.GetInt32(1); + var pieceName = ArmorChest.IDName[field]; + if (fieldCounts.ContainsKey(pieceName)) + continue; + fieldCounts.Add(pieceName, count); } } } - // Get quest ID with the most total carts - if (questTotalCarts.Count > 0) - { - var mostCompletedQuestId = questTotalCarts.Aggregate((x, y) => x.Value > y.Value ? x : y).Key; - var totalCartsInMostCompletedQuest = questTotalCarts[mostCompletedQuestId]; - - questCompendium.MostCompletedQuestWithCartsQuestID = mostCompletedQuestId; - questCompendium.MostCompletedQuestWithCarts = totalCartsInMostCompletedQuest; - } - else - { - // No quest entries with non-empty carts dictionaries found - questCompendium.MostCompletedQuestWithCartsQuestID = 0; - questCompendium.MostCompletedQuestWithCarts = 0; - } - - questCompendium.PercentOfSoloQuests = GetSoloQuestsPercentage(conn); - questCompendium.QuestPartySizeAverage = GetAvgValue("PartySize", "Quests", conn); - questCompendium.QuestPartySizeMedian = GetMedianValue("PartySize", "Quests", conn); - questCompendium.QuestPartySizeMode = GetModeValue("PartySize", "Quests", conn); - questCompendium.PercentOfGuildFood = GetNonZeroPercentageOfField("GuildFoodID", "PlayerGear", conn); - questCompendium.MostCommonGuildFood = GetModeValue("GuildFoodID", "PlayerGear", conn); - questCompendium.MostCommonDivaSkill = GetModeValue("DivaSkillID", "PlayerGear", conn); - questCompendium.PercentOfDivaSkill = GetNonZeroPercentageOfField("DivaSkillID", "PlayerGear", conn); + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } - // Query to get the item IDs for each player gear row - query = @" - SELECT - PlayerInventoryID, - Item1ID, - Item2ID, - Item3ID, - Item4ID, - Item5ID, - Item6ID, - Item7ID, - Item8ID, - Item9ID, - Item10ID, - Item11ID, - Item12ID, - Item13ID, - Item14ID, - Item15ID, - Item16ID, - Item17ID, - Item18ID, - Item19ID, - Item20ID - FROM PlayerInventory"; + return fieldCounts; + } - // Create a list to hold the PlayerInventoryItemIds objects - var playerInventoryItemIdsList = new List(); + public Dictionary GetMostCommonArmsPieces() + { + Dictionary fieldCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common arms pieces. dataSource: {0}", this.dataSource); + return fieldCounts; + } - // Loop through the query results and create a PlayerInventoryItemIds object for each row - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try + { + var sql = + @"SELECT + ArmsID, + COUNT(*) as count + FROM + PlayerGear + GROUP BY + ArmsID + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { - var playerInventoryID = (long)reader["PlayerInventoryID"]; - var itemIds = new List(); - - for (var i = 1; i <= 20; i++) - { - var itemId = (long)reader[$"Item{i}ID"]; - itemIds.Add(itemId); - } - - var playerInventoryItemIds = new PlayerInventoryItemIds - { - PlayerInventoryID = playerInventoryID, - ItemIds = itemIds, - }; - - playerInventoryItemIdsList.Add(playerInventoryItemIds); + var field = reader.GetInt32(0); + var count = reader.GetInt32(1); + var pieceName = ArmorArms.IDName[field]; + fieldCounts.Add(pieceName, count); } } } - // Loop through the PlayerInventoryItemIds objects and check if any of them have a skill fruit item ID - var skillFruitUsageCount = 0; - foreach (var playerInventoryItemIds in playerInventoryItemIdsList) - { - if (playerInventoryItemIds.ItemIds == null) - { - continue; - } + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } - foreach (var itemId in playerInventoryItemIds.ItemIds) + return fieldCounts; + } + + public Dictionary GetMostCommonWaistPieces() + { + Dictionary fieldCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common waist pieces. dataSource: {0}", this.dataSource); + return fieldCounts; + } + + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try + { + var sql = + @"SELECT + WaistID, + COUNT(*) as count + FROM + PlayerGear + GROUP BY + WaistID + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) + { + using (var reader = cmd.ExecuteReader()) { - if (SkillFruits.ItemID.ContainsKey(itemId)) + while (reader.Read()) { - skillFruitUsageCount++; - break; + var field = reader.GetInt32(0); + var count = reader.GetInt32(1); + var pieceName = ArmorWaist.IDName[field]; + fieldCounts.Add(pieceName, count); } } } - // Calculate the percentage of skill fruit usage - var skillFruitUsagePercentage = skillFruitUsageCount * 100.0 / GetTableRowCount("RunID", "Quests", conn); - - questCompendium.PercentOfSkillFruit = skillFruitUsagePercentage; - transaction.Commit(); } catch (Exception ex) @@ -12544,952 +12978,1066 @@ FROM Quests } } - return questCompendium; + return fieldCounts; } - // TODO: i still need to reorganize all regions. ideally i put in separate classes/files. maybe a DatabaseHelper class? - - /// - /// Helper function to calculate the median of a list of integers. - /// - /// - /// - private static double CalculateMedianOfList(List values) + public Dictionary GetMostCommonLegsPieces() { - var count = values.Count; - if (count % 2 == 0) + Dictionary fieldCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) { - // Even number of values, average the middle two - var middleIndex = count / 2; - return (values[middleIndex - 1] + values[middleIndex]) / 2.0; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common legs pieces. dataSource: {0}", this.dataSource); + return fieldCounts; } - else + + using (var conn = new SQLiteConnection(this.dataSource)) { - // Odd number of values, return the middle value - var middleIndex = (count - 1) / 2; - return values[middleIndex]; - } - } - - /// - /// Gets the count of int value. - /// - /// Name of the field. - /// Name of the table. - /// The value. - /// The connection. - /// - private static long GetCountOfIntValue(string fieldName, string tableName, int value, SQLiteConnection conn) - { - long count = 0; - var query = $"SELECT COUNT(*) FROM {tableName} WHERE {fieldName} = @value"; - using (var cmd = new SQLiteCommand(query, conn)) - { - cmd.Parameters.AddWithValue("@value", value); - count = Convert.ToInt64(cmd.ExecuteScalar(), CultureInfo.InvariantCulture); - } - - return count; - } - - /// - /// Gets the average of field. - /// - /// Name of the table. - /// Name of the field. - /// The connection. - /// - private static double GetAverageOfDictionaryField(string tableName, string fieldName, SQLiteConnection conn) - { - var query = $"SELECT {fieldName} FROM {tableName}"; - - double sumOfValues = 0; - var numberOfEntries = 0; - - using (var cmd = new SQLiteCommand(query, conn)) - { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - while (reader.Read()) + try { - var valueDictionaryString = (string)reader[fieldName]; - - if (string.IsNullOrEmpty(valueDictionaryString) || valueDictionaryString == "{}") - { - continue; - } - - var valueDictionary = JObject.Parse(valueDictionaryString) - .ToObject>(); - - if (valueDictionary == null) + var sql = + @"SELECT + LegsID, + COUNT(*) as count + FROM + PlayerGear + GROUP BY + LegsID + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { - return sumOfValues; + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var field = reader.GetInt32(0); + var count = reader.GetInt32(1); + var pieceName = ArmorLegs.IDName[field]; + fieldCounts.Add(pieceName, count); + } + } } - var averageOfValueValues = valueDictionary.Values.Average(); - sumOfValues += averageOfValueValues; - numberOfEntries++; + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } } - if (numberOfEntries == 0) - { - return 0; // Or any other default value - } - - var averageValue = sumOfValues / numberOfEntries; - - return averageValue; + return fieldCounts; } - /// - /// Gets the median of field. - /// - /// Name of the table. - /// Name of the field. - /// The connection. - /// - private static double GetMedianOfDictionaryField(string tableName, string fieldName, SQLiteConnection conn) + public Dictionary GetMostCommonDivaSkill() { - var query = $"SELECT {fieldName} FROM {tableName}"; - var valueList = new List(); - var medianValue = 0.0; + Dictionary fieldCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common diva skill. dataSource: {0}", this.dataSource); + return fieldCounts; + } - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - while (reader.Read()) + try { - var valueDictionaryString = (string)reader[fieldName]; - if (string.IsNullOrEmpty(valueDictionaryString) || valueDictionaryString == "{}") - { - continue; - } - - var valueDictionary = JObject.Parse(valueDictionaryString).ToObject>(); - if (valueDictionary == null) + var sql = + @"SELECT + DivaSkillID, + COUNT(*) as count + FROM + PlayerGear + GROUP BY + DivaSkillID + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { - return medianValue; + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var field = reader.GetInt32(0); + var count = reader.GetInt32(1); + var name = SkillDiva.IDName[field]; + fieldCounts.Add(name, count); + } + } } - valueList.AddRange(valueDictionary.Values); + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } } - if (valueList.Count > 0) - { - valueList.Sort(); - medianValue = valueList.Count % 2 == 0 - ? (valueList[valueList.Count / 2] + valueList[(valueList.Count / 2) - 1]) / 2.0 - : valueList[valueList.Count / 2]; - } - - return medianValue; + return fieldCounts; } - /// - /// Gets the mode of field. - /// - /// Name of the table. - /// Name of the field. - /// The connection. - /// - private static double GetModeOfDictionaryField(string tableName, string fieldName, SQLiteConnection conn) + public Dictionary GetMostCommonGuildFood() { - double modeValue = 0; - var maxCount = 0; - var query = $"SELECT {fieldName} FROM {tableName}"; - var valueDictionary = new Dictionary(); + Dictionary fieldCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common guild food. dataSource: {0}", this.dataSource); + return fieldCounts; + } - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - while (reader.Read()) + try { - var valueDictionaryString = (string)reader[fieldName]; - - if (string.IsNullOrEmpty(valueDictionaryString) || valueDictionaryString == "{}") - { - continue; - } - - var dict = JObject.Parse(valueDictionaryString).ToObject>(); - if (dict == null) - { - return modeValue; - } - - foreach (var value in dict.Values) + var sql = + @"SELECT + GuildFoodID, + COUNT(*) as count + FROM + PlayerGear + GROUP BY + GuildFoodID + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { - if (valueDictionary.ContainsKey(value)) - { - valueDictionary[value]++; - } - else + using (var reader = cmd.ExecuteReader()) { - valueDictionary.Add(value, 1); + while (reader.Read()) + { + var field = reader.GetInt32(0); + var count = reader.GetInt32(1); + var name = SkillArmor.IDName[field]; + fieldCounts.Add(name, count); + } } } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } } - foreach (var kvp in valueDictionary) + return fieldCounts; + } + + public Dictionary GetMostCommonRankBands() + { + Dictionary fieldCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) { - if (kvp.Value > maxCount) - { - modeValue = kvp.Key; - maxCount = kvp.Value; - } + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common rank bands. dataSource: {0}", this.dataSource); + return fieldCounts; } - return modeValue; - } - - /// - /// Gets the row with highest value. - /// - /// Name of the table. - /// Name of the field. - /// The connection. - /// - private static (double, long) GetRowWithHighestDictionaryValue(string tableName, string fieldName, SQLiteConnection conn) - { - var query = $"SELECT {fieldName}, RunID FROM {tableName}"; - - double highestValue = 0; - long highestValueRunID = 0; - - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - while (reader.Read()) + try { - var runID = (long)reader["RunID"]; - var valueDictionaryString = (string)reader[fieldName]; - - if (string.IsNullOrEmpty(valueDictionaryString) || valueDictionaryString == "{}") - { - continue; - } - - var valueDictionary = JObject.Parse(valueDictionaryString) - .ToObject>(); - - if (valueDictionary == null) + var sql = + @"SELECT + RankName, + COUNT(*) as count + FROM + Quests + GROUP BY + RankName + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { - return (highestValue, highestValueRunID); + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var field = reader.GetString(0); + var count = reader.GetInt32(1); + fieldCounts.Add(field, count); + } + } } - var maxValueInField = valueDictionary.Values.Max(); - - if (maxValueInField > highestValue) - { - highestValue = maxValueInField; - highestValueRunID = runID; - } + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } } - return (highestValue, highestValueRunID); + return fieldCounts; } - /// - /// Gets the quest with highest hits taken blocked. - /// - /// The connection. - /// - private static (double, long) GetQuestWithHighestHitsTakenBlocked(SQLiteConnection conn) + public Dictionary GetMostCommonObjectives() { - var query = "SELECT RunID, HitsTakenBlockedDictionary FROM Quests"; - long highestHitsTakenBlockedRunID = 0; - var highestHitsTakenBlockedCount = 0; + Dictionary fieldCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common objectives. dataSource: {0}", this.dataSource); + return fieldCounts; + } - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - while (reader.Read()) + try { - var runID = (long)reader["RunID"]; - var hitsTakenBlockedDictionaryString = (string)reader["HitsTakenBlockedDictionary"]; - - if (string.IsNullOrEmpty(hitsTakenBlockedDictionaryString) || hitsTakenBlockedDictionaryString == "{}") - { - continue; - } - - var hitsTakenBlocked = JObject.Parse(hitsTakenBlockedDictionaryString) -.ToObject>>(); - if (hitsTakenBlocked == null) + var sql = + @"SELECT + ObjectiveName, + COUNT(*) as count + FROM + Quests + GROUP BY + ObjectiveName + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { - return (0.0, 0); + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var field = reader.GetString(0); + var count = reader.GetInt32(1); + fieldCounts.Add(field, count); + } + } } - var hitsTakenBlockedCount = hitsTakenBlocked.Count; - if (hitsTakenBlockedCount > highestHitsTakenBlockedCount) - { - highestHitsTakenBlockedCount = hitsTakenBlockedCount; - highestHitsTakenBlockedRunID = runID; - } + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } } - return (highestHitsTakenBlockedCount, highestHitsTakenBlockedRunID); + return fieldCounts; } - /// - /// Gets the average hits taken blocked count. - /// - /// The connection. - /// - private static double GetAverageHitsTakenBlockedCount(SQLiteConnection conn) + public Dictionary GetMostCommonSetNames() { - var totalQuestRunsQuery = "SELECT COUNT(*) as TotalQuestRuns FROM Quests"; - var hitsTakenBlockedCountQuery = "SELECT HitsTakenBlockedDictionary FROM Quests"; - double sumOfHitsTakenBlockedCount = 0; - var totalQuestRuns = 0; - - using (var cmd = new SQLiteCommand(totalQuestRunsQuery, conn)) + Dictionary fieldCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) { - totalQuestRuns = Convert.ToInt32(cmd.ExecuteScalar(), CultureInfo.InvariantCulture); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common set names. dataSource: {0}", this.dataSource); + return fieldCounts; } - using (var cmd = new SQLiteCommand(hitsTakenBlockedCountQuery, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - while (reader.Read()) + try { - var hitsTakenBlockedDictionaryString = (string)reader["HitsTakenBlockedDictionary"]; - - if (string.IsNullOrEmpty(hitsTakenBlockedDictionaryString) || hitsTakenBlockedDictionaryString == "{}") - { - continue; - } - - var hitsTakenBlocked = JObject.Parse(hitsTakenBlockedDictionaryString) - .ToObject>>(); - if (hitsTakenBlocked == null) + var sql = + @"SELECT + GearName, + COUNT(*) as count + FROM + PlayerGear + GROUP BY + GearName + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { - return 0; + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var field = reader.GetString(0); + var count = reader.GetInt32(1); + fieldCounts.Add(field, count); + } + } } - var hitsTakenBlockedCount = hitsTakenBlocked.Count; - sumOfHitsTakenBlockedCount += hitsTakenBlockedCount; + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } } - var averageHitsTakenBlockedCount = sumOfHitsTakenBlockedCount / totalQuestRuns; - return averageHitsTakenBlockedCount; + return fieldCounts; } - /// - /// Gets the median hits taken blocked count. - /// - /// The connection. - /// - private static double GetMedianHitsTakenBlockedCount(SQLiteConnection conn) + public Dictionary GetQuestsCompletedByDate() { - var hitsTakenBlockedCountQuery = "SELECT HitsTakenBlockedDictionary FROM Quests"; - var hitsTakenBlockedCountList = new List(); - var medianHitsTakenBlockedCount = 0.0; + Dictionary questsCompletedByDate = new (); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quests completed by date. dataSource: {0}", this.dataSource); + return questsCompletedByDate; + } - using (var cmd = new SQLiteCommand(hitsTakenBlockedCountQuery, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - while (reader.Read()) + try { - var hitsTakenBlockedDictionaryString = (string)reader["HitsTakenBlockedDictionary"]; - - if (string.IsNullOrEmpty(hitsTakenBlockedDictionaryString) || hitsTakenBlockedDictionaryString == "{}") - { - continue; - } - - var hitsTakenBlocked = JObject.Parse(hitsTakenBlockedDictionaryString) - .ToObject>>(); - - if (hitsTakenBlocked == null) + var sql = + @"SELECT + DATE(CreatedAt) as DateOnly, + COUNT(*) as completions + FROM + Quests + GROUP BY + DateOnly + ORDER BY DateOnly ASC"; + using (var cmd = new SQLiteCommand(sql, conn)) { - return medianHitsTakenBlockedCount; + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var date = reader.GetDateTime(0); + var completions = reader.GetInt32(1); + questsCompletedByDate.Add(date, completions); + } + } } - var hitsTakenBlockedCount = hitsTakenBlocked.Count; - hitsTakenBlockedCountList.Add(hitsTakenBlockedCount); + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } } - hitsTakenBlockedCountList.Sort(); - var count = hitsTakenBlockedCountList.Count; - - if (count > 0) - { - if (count % 2 == 0) - { - var middle = count / 2; - medianHitsTakenBlockedCount = (hitsTakenBlockedCountList[middle - 1] + hitsTakenBlockedCountList[middle]) / 2.0; - } - else - { - var middle = count / 2; - medianHitsTakenBlockedCount = hitsTakenBlockedCountList[middle]; - } - } - - return medianHitsTakenBlockedCount; + return questsCompletedByDate; } - /// - /// Gets the total count of value in dictionary field. - /// - /// The field. - /// The table. - /// The connection. - /// - private static long GetTotalCountOfValueInDictionaryField(string field, string table, SQLiteConnection conn) + public Dictionary GetMostCommonWeaponNames() { - var query = $"SELECT {field} FROM {table}"; - long totalCount = 0; + Dictionary weaponCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common weapon names. dataSource: {0}", this.dataSource); + return weaponCounts; + } - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - while (reader.Read()) + try { - var valueDictionaryString = (string)reader[field]; - - if (string.IsNullOrEmpty(valueDictionaryString) || valueDictionaryString == "{}") - { - continue; - } - - if (field == "HitsTakenBlockedDictionary" && table == "Quests") + var sql = + @"SELECT + WeaponClassID, + COALESCE(BlademasterWeaponID, GunnerWeaponID) as WeaponID, + COUNT(*) as Frequency + FROM + PlayerGear + GROUP BY + WeaponID + ORDER BY Frequency DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) { - var dictionary = JObject.Parse(valueDictionaryString) - .ToObject>>(); - if (dictionary == null) + using (var reader = cmd.ExecuteReader()) { - return totalCount; - } + while (reader.Read()) + { + var weaponClassID = reader.GetInt32(0); + var weaponID = reader.GetInt32(1); + var frequency = reader.GetInt32(2); + var weaponName = string.Empty; + if (WeaponBlademaster.IDName.ContainsKey(weaponID) && WeaponClass.IDName[weaponClassID] == "Blademaster") + { + weaponName = WeaponBlademaster.IDName[weaponID]; + } + else if (WeaponGunner.IDName.ContainsKey(weaponID) && WeaponClass.IDName[weaponClassID] == "Gunner") + { + weaponName = WeaponGunner.IDName[weaponID]; + } - totalCount += dictionary.Count; - } - else if (table == "Quests" && (field == "KeystrokesDictionary" || field == "MouseInputDictionary" || field == "GamepadInputDictionary")) - { - var dictionary = JObject.Parse(valueDictionaryString) - .ToObject>(); - if (dictionary == null) - { - return totalCount; + if (!string.IsNullOrEmpty(weaponName)) + { + weaponCounts[weaponName] = frequency; + } + } } - - totalCount += dictionary.Count; } - else - { - var dictionary = JObject.Parse(valueDictionaryString) - .ToObject>(); - if (dictionary == null) - { - return totalCount; - } - totalCount += dictionary.Count; - } + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } } - return totalCount; + return weaponCounts; } - /// - /// Gets the table row count. - /// - /// Name of the field. - /// Name of the table. - /// The connection. - /// - private static long GetTableRowCount(string fieldName, string tableName, SQLiteConnection conn) + public Dictionary GetMostCommonStyleRankSkills() { - var sql = $"SELECT COUNT({fieldName}) FROM {tableName}"; - using (var command = new SQLiteCommand(sql, conn)) + Dictionary skillCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) { - return Convert.ToInt64(command.ExecuteScalar(), CultureInfo.InvariantCulture); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common style rank skills. dataSource: {0}", this.dataSource); + return skillCounts; } - } - /// - /// Gets the count of string value. - /// - /// Name of the field. - /// Name of the table. - /// The value. - /// The connection. - /// - private static long GetCountOfStringValue(string fieldName, string tableName, string value, SQLiteConnection conn) - { - long count = 0; - var query = $"SELECT COUNT(*) FROM {tableName} WHERE {fieldName} = @value"; - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - cmd.Parameters.AddWithValue("@value", value); - count = Convert.ToInt64(cmd.ExecuteScalar(), CultureInfo.InvariantCulture); - } - - return count; - } + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try + { + var sql = + @"SELECT + StyleRankSkill1ID, + COUNT(*) as count + FROM + StyleRankSkills + GROUP BY + StyleRankSkill1ID + UNION + SELECT + StyleRankSkill2ID, + COUNT(*) as count + FROM + StyleRankSkills + GROUP BY + StyleRankSkill2ID + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) + { + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var skillID = reader.GetInt32(0); + var count = reader.GetInt32(1); + var skillName = SkillStyleRank.IDName[skillID]; + if (!skillCounts.ContainsKey(skillName)) + { + skillCounts.Add(skillName, count); + } + else + { + skillCounts[skillName] += count; + } + } + } + } - /// - /// Get the maximum value of a given field in a given table. - /// - /// - /// - /// - /// - private static long GetMaxValue(string field, string table, SQLiteConnection conn) - { - var query = $"SELECT MAX({field}) FROM {table} WHERE {field} IS NOT NULL"; - using (var cmd = new SQLiteCommand(query, conn)) - { - var maxValue = (long)cmd.ExecuteScalar(); - return maxValue; + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } } - } - /// - /// Get the minimum value of a given field in a given table. - /// - /// - /// - /// - /// - private static long GetMinValue(string field, string table, SQLiteConnection conn) - { - var query = $"SELECT MIN({field}) FROM {table} WHERE {field} IS NOT NULL"; - using (var cmd = new SQLiteCommand(query, conn)) - { - var minValue = (long)cmd.ExecuteScalar(); - return minValue; - } + return skillCounts; } - /// - /// Get the average value of a given field in a given table. - /// - /// - /// - /// - /// - private static double GetAvgValue(string field, string table, SQLiteConnection conn) + public Dictionary GetMostCommonCaravanSkills() { - var query = $"SELECT AVG({field}) FROM {table} WHERE {field} IS NOT NULL"; - using (var cmd = new SQLiteCommand(query, conn)) + Dictionary skillCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) { - var result = cmd.ExecuteScalar(); - return result == DBNull.Value ? 0.0 : (double)result; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common caravan skills. dataSource: {0}", this.dataSource); + return skillCounts; } - } - /// - /// Get the median value of a given field in a given table. - /// - /// - /// - /// - /// - private static double GetMedianValue(string field, string table, SQLiteConnection conn) - { - var query = $"SELECT {field} FROM {table} WHERE {field} IS NOT NULL ORDER BY {field}"; - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - if (!reader.HasRows) + try { - return 0.0; - } + var sql = + @"SELECT + CaravanSkill1ID, + COUNT(*) as count + FROM + CaravanSkills + GROUP BY + CaravanSkill1ID + UNION + SELECT + CaravanSkill2ID, + COUNT(*) as count + FROM + CaravanSkills + GROUP BY + CaravanSkill2ID + UNION + SELECT + CaravanSkill3ID, + COUNT(*) as count + FROM + CaravanSkills + GROUP BY + CaravanSkill3ID + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) + { + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var skillID = reader.GetInt32(0); + var count = reader.GetInt32(1); + var skillName = SkillCaravan.IDName[skillID]; + if (!skillCounts.ContainsKey(skillName)) + { + skillCounts.Add(skillName, count); + } + else + { + skillCounts[skillName] += count; + } + } + } + } - var values = new List(); - while (reader.Read()) + transaction.Commit(); + } + catch (Exception ex) { - values.Add(reader.GetInt64(0)); + HandleError(transaction, ex); } - - var valuesArray = values.ToArray(); - Array.Sort(valuesArray); - var count = valuesArray.Length; - var medianValue = count % 2 == 0 ? (valuesArray[count / 2] + (double)valuesArray[(count / 2) - 1]) / 2 : valuesArray[count / 2]; - return medianValue; } } + + return skillCounts; } - /// - /// Get the mode value of a given field in a given table. - /// - /// - /// - /// - /// - private static long GetModeValue(string field, string table, SQLiteConnection conn) + public Dictionary GetMostCommonPartySize() { - var query = $"SELECT {field}, COUNT(*) as count FROM {table} WHERE {field} IS NOT NULL GROUP BY {field} ORDER BY count DESC LIMIT 1"; - using (var cmd = new SQLiteCommand(query, conn)) + Dictionary fieldCounts = new (); + if (string.IsNullOrEmpty(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get most common party size. dataSource: {0}", this.dataSource); + return fieldCounts; + } + + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - if (reader.Read()) + try { - return reader.GetInt64(0); + var sql = + @"SELECT + PartySize, + COUNT(*) as count + FROM + Quests + GROUP BY + PartySize + ORDER BY count DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) + { + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var field = reader.GetInt32(0); + var count = reader.GetInt32(1); + fieldCounts.Add(field, count); + } + } + } + + transaction.Commit(); } - else + catch (Exception ex) { - return 0; + HandleError(transaction, ex); } } } + + return fieldCounts; } /// - /// Gets the maximum value with WHERE clause. + /// Gets the total time spent in quests in descending order. /// - /// The field. - /// The table. - /// The connection. - /// The where field. - /// The where value. /// - private static long GetMaxValueWithWhere(string field, string table, SQLiteConnection conn, string whereField, long whereValue) + public Dictionary GetTotalTimeSpentInQuests() { - var query = $"SELECT MAX({field}) FROM {table} WHERE {whereField} = @whereValue"; - using (var command = new SQLiteCommand(query, conn)) + Dictionary questTimeSpent = new (); + if (string.IsNullOrEmpty(this.dataSource)) { - command.Parameters.AddWithValue("@whereValue", whereValue); - var result = command.ExecuteScalar(); - return result == DBNull.Value ? 0 : (long)result; + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get total time spent in quests. dataSource: {0}", this.dataSource); + return questTimeSpent; } - } - - /// - /// Gets the total unique armor pieces. - /// - /// The connection. - /// - private static long GetTotalUniqueArmorPieces(SQLiteConnection conn) - { - long totalUniqueArmorPieces = 0; - - var query = @" - SELECT - COUNT(DISTINCT HeadID) + - COUNT(DISTINCT ChestID) + - COUNT(DISTINCT ArmsID) + - COUNT(DISTINCT WaistID) + - COUNT(DISTINCT LegsID) AS TotalUniqueArmorPieces - FROM PlayerGear - "; - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - while (reader.Read()) + try { - totalUniqueArmorPieces += (long)reader["TotalUniqueArmorPieces"]; + var sql = + @"SELECT + QuestID, + TOTAL(FinalTimeValue) as timeSpent + FROM + Quests + GROUP BY + QuestID + ORDER BY timeSpent DESC"; + using (var cmd = new SQLiteCommand(sql, conn)) + { + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var questID = reader.GetInt32(0); + var timeSpent = Convert.ToInt32(reader.GetDouble(1)); + questTimeSpent.Add(questID, timeSpent); + } + } + } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } } - return totalUniqueArmorPieces; + return questTimeSpent; } - /// - /// Gets the total unique weapon i ds. - /// - /// The connection. - /// - private static long GetTotalUniqueWeaponIDs(SQLiteConnection conn) + public void InsertMezFesMinigameScore(DataLoader dataLoader, int previousMezFesArea, int previousMezFesScore) { - long totalUniqueWeaponIDs = 0; + if (!MezFesMinigames.ID.ContainsKey(previousMezFesArea) || previousMezFesScore <= 0) + { + Logger.Error(CultureInfo.InvariantCulture, "Wrong MezFes area or empty score. Area ID: {0}, Score: {1}", previousMezFesArea, previousMezFesScore); + return; + } - var query = @" - SELECT - COUNT(DISTINCT BlademasterWeaponID) + - COUNT(DISTINCT GunnerWeaponID) AS TotalUniqueWeaponIDs - FROM PlayerGear - "; + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot insert MezFes minigame score. dataSource: {0}", this.dataSource); + return; + } - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - while (reader.Read()) + try { - totalUniqueWeaponIDs += (long)reader["TotalUniqueWeaponIDs"]; - } - } - } + var sql = @"INSERT INTO MezFes ( + CreatedAt, + CreatedBy, + MezFesMinigameID, + Score + ) VALUES ( + @CreatedAt, + @CreatedBy, + @MezFesMinigameID, + @Score)"; - return totalUniqueWeaponIDs; - } + using (var cmd = new SQLiteCommand(sql, conn)) + { + var createdAt = DateTime.UtcNow; + var createdBy = ViewModels.Windows.AddressModel.GetFullCurrentProgramVersion(); - /// - /// Gets the total unique decorations. Does not count weapon slots, as those are meant to use sigils. - /// - /// The connection. - /// - private static long GetTotalUniqueDecorations(SQLiteConnection conn) - { - long totalUniqueDecorations = 0; + cmd.Parameters.AddWithValue("@CreatedAt", createdAt); + cmd.Parameters.AddWithValue("@CreatedBy", createdBy); + cmd.Parameters.AddWithValue("@MezFesMinigameID", previousMezFesArea); + cmd.Parameters.AddWithValue("@Score", previousMezFesScore); - var query = @" - SELECT - COUNT(DISTINCT HeadSlot1ID) + - COUNT(DISTINCT HeadSlot2ID) + - COUNT(DISTINCT HeadSlot3ID) + - COUNT(DISTINCT ChestSlot1ID) + - COUNT(DISTINCT ChestSlot2ID) + - COUNT(DISTINCT ChestSlot3ID) + - COUNT(DISTINCT ArmsSlot1ID) + - COUNT(DISTINCT ArmsSlot2ID) + - COUNT(DISTINCT ArmsSlot3ID) + - COUNT(DISTINCT WaistSlot1ID) + - COUNT(DISTINCT WaistSlot2ID) + - COUNT(DISTINCT WaistSlot3ID) + - COUNT(DISTINCT LegsSlot1ID) + - COUNT(DISTINCT LegsSlot2ID) + - COUNT(DISTINCT LegsSlot3ID) AS TotalUniqueDecorations - FROM PlayerGear - "; + cmd.ExecuteNonQuery(); + } - using (var cmd = new SQLiteCommand(query, conn)) - { - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) + transaction.Commit(); + } + catch (Exception ex) { - totalUniqueDecorations += (long)reader["TotalUniqueDecorations"]; + HandleError(transaction, ex); } } } - - return totalUniqueDecorations; } /// - /// Gets the most common decoration identifier. + /// Gets the total quest time elapsed in frames. /// /// The connection. /// - private static long GetMostCommonDecorationID(SQLiteConnection conn) + public long GetTotalQuestTimeElapsed() { - Dictionary decorationCounts = new (); + long totalTimeElapsed = 0; - var query = @" - SELECT HeadSlot1ID, HeadSlot2ID, HeadSlot3ID, - ChestSlot1ID, ChestSlot2ID, ChestSlot3ID, - ArmsSlot1ID, ArmsSlot2ID, ArmsSlot3ID, - WaistSlot1ID, WaistSlot2ID, WaistSlot3ID, - LegsSlot1ID, LegsSlot2ID, LegsSlot3ID - FROM PlayerGear - "; + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get total frames elapsed in quests. dataSource: {0}", this.dataSource); + return totalTimeElapsed; + } - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + + using (var transaction = conn.BeginTransaction()) { - while (reader.Read()) + try { - long?[] decorationIDs = - { - reader.IsDBNull(0) ? null : reader.GetInt64(0), - reader.IsDBNull(1) ? null : reader.GetInt64(1), - reader.IsDBNull(2) ? null : reader.GetInt64(2), - reader.IsDBNull(3) ? null : reader.GetInt64(3), - reader.IsDBNull(4) ? null : reader.GetInt64(4), - reader.IsDBNull(5) ? null : reader.GetInt64(5), - reader.IsDBNull(6) ? null : reader.GetInt64(6), - reader.IsDBNull(7) ? null : reader.GetInt64(7), - reader.IsDBNull(8) ? null : reader.GetInt64(8), - reader.IsDBNull(9) ? null : reader.GetInt64(9), - reader.IsDBNull(10) ? null : reader.GetInt64(10), - reader.IsDBNull(11) ? null : reader.GetInt64(11), - reader.IsDBNull(12) ? null : reader.GetInt64(12), - reader.IsDBNull(13) ? null : reader.GetInt64(13), - }; + var query = @"SELECT TOTAL(FinalTimeValue) AS TotalTimeElapsed FROM Quests"; - foreach (var decorationID in decorationIDs) + using (var cmd = new SQLiteCommand(query, conn)) { - if (decorationID.HasValue && decorationID.Value != 0) + using (var reader = cmd.ExecuteReader()) { - if (decorationCounts.ContainsKey(decorationID.Value)) - { - decorationCounts[decorationID.Value]++; - } - else + if (reader.Read()) { - decorationCounts[decorationID.Value] = 1; + totalTimeElapsed = Convert.ToInt64(reader["TotalTimeElapsed"], CultureInfo.InvariantCulture); } } } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); } } } - var mostCommonDecorationID = decorationCounts.OrderByDescending(x => x.Value).Select(x => (long?)x.Key).FirstOrDefault(); - - return mostCommonDecorationID == null ? 0 : (long)mostCommonDecorationID; + return totalTimeElapsed; } - private static long GetLeastUsedArmorSkillID(SQLiteConnection conn) + public QuestCompendium GetQuestCompendium() { - long leastUsedArmorSkillID = 0; - - var query = @" - SELECT ActiveSkill1ID, ActiveSkill2ID, ActiveSkill3ID, ActiveSkill4ID, ActiveSkill5ID, - ActiveSkill6ID, ActiveSkill7ID, ActiveSkill8ID, ActiveSkill9ID, ActiveSkill10ID, - ActiveSkill11ID, ActiveSkill12ID, ActiveSkill13ID, ActiveSkill14ID, ActiveSkill15ID, - ActiveSkill16ID, ActiveSkill17ID, ActiveSkill18ID, ActiveSkill19ID - FROM ActiveSkills - "; - - Dictionary skillCounts = new (); + var questCompendium = new QuestCompendium(); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get quest compendium. dataSource: {0}", this.dataSource); + return questCompendium; + } - using (var cmd = new SQLiteCommand(query, conn)) + using (var conn = new SQLiteConnection(this.dataSource)) { - using (var reader = cmd.ExecuteReader()) + conn.Open(); + using (var transaction = conn.BeginTransaction()) { - while (reader.Read()) + try { - for (var i = 0; i < reader.FieldCount; i++) - { - var skillID = reader.GetInt64(i); + // TODO i guess this works? + (questCompendium.MostCompletedQuestRuns, questCompendium.MostCompletedQuestRunsQuestID) = GetMostCompletedQuestRun(conn); + questCompendium.MostCompletedQuestRunsAttempted = GetMostCompletedQuestRunsAttempted(conn, questCompendium.MostCompletedQuestRunsQuestID); + (questCompendium.MostAttemptedQuestRuns, questCompendium.MostAttemptedQuestRunsQuestID) = GetMostAttemptedQuestRun(conn); + questCompendium.MostAttemptedQuestRunsCompleted = GetMostAttemptedQuestRunsCompleted(conn, questCompendium.MostAttemptedQuestRunsQuestID); + questCompendium.TotalQuestsCompleted = GetTableRowCount("RunID", "Quests", conn); + questCompendium.TotalQuestsAttempted = GetSumValue("Attempts", "QuestAttempts", conn); + questCompendium.QuestCompletionTimeElapsedAverage = GetAvgValue("FinalTimeValue", "Quests", conn); + questCompendium.QuestCompletionTimeElapsedMedian = GetMedianValue("FinalTimeValue", "Quests", conn); - if (skillID != 0) + var query = @"SELECT TOTAL(FinalTimeValue) AS TotalTimeElapsed + FROM Quests + "; + + using (var cmd = new SQLiteCommand(query, conn)) + { + using (var reader = cmd.ExecuteReader()) { - if (skillCounts.ContainsKey(skillID)) + if (reader.Read()) { - skillCounts[skillID]++; + var value = Convert.ToInt64(reader["TotalTimeElapsed"], CultureInfo.InvariantCulture); + + questCompendium.TotalTimeElapsedQuests = value; } - else + } + } + + // Initialize a list to hold the final cart values + var finalCartValues = new List(); + + // Query to get the last cart value from each quest entry + query = @"SELECT CartsDictionary FROM Quests WHERE CartsDictionary IS NOT NULL"; + + using (var cmd = new SQLiteCommand(query, conn)) + { + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) { - skillCounts[skillID] = 1; + var cartsDictionaryJson = reader.GetString(0); + if (!string.IsNullOrEmpty(cartsDictionaryJson) && cartsDictionaryJson != "{}") + { + // Deserialize the carts dictionary JSON string + var cartsDictionary = JsonConvert.DeserializeObject>(cartsDictionaryJson); + + if (cartsDictionary != null) + { + // Get the last cart value from the dictionary + var finalCartValue = cartsDictionary.Values.LastOrDefault(); + + // Add the final cart value to the list + finalCartValues.Add(finalCartValue); + } + } } } } - } - } - } - leastUsedArmorSkillID = skillCounts.OrderBy(x => x.Value).Select(x => x.Key).FirstOrDefault(); + // Calculate the average and median final cart values + if (finalCartValues.Count > 0) + { + questCompendium.TotalCartsInQuestAverage = finalCartValues.Average(); + questCompendium.TotalCartsInQuestMedian = CalculateMedianOfList(finalCartValues); + } + else + { + // No quest entries with non-empty carts dictionaries found + questCompendium.TotalCartsInQuestAverage = 0; + questCompendium.TotalCartsInQuestMedian = 0; + } - return leastUsedArmorSkillID; - } + questCompendium.TotalCartsInQuest = finalCartValues.Sum(); - /// - /// Gets the minimum value with WHERE clause. - /// - /// The field. - /// The table. - /// The connection. - /// The where field. - /// The where value. - /// - private static long GetMinValueWithWhere(string field, string table, SQLiteConnection conn, string whereField, long whereValue) - { - var query = $"SELECT MIN({field}) FROM {table} WHERE {whereField} = @whereValue"; - using (var command = new SQLiteCommand(query, conn)) - { - command.Parameters.AddWithValue("@whereValue", whereValue); - var result = command.ExecuteScalar(); - return result == DBNull.Value ? 0 : (long)result; - } - } + // Initialize dictionary to hold the total carts for each quest ID + Dictionary questTotalCarts = new (); - /// - /// Gets the average value with WHERE clause. - /// - /// The field. - /// The table. - /// The connection. - /// The where field. - /// The where value. - /// - private static double GetAverageValueWithWhere(string field, string table, SQLiteConnection conn, string whereField, long whereValue) - { - var query = $"SELECT AVG({field}) FROM {table} WHERE {whereField} = @whereValue"; - using (var command = new SQLiteCommand(query, conn)) - { - command.Parameters.AddWithValue("@whereValue", whereValue); - var result = command.ExecuteScalar(); - return result == DBNull.Value ? 0.0 : (double)result; - } - } + // Query to get carts dictionary for all quests with non-empty carts dictionary + query = @"SELECT QuestId, CartsDictionary FROM Quests WHERE CartsDictionary IS NOT NULL AND CartsDictionary != '{}'"; + + using (var cmd = new SQLiteCommand(query, conn)) + { + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var questId = Convert.ToInt32(reader["QuestId"], CultureInfo.InvariantCulture); + var cartsDictionaryJson = reader.GetString(1); + + // Deserialize carts dictionary JSON string + var cartsDictionary = JsonConvert.DeserializeObject>(cartsDictionaryJson); + + if (cartsDictionary != null) + { + // Add total carts for this quest to the questTotalCarts dictionary + var totalCarts = cartsDictionary.Values.Sum(); + questTotalCarts[questId] = totalCarts; + } + } + } + } + + // Get quest ID with the most total carts + if (questTotalCarts.Count > 0) + { + var mostCompletedQuestId = questTotalCarts.Aggregate((x, y) => x.Value > y.Value ? x : y).Key; + var totalCartsInMostCompletedQuest = questTotalCarts[mostCompletedQuestId]; + + questCompendium.MostCompletedQuestWithCartsQuestID = mostCompletedQuestId; + questCompendium.MostCompletedQuestWithCarts = totalCartsInMostCompletedQuest; + } + else + { + // No quest entries with non-empty carts dictionaries found + questCompendium.MostCompletedQuestWithCartsQuestID = 0; + questCompendium.MostCompletedQuestWithCarts = 0; + } + + questCompendium.PercentOfSoloQuests = GetSoloQuestsPercentage(conn); + questCompendium.QuestPartySizeAverage = GetAvgValue("PartySize", "Quests", conn); + questCompendium.QuestPartySizeMedian = GetMedianValue("PartySize", "Quests", conn); + questCompendium.QuestPartySizeMode = GetModeValue("PartySize", "Quests", conn); + questCompendium.PercentOfGuildFood = GetNonZeroPercentageOfField("GuildFoodID", "PlayerGear", conn); + questCompendium.MostCommonGuildFood = GetModeValue("GuildFoodID", "PlayerGear", conn); + questCompendium.MostCommonDivaSkill = GetModeValue("DivaSkillID", "PlayerGear", conn); + questCompendium.PercentOfDivaSkill = GetNonZeroPercentageOfField("DivaSkillID", "PlayerGear", conn); + + // Query to get the item IDs for each player gear row + query = @" + SELECT + PlayerInventoryID, + Item1ID, + Item2ID, + Item3ID, + Item4ID, + Item5ID, + Item6ID, + Item7ID, + Item8ID, + Item9ID, + Item10ID, + Item11ID, + Item12ID, + Item13ID, + Item14ID, + Item15ID, + Item16ID, + Item17ID, + Item18ID, + Item19ID, + Item20ID + FROM PlayerInventory"; + + // Create a list to hold the PlayerInventoryItemIds objects + var playerInventoryItemIdsList = new List(); + + // Loop through the query results and create a PlayerInventoryItemIds object for each row + using (var cmd = new SQLiteCommand(query, conn)) + { + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var playerInventoryID = (long)reader["PlayerInventoryID"]; + var itemIds = new List(); + + for (var i = 1; i <= 20; i++) + { + var itemId = (long)reader[$"Item{i}ID"]; + itemIds.Add(itemId); + } + + var playerInventoryItemIds = new PlayerInventoryItemIds + { + PlayerInventoryID = playerInventoryID, + ItemIds = itemIds, + }; + + playerInventoryItemIdsList.Add(playerInventoryItemIds); + } + } + } + + // Loop through the PlayerInventoryItemIds objects and check if any of them have a skill fruit item ID + var skillFruitUsageCount = 0; + foreach (var playerInventoryItemIds in playerInventoryItemIdsList) + { + if (playerInventoryItemIds.ItemIds == null) + { + continue; + } + + foreach (var itemId in playerInventoryItemIds.ItemIds) + { + if (SkillFruits.ItemID.ContainsKey(itemId)) + { + skillFruitUsageCount++; + break; + } + } + } + + // Calculate the percentage of skill fruit usage + var skillFruitUsagePercentage = skillFruitUsageCount * 100.0 / GetTableRowCount("RunID", "Quests", conn); + + questCompendium.PercentOfSkillFruit = skillFruitUsagePercentage; + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } + + return questCompendium; + } + + // TODO: i still need to reorganize all regions. ideally i put in separate classes/files. maybe a DatabaseHelper class? /// - /// Gets the median value with WHERE clause. + /// Helper function to calculate the median of a list of integers. /// - /// The field. - /// The table. - /// The connection. - /// The where field. - /// The where value. + /// /// - private static double GetMedianValueWithWhere(string field, string table, SQLiteConnection conn, string whereField, long whereValue) + private static double CalculateMedianOfList(List values) { - // TODO: not sure if correct - var query = $"SELECT AVG({field}) FROM (SELECT {field}, ROW_NUMBER() OVER (ORDER BY {field}) AS RowNum, COUNT(*) OVER() AS TotalRows FROM {table} WHERE {whereField} = @whereValue) temp WHERE RowNum BETWEEN (TotalRows/2) + 1 AND (TotalRows/2) + 2;"; - using (var command = new SQLiteCommand(query, conn)) + var count = values.Count; + if (count % 2 == 0) { - command.Parameters.AddWithValue("@whereValue", whereValue); - var result = command.ExecuteScalar(); - return result == DBNull.Value ? 0.0 : (double)result; + // Even number of values, average the middle two + var middleIndex = count / 2; + return (values[middleIndex - 1] + values[middleIndex]) / 2.0; + } + else + { + // Odd number of values, return the middle value + var middleIndex = (count - 1) / 2; + return values[middleIndex]; } } /// - /// Get the sum of values in a given field in a given table, excluding null values. + /// Gets the count of int value. /// - /// - /// - /// + /// Name of the field. + /// Name of the table. + /// The value. + /// The connection. /// - private static long GetSumValue(string field, string table, SQLiteConnection conn) + private static long GetCountOfIntValue(string fieldName, string tableName, int value, SQLiteConnection conn) { - var query = $"SELECT TOTAL({field}) FROM {table} WHERE {field} IS NOT NULL"; + long count = 0; + var query = $"SELECT COUNT(*) FROM {tableName} WHERE {fieldName} = @value"; using (var cmd = new SQLiteCommand(query, conn)) { - var sum = Convert.ToInt64(cmd.ExecuteScalar(), CultureInfo.InvariantCulture); - return sum; + cmd.Parameters.AddWithValue("@value", value); + count = Convert.ToInt64(cmd.ExecuteScalar(), CultureInfo.InvariantCulture); } + + return count; } /// - /// Gets the record with highest value in field. + /// Gets the average of field. /// - /// The connection. /// Name of the table. /// Name of the field. + /// The connection. /// - private static (double, long) GetRecordWithHighestValueInField(SQLiteConnection conn, string tableName, string fieldName) + private static double GetAverageOfDictionaryField(string tableName, string fieldName, SQLiteConnection conn) { - var query = $"SELECT RunID, {fieldName} FROM {tableName}"; - long highestValueRunID = 0; - double highestValue = 0; + var query = $"SELECT {fieldName} FROM {tableName}"; + + double sumOfValues = 0; + var numberOfEntries = 0; using (var cmd = new SQLiteCommand(query, conn)) { @@ -13497,46 +14045,50 @@ private static (double, long) GetRecordWithHighestValueInField(SQLiteConnection { while (reader.Read()) { - var runID = (long)reader["RunID"]; - var fieldValueString = (string)reader[fieldName]; + var valueDictionaryString = (string)reader[fieldName]; - if (string.IsNullOrEmpty(fieldValueString) || fieldValueString == "{}") + if (string.IsNullOrEmpty(valueDictionaryString) || valueDictionaryString == "{}") { continue; } - var fieldValue = JObject.Parse(fieldValueString) - .ToObject>>(); - if (fieldValue == null) - { - return (highestValue, highestValueRunID); - } + var valueDictionary = JObject.Parse(valueDictionaryString) + .ToObject>(); - var fieldValueMax = fieldValue.Max(kv1 => kv1.Value.Max(kv2 => kv2.Value)); - if (fieldValueMax > highestValue) + if (valueDictionary == null) { - highestValue = fieldValueMax; - highestValueRunID = runID; + return sumOfValues; } + + var averageOfValueValues = valueDictionary.Values.Average(); + sumOfValues += averageOfValueValues; + numberOfEntries++; } } } - return (highestValue, highestValueRunID); + if (numberOfEntries == 0) + { + return 0; // Or any other default value + } + + var averageValue = sumOfValues / numberOfEntries; + + return averageValue; } /// - /// Gets the record with lowest value in field. + /// Gets the median of field. /// - /// The connection. /// Name of the table. /// Name of the field. + /// The connection. /// - private static (double, long) GetRecordWithLowestValueInField(SQLiteConnection conn, string tableName, string fieldName) + private static double GetMedianOfDictionaryField(string tableName, string fieldName, SQLiteConnection conn) { - var query = $"SELECT RunID, {fieldName} FROM {tableName}"; - long lowestValueRunID = 0; - var lowestValue = double.MaxValue; + var query = $"SELECT {fieldName} FROM {tableName}"; + var valueList = new List(); + var medianValue = 0.0; using (var cmd = new SQLiteCommand(query, conn)) { @@ -13544,1101 +14096,1862 @@ private static (double, long) GetRecordWithLowestValueInField(SQLiteConnection c { while (reader.Read()) { - var runID = (long)reader["RunID"]; - var fieldValueString = (string)reader[fieldName]; - - if (string.IsNullOrEmpty(fieldValueString) || fieldValueString == "{}") + var valueDictionaryString = (string)reader[fieldName]; + if (string.IsNullOrEmpty(valueDictionaryString) || valueDictionaryString == "{}") { continue; } - var fieldValue = JObject.Parse(fieldValueString) - .ToObject>>(); - if (fieldValue == null) + var valueDictionary = JObject.Parse(valueDictionaryString).ToObject>(); + if (valueDictionary == null) { - return (lowestValue, lowestValueRunID); + return medianValue; } - var value = fieldValue.Min(kv1 => kv1.Value.Min(kv2 => kv2.Value)); - if (value < lowestValue) - { - lowestValue = value; - lowestValueRunID = runID; - } + valueList.AddRange(valueDictionary.Values); } } } - return (lowestValue, lowestValueRunID); + if (valueList.Count > 0) + { + valueList.Sort(); + medianValue = valueList.Count % 2 == 0 + ? (valueList[valueList.Count / 2] + valueList[(valueList.Count / 2) - 1]) / 2.0 + : valueList[valueList.Count / 2]; + } + + return medianValue; } /// - /// Gets the most completed quest run. + /// Gets the mode of field. /// + /// Name of the table. + /// Name of the field. /// The connection. /// - private static (long, long) GetMostCompletedQuestRun(SQLiteConnection conn) + private static double GetModeOfDictionaryField(string tableName, string fieldName, SQLiteConnection conn) { - long timesCompleted = 0; - long questId = 0; - - var query = @" - SELECT QuestID, COUNT(*) as TimesCompleted - FROM Quests - GROUP BY QuestID - ORDER BY TimesCompleted DESC - LIMIT 1"; + double modeValue = 0; + var maxCount = 0; + var query = $"SELECT {fieldName} FROM {tableName}"; + var valueDictionary = new Dictionary(); using (var cmd = new SQLiteCommand(query, conn)) { using (var reader = cmd.ExecuteReader()) { - if (reader.Read()) + while (reader.Read()) { - // The most completed quest ID - questId = (long)reader["QuestID"]; + var valueDictionaryString = (string)reader[fieldName]; - // The number of times the quest was completed - timesCompleted = (long)reader["TimesCompleted"]; + if (string.IsNullOrEmpty(valueDictionaryString) || valueDictionaryString == "{}") + { + continue; + } + + var dict = JObject.Parse(valueDictionaryString).ToObject>(); + if (dict == null) + { + return modeValue; + } + + foreach (var value in dict.Values) + { + if (valueDictionary.ContainsKey(value)) + { + valueDictionary[value]++; + } + else + { + valueDictionary.Add(value, 1); + } + } } } } - return (timesCompleted, questId); + foreach (var kvp in valueDictionary) + { + if (kvp.Value > maxCount) + { + modeValue = kvp.Key; + maxCount = kvp.Value; + } + } + + return modeValue; } /// - /// Gets the most completed quest runs attempted. + /// Gets the row with highest value. /// + /// Name of the table. + /// Name of the field. /// The connection. /// - private static long GetMostCompletedQuestRunsAttempted(SQLiteConnection conn, long mostCompletedQuestRunsQuestID) + private static (double, long) GetRowWithHighestDictionaryValue(string tableName, string fieldName, SQLiteConnection conn) { - long attempts = 0; - var query = @" - SELECT TOTAL(Attempts) AS TotalAttempts - FROM QuestAttempts - WHERE QuestID = @questId"; + var query = $"SELECT {fieldName}, RunID FROM {tableName}"; + + double highestValue = 0; + long highestValueRunID = 0; using (var cmd = new SQLiteCommand(query, conn)) { - cmd.Parameters.AddWithValue("@questId", mostCompletedQuestRunsQuestID); using (var reader = cmd.ExecuteReader()) { - if (reader.Read()) + while (reader.Read()) { - attempts = Convert.ToInt64(reader["TotalAttempts"], CultureInfo.InvariantCulture); + var runID = (long)reader["RunID"]; + var valueDictionaryString = (string)reader[fieldName]; + + if (string.IsNullOrEmpty(valueDictionaryString) || valueDictionaryString == "{}") + { + continue; + } + + var valueDictionary = JObject.Parse(valueDictionaryString) + .ToObject>(); + + if (valueDictionary == null) + { + return (highestValue, highestValueRunID); + } + + var maxValueInField = valueDictionary.Values.Max(); + + if (maxValueInField > highestValue) + { + highestValue = maxValueInField; + highestValueRunID = runID; + } } } } - return attempts; + return (highestValue, highestValueRunID); } /// - /// Gets the most attempted quest run. + /// Gets the quest with highest hits taken blocked. /// /// The connection. /// - private static (long, long) GetMostAttemptedQuestRun(SQLiteConnection conn) + private static (double, long) GetQuestWithHighestHitsTakenBlocked(SQLiteConnection conn) { - long questID = 0; - long attempts = 0; - var query = @" - SELECT q.QuestID, TOTAL(q.Attempts) AS Attempts - FROM QuestAttempts q - JOIN ( - SELECT QuestID, COUNT(*) AS TotalAttempts - FROM QuestAttempts - GROUP BY QuestID - ORDER BY TotalAttempts DESC - LIMIT 1 - ) m ON q.QuestID = m.QuestID - GROUP BY q.QuestID"; + var query = "SELECT RunID, HitsTakenBlockedDictionary FROM Quests"; + long highestHitsTakenBlockedRunID = 0; + var highestHitsTakenBlockedCount = 0; using (var cmd = new SQLiteCommand(query, conn)) { using (var reader = cmd.ExecuteReader()) { - if (reader.Read()) + while (reader.Read()) { - questID = (long)reader["QuestID"]; - attempts = Convert.ToInt64(reader["Attempts"], CultureInfo.InvariantCulture); + var runID = (long)reader["RunID"]; + var hitsTakenBlockedDictionaryString = (string)reader["HitsTakenBlockedDictionary"]; + + if (string.IsNullOrEmpty(hitsTakenBlockedDictionaryString) || hitsTakenBlockedDictionaryString == "{}") + { + continue; + } + + var hitsTakenBlocked = JObject.Parse(hitsTakenBlockedDictionaryString) +.ToObject>>(); + if (hitsTakenBlocked == null) + { + return (0.0, 0); + } + + var hitsTakenBlockedCount = hitsTakenBlocked.Count; + if (hitsTakenBlockedCount > highestHitsTakenBlockedCount) + { + highestHitsTakenBlockedCount = hitsTakenBlockedCount; + highestHitsTakenBlockedRunID = runID; + } } } } - return (attempts, questID); + return (highestHitsTakenBlockedCount, highestHitsTakenBlockedRunID); } /// - /// Gets the most attempted quest runs completed. + /// Gets the average hits taken blocked count. /// /// The connection. - /// The most attempted quest runs quest identifier. /// - private static long GetMostAttemptedQuestRunsCompleted(SQLiteConnection conn, long mostAttemptedQuestRunsQuestID) + private static double GetAverageHitsTakenBlockedCount(SQLiteConnection conn) { - long timesCompleted = 0; - - var query = @" - SELECT COUNT(*) AS TimesCompleted - FROM Quests - WHERE QuestID = @QuestID"; + var totalQuestRunsQuery = "SELECT COUNT(*) as TotalQuestRuns FROM Quests"; + var hitsTakenBlockedCountQuery = "SELECT HitsTakenBlockedDictionary FROM Quests"; + double sumOfHitsTakenBlockedCount = 0; + var totalQuestRuns = 0; - using (var cmd = new SQLiteCommand(query, conn)) + using (var cmd = new SQLiteCommand(totalQuestRunsQuery, conn)) { - cmd.Parameters.AddWithValue("@QuestID", mostAttemptedQuestRunsQuestID); + totalQuestRuns = Convert.ToInt32(cmd.ExecuteScalar(), CultureInfo.InvariantCulture); + } + using (var cmd = new SQLiteCommand(hitsTakenBlockedCountQuery, conn)) + { using (var reader = cmd.ExecuteReader()) { - if (reader.Read()) + while (reader.Read()) { - // The number of times the most attempted quest was completed - timesCompleted = (long)reader["TimesCompleted"]; + var hitsTakenBlockedDictionaryString = (string)reader["HitsTakenBlockedDictionary"]; + + if (string.IsNullOrEmpty(hitsTakenBlockedDictionaryString) || hitsTakenBlockedDictionaryString == "{}") + { + continue; + } + + var hitsTakenBlocked = JObject.Parse(hitsTakenBlockedDictionaryString) + .ToObject>>(); + if (hitsTakenBlocked == null) + { + return 0; + } + + var hitsTakenBlockedCount = hitsTakenBlocked.Count; + sumOfHitsTakenBlockedCount += hitsTakenBlockedCount; } } } - return timesCompleted; + var averageHitsTakenBlockedCount = sumOfHitsTakenBlockedCount / totalQuestRuns; + return averageHitsTakenBlockedCount; } /// - /// Takes the field parameter and returns the percentage of non-zero values in that field. + /// Gets the median hits taken blocked count. /// - /// The field. - /// The table. /// The connection. /// - private static double GetNonZeroPercentageOfField(string field, string table, SQLiteConnection conn) + private static double GetMedianHitsTakenBlockedCount(SQLiteConnection conn) { - // Initialize variables to hold the total number of entries and the number of entries with non-zero field values - long totalEntries = 0; - long nonZeroEntries = 0; - - // Query to get the total number of entries and the number of entries with non-zero field values - var query = $"SELECT COUNT(*) AS TotalEntries, COUNT(CASE WHEN {field} != 0 THEN 1 ELSE NULL END) AS NonZeroEntries FROM {table}"; + var hitsTakenBlockedCountQuery = "SELECT HitsTakenBlockedDictionary FROM Quests"; + var hitsTakenBlockedCountList = new List(); + var medianHitsTakenBlockedCount = 0.0; - using (var cmd = new SQLiteCommand(query, conn)) + using (var cmd = new SQLiteCommand(hitsTakenBlockedCountQuery, conn)) { using (var reader = cmd.ExecuteReader()) { - if (reader.Read()) + while (reader.Read()) { - totalEntries = (long)reader["TotalEntries"]; - nonZeroEntries = (long)reader["NonZeroEntries"]; + var hitsTakenBlockedDictionaryString = (string)reader["HitsTakenBlockedDictionary"]; + + if (string.IsNullOrEmpty(hitsTakenBlockedDictionaryString) || hitsTakenBlockedDictionaryString == "{}") + { + continue; + } + + var hitsTakenBlocked = JObject.Parse(hitsTakenBlockedDictionaryString) + .ToObject>>(); + + if (hitsTakenBlocked == null) + { + return medianHitsTakenBlockedCount; + } + + var hitsTakenBlockedCount = hitsTakenBlocked.Count; + hitsTakenBlockedCountList.Add(hitsTakenBlockedCount); } } } - // Calculate the percentage of non-zero values - var percentage = nonZeroEntries * 100.0 / totalEntries; + hitsTakenBlockedCountList.Sort(); + var count = hitsTakenBlockedCountList.Count; - return percentage; + if (count > 0) + { + if (count % 2 == 0) + { + var middle = count / 2; + medianHitsTakenBlockedCount = (hitsTakenBlockedCountList[middle - 1] + hitsTakenBlockedCountList[middle]) / 2.0; + } + else + { + var middle = count / 2; + medianHitsTakenBlockedCount = hitsTakenBlockedCountList[middle]; + } + } + + return medianHitsTakenBlockedCount; } /// - /// Gets the solo quests percentage. + /// Gets the total count of value in dictionary field. /// + /// The field. + /// The table. /// The connection. /// - private static double GetSoloQuestsPercentage(SQLiteConnection conn) + private static long GetTotalCountOfValueInDictionaryField(string field, string table, SQLiteConnection conn) { - // Initialize variables to hold the total number of quests and the number of solo quests - long totalQuests = 0; - long soloQuests = 0; - - // Query to get the total number of quests and the number of solo quests - var query = @" - SELECT COUNT(*) AS TotalQuests, - COUNT(CASE WHEN PartySize = 1 THEN 1 ELSE NULL END) AS SoloQuests - FROM Quests - "; + var query = $"SELECT {field} FROM {table}"; + long totalCount = 0; using (var cmd = new SQLiteCommand(query, conn)) { using (var reader = cmd.ExecuteReader()) { - if (reader.Read()) + while (reader.Read()) { - totalQuests = (long)reader["TotalQuests"]; - soloQuests = (long)reader["SoloQuests"]; + var valueDictionaryString = (string)reader[field]; + + if (string.IsNullOrEmpty(valueDictionaryString) || valueDictionaryString == "{}") + { + continue; + } + + if (field == "HitsTakenBlockedDictionary" && table == "Quests") + { + var dictionary = JObject.Parse(valueDictionaryString) + .ToObject>>(); + if (dictionary == null) + { + return totalCount; + } + + totalCount += dictionary.Count; + } + else if (table == "Quests" && (field == "KeystrokesDictionary" || field == "MouseInputDictionary" || field == "GamepadInputDictionary")) + { + var dictionary = JObject.Parse(valueDictionaryString) + .ToObject>(); + if (dictionary == null) + { + return totalCount; + } + + totalCount += dictionary.Count; + } + else + { + var dictionary = JObject.Parse(valueDictionaryString) + .ToObject>(); + if (dictionary == null) + { + return totalCount; + } + + totalCount += dictionary.Count; + } } } } - // Calculate the percentage of solo quests - return soloQuests * 100.0 / totalQuests; + return totalCount; } - public GearCompendium GetGearCompendium() + /// + /// Gets the table row count. + /// + /// Name of the field. + /// Name of the table. + /// The connection. + /// + private static long GetTableRowCount(string fieldName, string tableName, SQLiteConnection conn) { - var gearCompendium = new GearCompendium(); - if (string.IsNullOrEmpty(this.dataSource)) + var sql = $"SELECT COUNT({fieldName}) FROM {tableName}"; + using (var command = new SQLiteCommand(sql, conn)) { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get gear compendium. dataSource: {0}", this.dataSource); - return gearCompendium; + return Convert.ToInt64(command.ExecuteScalar(), CultureInfo.InvariantCulture); } + } - using (var conn = new SQLiteConnection(this.dataSource)) + /// + /// Gets the count of string value. + /// + /// Name of the field. + /// Name of the table. + /// The value. + /// The connection. + /// + private static long GetCountOfStringValue(string fieldName, string tableName, string value, SQLiteConnection conn) + { + long count = 0; + var query = $"SELECT COUNT(*) FROM {tableName} WHERE {fieldName} = @value"; + using (var cmd = new SQLiteCommand(query, conn)) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) - { - try - { - gearCompendium.MostUsedWeaponType = GetModeValue("WeaponTypeID", "PlayerGear", conn); - - gearCompendium.TotalUniqueArmorPiecesUsed = GetTotalUniqueArmorPieces(conn); + cmd.Parameters.AddWithValue("@value", value); + count = Convert.ToInt64(cmd.ExecuteScalar(), CultureInfo.InvariantCulture); + } - gearCompendium.TotalUniqueWeaponsUsed = GetTotalUniqueWeaponIDs(conn); + return count; + } - gearCompendium.TotalUniqueDecorationsUsed = GetTotalUniqueDecorations(conn); + /// + /// Get the maximum value of a given field in a given table. + /// + /// + /// + /// + /// + private static long GetMaxValue(string field, string table, SQLiteConnection conn) + { + var query = $"SELECT MAX({field}) FROM {table} WHERE {field} IS NOT NULL"; + using (var cmd = new SQLiteCommand(query, conn)) + { + var maxValue = (long)cmd.ExecuteScalar(); + return maxValue; + } + } - gearCompendium.MostCommonDecorationID = GetMostCommonDecorationID(conn); + /// + /// Get the minimum value of a given field in a given table. + /// + /// + /// + /// + /// + private static long GetMinValue(string field, string table, SQLiteConnection conn) + { + var query = $"SELECT MIN({field}) FROM {table} WHERE {field} IS NOT NULL"; + using (var cmd = new SQLiteCommand(query, conn)) + { + var minValue = (long)cmd.ExecuteScalar(); + return minValue; + } + } - gearCompendium.LeastUsedArmorSkill = GetLeastUsedArmorSkillID(conn); + /// + /// Get the average value of a given field in a given table. + /// + /// + /// + /// + /// + private static double GetAvgValue(string field, string table, SQLiteConnection conn) + { + var query = $"SELECT AVG({field}) FROM {table} WHERE {field} IS NOT NULL"; + using (var cmd = new SQLiteCommand(query, conn)) + { + var result = cmd.ExecuteScalar(); + return result == DBNull.Value ? 0.0 : (double)result; + } + } - transaction.Commit(); + /// + /// Get the median value of a given field in a given table. + /// + /// + /// + /// + /// + private static double GetMedianValue(string field, string table, SQLiteConnection conn) + { + var query = $"SELECT {field} FROM {table} WHERE {field} IS NOT NULL ORDER BY {field}"; + using (var cmd = new SQLiteCommand(query, conn)) + { + using (var reader = cmd.ExecuteReader()) + { + if (!reader.HasRows) + { + return 0.0; } - catch (Exception ex) + + var values = new List(); + while (reader.Read()) { - HandleError(transaction, ex); + values.Add(reader.GetInt64(0)); } + + var valuesArray = values.ToArray(); + Array.Sort(valuesArray); + var count = valuesArray.Length; + var medianValue = count % 2 == 0 ? (valuesArray[count / 2] + (double)valuesArray[(count / 2) - 1]) / 2 : valuesArray[count / 2]; + return medianValue; } } - - return gearCompendium; } - public PerformanceCompendium GetPerformanceCompendium() + /// + /// Get the mode value of a given field in a given table. + /// + /// + /// + /// + /// + private static long GetModeValue(string field, string table, SQLiteConnection conn) { - var performanceCompendium = new PerformanceCompendium(); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get performance compendium. dataSource: {0}", this.dataSource); - return performanceCompendium; - } - - using (var conn = new SQLiteConnection(this.dataSource)) + var query = $"SELECT {field}, COUNT(*) as count FROM {table} WHERE {field} IS NOT NULL GROUP BY {field} ORDER BY count DESC LIMIT 1"; + using (var cmd = new SQLiteCommand(query, conn)) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + using (var reader = cmd.ExecuteReader()) { - try + if (reader.Read()) { - (performanceCompendium.HighestTrueRaw, performanceCompendium.HighestTrueRawRunID) = GetRowWithHighestDictionaryValue("Quests", "AttackBuffDictionary", conn); - performanceCompendium.TrueRawAverage = GetAverageOfDictionaryField("Quests", "AttackBuffDictionary", conn); - performanceCompendium.TrueRawMedian = GetMedianOfDictionaryField("Quests", "AttackBuffDictionary", conn); - - (performanceCompendium.HighestActionsPerMinute, performanceCompendium.HighestActionsPerMinuteRunID) = GetRowWithHighestDictionaryValue("Quests", "ActionsPerMinuteDictionary", conn); - performanceCompendium.ActionsPerMinuteAverage = GetAverageOfDictionaryField("Quests", "ActionsPerMinuteDictionary", conn); - performanceCompendium.ActionsPerMinuteMedian = GetMedianOfDictionaryField("Quests", "ActionsPerMinuteDictionary", conn); - - (performanceCompendium.HighestDPS, performanceCompendium.HighestDPSRunID) = GetRowWithHighestDictionaryValue("Quests", "DamagePerSecondDictionary", conn); - performanceCompendium.DPSAverage = GetAverageOfDictionaryField("Quests", "DamagePerSecondDictionary", conn); - performanceCompendium.DPSMedian = GetMedianOfDictionaryField("Quests", "DamagePerSecondDictionary", conn); + return reader.GetInt64(0); + } + else + { + return 0; + } + } + } + } - (performanceCompendium.HighestHitCount, performanceCompendium.HighestHitCountRunID) = GetRowWithHighestDictionaryValue("Quests", "HitCountDictionary", conn); - performanceCompendium.HitCountAverage = GetAverageOfDictionaryField("Quests", "HitCountDictionary", conn); - performanceCompendium.HitCountMedian = GetMedianOfDictionaryField("Quests", "HitCountDictionary", conn); - - (performanceCompendium.HighestHitsPerSecond, performanceCompendium.HighestHitsPerSecondRunID) = GetRowWithHighestDictionaryValue("Quests", "HitsPerSecondDictionary", conn); - performanceCompendium.HitsPerSecondAverage = GetAverageOfDictionaryField("Quests", "HitsPerSecondDictionary", conn); - performanceCompendium.HitsPerSecondMedian = GetMedianOfDictionaryField("Quests", "HitsPerSecondDictionary", conn); - - (performanceCompendium.HighestHitsTakenBlockedPerSecond, performanceCompendium.HighestHitsTakenBlockedPerSecondRunID) = GetRowWithHighestDictionaryValue("Quests", "HitsTakenBlockedPerSecondDictionary", conn); - performanceCompendium.HitsTakenBlockedPerSecondAverage = GetAverageOfDictionaryField("Quests", "HitsTakenBlockedPerSecondDictionary", conn); - performanceCompendium.HitsTakenBlockedPerSecondMedian = GetMedianOfDictionaryField("Quests", "HitsTakenBlockedPerSecondDictionary", conn); - - (performanceCompendium.HighestSingleHitDamage, performanceCompendium.HighestSingleHitDamageRunID) = GetRowWithHighestDictionaryValue("Quests", "DamageDealtDictionary", conn); - performanceCompendium.SingleHitDamageAverage = GetAverageOfDictionaryField("Quests", "DamageDealtDictionary", conn); - performanceCompendium.SingleHitDamageMedian = GetMedianOfDictionaryField("Quests", "DamageDealtDictionary", conn); - - (performanceCompendium.HighestHitsTakenBlocked, performanceCompendium.HighestHitsTakenBlockedRunID) = GetQuestWithHighestHitsTakenBlocked(conn); - performanceCompendium.HitsTakenBlockedAverage = GetAverageHitsTakenBlockedCount(conn); - performanceCompendium.HitsTakenBlockedMedian = GetMedianHitsTakenBlockedCount(conn); - - performanceCompendium.TotalActions = GetTotalCountOfValueInDictionaryField("KeystrokesDictionary", "Quests", conn) + GetTotalCountOfValueInDictionaryField("MouseInputDictionary", "Quests", conn) + GetTotalCountOfValueInDictionaryField("GamepadInputDictionary", "Quests", conn); - performanceCompendium.TotalHitsCount = GetTotalCountOfValueInDictionaryField("HitCountDictionary", "Quests", conn); - performanceCompendium.TotalHitsTakenBlocked = GetTotalCountOfValueInDictionaryField("HitsTakenBlockedDictionary", "Quests", conn); - - performanceCompendium.HealthAverage = GetAverageOfDictionaryField("Quests", "PlayerHPDictionary", conn); - performanceCompendium.HealthMedian = GetMedianOfDictionaryField("Quests", "PlayerHPDictionary", conn); - performanceCompendium.HealthMode = GetModeOfDictionaryField("Quests", "PlayerHPDictionary", conn); - - performanceCompendium.StaminaAverage = GetAverageOfDictionaryField("Quests", "PlayerStaminaDictionary", conn); - performanceCompendium.StaminaMedian = GetMedianOfDictionaryField("Quests", "PlayerStaminaDictionary", conn); - performanceCompendium.StaminaMode = GetModeOfDictionaryField("Quests", "PlayerStaminaDictionary", conn); - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); - } - } + /// + /// Gets the maximum value with WHERE clause. + /// + /// The field. + /// The table. + /// The connection. + /// The where field. + /// The where value. + /// + private static long GetMaxValueWithWhere(string field, string table, SQLiteConnection conn, string whereField, long whereValue) + { + var query = $"SELECT MAX({field}) FROM {table} WHERE {whereField} = @whereValue"; + using (var command = new SQLiteCommand(query, conn)) + { + command.Parameters.AddWithValue("@whereValue", whereValue); + var result = command.ExecuteScalar(); + return result == DBNull.Value ? 0 : (long)result; } - - return performanceCompendium; } - public MezFesCompendium GetMezFesCompendium() + /// + /// Gets the total unique armor pieces. + /// + /// The connection. + /// + private static long GetTotalUniqueArmorPieces(SQLiteConnection conn) { - var mezFesCompendium = new MezFesCompendium(); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get MezFes compendium. dataSource: {0}", this.dataSource); - return mezFesCompendium; - } + long totalUniqueArmorPieces = 0; - using (var conn = new SQLiteConnection(this.dataSource)) + var query = @" + SELECT + COUNT(DISTINCT HeadID) + + COUNT(DISTINCT ChestID) + + COUNT(DISTINCT ArmsID) + + COUNT(DISTINCT WaistID) + + COUNT(DISTINCT LegsID) AS TotalUniqueArmorPieces + FROM PlayerGear + "; + + using (var cmd = new SQLiteCommand(query, conn)) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + using (var reader = cmd.ExecuteReader()) { - try - { - mezFesCompendium.MinigamesPlayed = GetTableRowCount("MezFesID", "MezFes", conn); - mezFesCompendium.UrukiPachinkoTimesPlayed = GetCountOfIntValue("MezFesMinigameID", "MezFes", 464, conn); - mezFesCompendium.UrukiPachinkoHighscore = GetMaxValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 464); - mezFesCompendium.UrukiPachinkoAverageScore = GetAverageValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 464); - mezFesCompendium.UrukiPachinkoMedianScore = GetMedianValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 464); - - mezFesCompendium.GuukuScoopTimesPlayed = GetCountOfIntValue("MezFesMinigameID", "MezFes", 466, conn); - mezFesCompendium.GuukuScoopHighscore = GetMaxValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 466); - mezFesCompendium.GuukuScoopAverageScore = GetAverageValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 466); - mezFesCompendium.GuukuScoopMedianScore = GetMedianValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 466); - - mezFesCompendium.NyanrendoTimesPlayed = GetCountOfIntValue("MezFesMinigameID", "MezFes", 467, conn); - mezFesCompendium.NyanrendoHighscore = GetMaxValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 467); - mezFesCompendium.NyanrendoAverageScore = GetAverageValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 467); - mezFesCompendium.NyanrendoMedianScore = GetMedianValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 467); - - mezFesCompendium.PanicHoneyTimesPlayed = GetCountOfIntValue("MezFesMinigameID", "MezFes", 468, conn); - mezFesCompendium.PanicHoneyHighscore = GetMaxValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 468); - mezFesCompendium.PanicHoneyAverageScore = GetAverageValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 468); - mezFesCompendium.PanicHoneyMedianScore = GetMedianValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 468); - - mezFesCompendium.DokkanBattleCatsTimesPlayed = GetCountOfIntValue("MezFesMinigameID", "MezFes", 469, conn); - mezFesCompendium.DokkanBattleCatsHighscore = GetMaxValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 469); - mezFesCompendium.DokkanBattleCatsAverageScore = GetAverageValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 469); - mezFesCompendium.DokkanBattleCatsMedianScore = GetMedianValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 469); - - transaction.Commit(); - } - catch (Exception ex) + while (reader.Read()) { - HandleError(transaction, ex); + totalUniqueArmorPieces += (long)reader["TotalUniqueArmorPieces"]; } } } - return mezFesCompendium; + return totalUniqueArmorPieces; } - public MonsterCompendium GetMonsterCompendium() + /// + /// Gets the total unique weapon i ds. + /// + /// The connection. + /// + private static long GetTotalUniqueWeaponIDs(SQLiteConnection conn) { - var monsterCompendium = new MonsterCompendium(); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster compendium. dataSource: {0}", this.dataSource); - return monsterCompendium; - } + long totalUniqueWeaponIDs = 0; - using (var conn = new SQLiteConnection(this.dataSource)) + var query = @" + SELECT + COUNT(DISTINCT BlademasterWeaponID) + + COUNT(DISTINCT GunnerWeaponID) AS TotalUniqueWeaponIDs + FROM PlayerGear + "; + + using (var cmd = new SQLiteCommand(query, conn)) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + using (var reader = cmd.ExecuteReader()) { - try - { - (monsterCompendium.HighestMonsterAttackMultiplier, monsterCompendium.HighestMonsterAttackMultiplierRunID) = GetRecordWithHighestValueInField(conn, "Quests", "Monster1AttackMultiplierDictionary"); - (monsterCompendium.LowestMonsterAttackMultiplier, monsterCompendium.LowestMonsterAttackMultiplierRunID) = GetRecordWithLowestValueInField(conn, "Quests", "Monster1AttackMultiplierDictionary"); - - (monsterCompendium.HighestMonsterDefenseRate, monsterCompendium.HighestMonsterDefenseRateRunID) = GetRecordWithHighestValueInField(conn, "Quests", "Monster1DefenseRateDictionary"); - (monsterCompendium.LowestMonsterDefenseRate, monsterCompendium.LowestMonsterDefenseRateRunID) = GetRecordWithLowestValueInField(conn, "Quests", "Monster1DefenseRateDictionary"); - - (monsterCompendium.HighestMonsterSizeMultiplier, monsterCompendium.HighestMonsterSizeMultiplierRunID) = GetRecordWithHighestValueInField(conn, "Quests", "Monster1SizeMultiplierDictionary"); - (monsterCompendium.LowestMonsterSizeMultiplier, monsterCompendium.LowestMonsterSizeMultiplierRunID) = GetRecordWithLowestValueInField(conn, "Quests", "Monster1SizeMultiplierDictionary"); - - transaction.Commit(); - } - catch (Exception ex) + while (reader.Read()) { - HandleError(transaction, ex); + totalUniqueWeaponIDs += (long)reader["TotalUniqueWeaponIDs"]; } } } - return monsterCompendium; + return totalUniqueWeaponIDs; } - public MiscellaneousCompendium GetMiscellaneousCompendium() + /// + /// Gets the total unique decorations. Does not count weapon slots, as those are meant to use sigils. + /// + /// The connection. + /// + private static long GetTotalUniqueDecorations(SQLiteConnection conn) { - var miscellaneousCompendium = new MiscellaneousCompendium(); - if (string.IsNullOrEmpty(this.dataSource)) - { - Logger.Warn(CultureInfo.InvariantCulture, "Cannot get miscellaneous compendium. dataSource: {0}", this.dataSource); - return miscellaneousCompendium; - } + long totalUniqueDecorations = 0; - using (var conn = new SQLiteConnection(this.dataSource)) + var query = @" + SELECT + COUNT(DISTINCT HeadSlot1ID) + + COUNT(DISTINCT HeadSlot2ID) + + COUNT(DISTINCT HeadSlot3ID) + + COUNT(DISTINCT ChestSlot1ID) + + COUNT(DISTINCT ChestSlot2ID) + + COUNT(DISTINCT ChestSlot3ID) + + COUNT(DISTINCT ArmsSlot1ID) + + COUNT(DISTINCT ArmsSlot2ID) + + COUNT(DISTINCT ArmsSlot3ID) + + COUNT(DISTINCT WaistSlot1ID) + + COUNT(DISTINCT WaistSlot2ID) + + COUNT(DISTINCT WaistSlot3ID) + + COUNT(DISTINCT LegsSlot1ID) + + COUNT(DISTINCT LegsSlot2ID) + + COUNT(DISTINCT LegsSlot3ID) AS TotalUniqueDecorations + FROM PlayerGear + "; + + using (var cmd = new SQLiteCommand(query, conn)) { - conn.Open(); - using (var transaction = conn.BeginTransaction()) + using (var reader = cmd.ExecuteReader()) { - try - { - miscellaneousCompendium.TotalOverlaySessions = GetTableRowCount("SessionID", "Session", conn); - miscellaneousCompendium.HighestSessionDuration = GetMaxValue("SessionDuration", "Session", conn); - miscellaneousCompendium.LowestSessionDuration = GetMinValue("SessionDuration", "Session", conn); - miscellaneousCompendium.AverageSessionDuration = GetAvgValue("SessionDuration", "Session", conn); - miscellaneousCompendium.MedianSessionDuration = GetMedianValue("SessionDuration", "Session", conn); - - transaction.Commit(); - } - catch (Exception ex) + while (reader.Read()) { - HandleError(transaction, ex); + totalUniqueDecorations += (long)reader["TotalUniqueDecorations"]; } } } - return miscellaneousCompendium; + return totalUniqueDecorations; } - // Define a function that takes a connection string and the name of the table to check for foreign key violations - public static string CheckForeignKeys(SQLiteConnection connection, string tableName = "") + /// + /// Gets the most common decoration identifier. + /// + /// The connection. + /// + private static long GetMostCommonDecorationID(SQLiteConnection conn) { - Logger.Debug("Checking foreign keys"); + Dictionary decorationCounts = new (); - var query = "PRAGMA foreign_key_check"; - if (!string.IsNullOrEmpty(tableName)) - { - query += "('" + tableName + "')"; - } + var query = @" + SELECT HeadSlot1ID, HeadSlot2ID, HeadSlot3ID, + ChestSlot1ID, ChestSlot2ID, ChestSlot3ID, + ArmsSlot1ID, ArmsSlot2ID, ArmsSlot3ID, + WaistSlot1ID, WaistSlot2ID, WaistSlot3ID, + LegsSlot1ID, LegsSlot2ID, LegsSlot3ID + FROM PlayerGear + "; - using (var command = new SQLiteCommand(query, connection)) + using (var cmd = new SQLiteCommand(query, conn)) { - using (var reader = command.ExecuteReader()) + using (var reader = cmd.ExecuteReader()) { - if (reader.HasRows) + while (reader.Read()) { - // There are foreign key violations - var violations = new List>(); - - while (reader.Read()) + long?[] decorationIDs = { - var violation = new Dictionary(); - for (var i = 0; i < reader.FieldCount; i++) + reader.IsDBNull(0) ? null : reader.GetInt64(0), + reader.IsDBNull(1) ? null : reader.GetInt64(1), + reader.IsDBNull(2) ? null : reader.GetInt64(2), + reader.IsDBNull(3) ? null : reader.GetInt64(3), + reader.IsDBNull(4) ? null : reader.GetInt64(4), + reader.IsDBNull(5) ? null : reader.GetInt64(5), + reader.IsDBNull(6) ? null : reader.GetInt64(6), + reader.IsDBNull(7) ? null : reader.GetInt64(7), + reader.IsDBNull(8) ? null : reader.GetInt64(8), + reader.IsDBNull(9) ? null : reader.GetInt64(9), + reader.IsDBNull(10) ? null : reader.GetInt64(10), + reader.IsDBNull(11) ? null : reader.GetInt64(11), + reader.IsDBNull(12) ? null : reader.GetInt64(12), + reader.IsDBNull(13) ? null : reader.GetInt64(13), + }; + + foreach (var decorationID in decorationIDs) + { + if (decorationID.HasValue && decorationID.Value != 0) { - violation[reader.GetName(i)] = reader.GetValue(i); + if (decorationCounts.ContainsKey(decorationID.Value)) + { + decorationCounts[decorationID.Value]++; + } + else + { + decorationCounts[decorationID.Value] = 1; + } } - - violations.Add(violation); } - - Logger.Error(violations); - return JsonConvert.SerializeObject(violations); - } - else - { - // No foreign key violations - Logger.Debug("No foreign key violations found."); - return string.Empty; } } } + + var mostCommonDecorationID = decorationCounts.OrderByDescending(x => x.Value).Select(x => (long?)x.Key).FirstOrDefault(); + + return mostCommonDecorationID == null ? 0 : (long)mostCommonDecorationID; } - private int GetUserVersion(SQLiteConnection connection) + private static long GetLeastUsedArmorSkillID(SQLiteConnection conn) { - var version = 0; - using (var transaction = connection.BeginTransaction()) + long leastUsedArmorSkillID = 0; + + var query = @" + SELECT ActiveSkill1ID, ActiveSkill2ID, ActiveSkill3ID, ActiveSkill4ID, ActiveSkill5ID, + ActiveSkill6ID, ActiveSkill7ID, ActiveSkill8ID, ActiveSkill9ID, ActiveSkill10ID, + ActiveSkill11ID, ActiveSkill12ID, ActiveSkill13ID, ActiveSkill14ID, ActiveSkill15ID, + ActiveSkill16ID, ActiveSkill17ID, ActiveSkill18ID, ActiveSkill19ID + FROM ActiveSkills + "; + + Dictionary skillCounts = new (); + + using (var cmd = new SQLiteCommand(query, conn)) { - try + using (var reader = cmd.ExecuteReader()) { - var sql = @"PRAGMA user_version"; - using (var cmd = new SQLiteCommand(sql, connection)) + while (reader.Read()) { - var result = cmd.ExecuteScalar(); - if (result != DBNull.Value) + for (var i = 0; i < reader.FieldCount; i++) { - version = Convert.ToInt32(result, CultureInfo.InvariantCulture); - Logger.Info(CultureInfo.InvariantCulture, "Current user_version is {0}", version); + var skillID = reader.GetInt64(i); + + if (skillID != 0) + { + if (skillCounts.ContainsKey(skillID)) + { + skillCounts[skillID]++; + } + else + { + skillCounts[skillID] = 1; + } + } } } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } } - return version; + leastUsedArmorSkillID = skillCounts.OrderBy(x => x.Value).Select(x => x.Key).FirstOrDefault(); + + return leastUsedArmorSkillID; } - private void SetUserVersion(SQLiteConnection connection, int version) + /// + /// Gets the minimum value with WHERE clause. + /// + /// The field. + /// The table. + /// The connection. + /// The where field. + /// The where value. + /// + private static long GetMinValueWithWhere(string field, string table, SQLiteConnection conn, string whereField, long whereValue) { - using (var transaction = connection.BeginTransaction()) + var query = $"SELECT MIN({field}) FROM {table} WHERE {whereField} = @whereValue"; + using (var command = new SQLiteCommand(query, conn)) { - try - { - var sql = $"PRAGMA user_version = {version}"; - using (var cmd = new SQLiteCommand(sql, connection)) - { - cmd.ExecuteNonQuery(); - } - - Logger.Info(CultureInfo.InvariantCulture, "Set user_version to {0}", version); - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); - } + command.Parameters.AddWithValue("@whereValue", whereValue); + var result = command.ExecuteScalar(); + return result == DBNull.Value ? 0 : (long)result; } } /// - /// Run as last update step. + /// Gets the average value with WHERE clause. /// - /// - private void EnforceForeignKeys(SQLiteConnection connection) + /// The field. + /// The table. + /// The connection. + /// The where field. + /// The where value. + /// + private static double GetAverageValueWithWhere(string field, string table, SQLiteConnection conn, string whereField, long whereValue) { - var foreignKeysViolations = CheckForeignKeys(connection); - - // 10. If foreign key constraints were originally enabled then run PRAGMA foreign_key_check to verify that the schema change did not break any foreign key constraints. - if (foreignKeysViolations != string.Empty) + var query = $"SELECT AVG({field}) FROM {table} WHERE {whereField} = @whereValue"; + using (var command = new SQLiteCommand(query, conn)) { - Logger.Fatal(CultureInfo.InvariantCulture, "Foreign keys violations detected, closing program. Violations: {0}", foreignKeysViolations); - MessageBox.Show("Foreign keys violations detected, closing program.", Messages.ErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); - ApplicationService.HandleShutdown(); + command.Parameters.AddWithValue("@whereValue", whereValue); + var result = command.ExecuteScalar(); + return result == DBNull.Value ? 0.0 : (double)result; } - else + } + + /// + /// Gets the median value with WHERE clause. + /// + /// The field. + /// The table. + /// The connection. + /// The where field. + /// The where value. + /// + private static double GetMedianValueWithWhere(string field, string table, SQLiteConnection conn, string whereField, long whereValue) + { + // TODO: not sure if correct + var query = $"SELECT AVG({field}) FROM (SELECT {field}, ROW_NUMBER() OVER (ORDER BY {field}) AS RowNum, COUNT(*) OVER() AS TotalRows FROM {table} WHERE {whereField} = @whereValue) temp WHERE RowNum BETWEEN (TotalRows/2) + 1 AND (TotalRows/2) + 2;"; + using (var command = new SQLiteCommand(query, conn)) { - Logger.Debug("No foreign keys violations found"); + command.Parameters.AddWithValue("@whereValue", whereValue); + var result = command.ExecuteScalar(); + return result == DBNull.Value ? 0.0 : (double)result; } } - // TODO: i dont like using goto, but it seems to make code more succinct in this case. - // https://stackoverflow.com/questions/550662/database-schema-updates - // https://stackoverflow.com/questions/989558/best-practices-for-in-app-database-migration-for-sqlite - /* - The only schema altering commands directly supported by SQLite are the "rename table", "rename column", - "add column", "drop column" commands shown above. However, applications can make other arbitrary changes to - the format of a table using a simple sequence of operations. The steps to make arbitrary - changes to the schema design of some table X are as follows: - - 1. If foreign key constraints are enabled, disable them using PRAGMA foreign_keys=OFF. + /// + /// Get the sum of values in a given field in a given table, excluding null values. + /// + /// + /// + /// + /// + private static long GetSumValue(string field, string table, SQLiteConnection conn) + { + var query = $"SELECT TOTAL({field}) FROM {table} WHERE {field} IS NOT NULL"; + using (var cmd = new SQLiteCommand(query, conn)) + { + var sum = Convert.ToInt64(cmd.ExecuteScalar(), CultureInfo.InvariantCulture); + return sum; + } + } - 2. Start a transaction. + /// + /// Gets the record with highest value in field. + /// + /// The connection. + /// Name of the table. + /// Name of the field. + /// + private static (double, long) GetRecordWithHighestValueInField(SQLiteConnection conn, string tableName, string fieldName) + { + var query = $"SELECT RunID, {fieldName} FROM {tableName}"; + long highestValueRunID = 0; + double highestValue = 0; - 3. Remember the format of all indexes, triggers, and views associated with table X. This information will be needed in step 8 below. One way to do this is to run a query like the following: SELECT type, sql FROM sqlite_schema WHERE tbl_name='X'. + using (var cmd = new SQLiteCommand(query, conn)) + { + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var runID = (long)reader["RunID"]; + var fieldValueString = (string)reader[fieldName]; - 4. Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X. Make sure that the name "new_X" does not collide with any existing table name, of course. + if (string.IsNullOrEmpty(fieldValueString) || fieldValueString == "{}") + { + continue; + } - 5. Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X. + var fieldValue = JObject.Parse(fieldValueString) + .ToObject>>(); + if (fieldValue == null) + { + return (highestValue, highestValueRunID); + } - 6. Drop the old table X: DROP TABLE X. + var fieldValueMax = fieldValue.Max(kv1 => kv1.Value.Max(kv2 => kv2.Value)); + if (fieldValueMax > highestValue) + { + highestValue = fieldValueMax; + highestValueRunID = runID; + } + } + } + } - 7. Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X. + return (highestValue, highestValueRunID); + } - 8. Use CREATE INDEX, CREATE TRIGGER, and CREATE VIEW to reconstruct indexes, triggers, and views associated with table X. Perhaps use the old format of the triggers, indexes, and views saved from step 3 above as a guide, making changes as appropriate for the alteration. + /// + /// Gets the record with lowest value in field. + /// + /// The connection. + /// Name of the table. + /// Name of the field. + /// + private static (double, long) GetRecordWithLowestValueInField(SQLiteConnection conn, string tableName, string fieldName) + { + var query = $"SELECT RunID, {fieldName} FROM {tableName}"; + long lowestValueRunID = 0; + var lowestValue = double.MaxValue; - 9. If any views refer to table X in a way that is affected by the schema change, then drop those views using DROP VIEW and recreate them with whatever changes are necessary to accommodate the schema change using CREATE VIEW. - - 10. If foreign key constraints were originally enabled then run PRAGMA foreign_key_check to verify that the schema change did not break any foreign key constraints. - - 11. Commit the transaction started in step 2. - - 12. If foreign keys constraints were originally enabled, re-enable them now. - */ - private void MigrateToSchemaFromVersion(SQLiteConnection conn, int fromVersion) - { - // Keep in mind if you manually change the user_version to 0 and then perform the updates to for example v0.25.0, you - // will lose the data for the fields that got updated to v0.25.0, because the program assumes that you are in - // user_version 1 - // Or put another way: - // PerformUpdateToVersion_0_23_0 updates x y z fields in Quests table. - // then PerformUpdateToVersion_0_25_0 updates a b c fields in Quests table taking into account the data - // that we now have after doing PerformUpdateToVersion_0_23_0. - // This means that, if you do a run with version - // v0.25.0, then set user_version to 0, then run MigrateToSchemaFromVersion, it will wipe Refresh Rate/etc - // because PerformUpdateToVersion_0_23_0 would put default values on new_Quests and PerformUpdateToVersion_0_25_0 - // would port those default values instead. - // But if you are from v0.25.0 then set user_version to 1 then run MigrateToSchemaFromVersion, your data will stay - // because PerformUpdateToVersion_0_25_0 does take into account the Refresh Rate field, and we skip - // PerformUpdateToVersion_0_23_0. - // Create a Stopwatch instance - var stopwatch = new Stopwatch(); - - // Start the stopwatch - stopwatch.Start(); - - // 1. If foreign key constraints are enabled, disable them using PRAGMA foreign_keys=OFF. - DisableForeignKeyConstraints(conn); - - // 2. Start a transaction. - using (var transaction = conn.BeginTransaction()) + using (var cmd = new SQLiteCommand(query, conn)) { - try + using (var reader = cmd.ExecuteReader()) { - var newVersion = fromVersion; - - // allow migrations to fall through switch cases to do a complete run - // start with current version + 1 - // [self beginTransaction]; - switch (fromVersion) + while (reader.Read()) { - default: - Logger.Info(CultureInfo.InvariantCulture, "No new schema updates found. Schema version: {0}", fromVersion); - MessageBox.Show( - string.Format(CultureInfo.InvariantCulture, -@"No new schema updates found! Schema version: {0}", fromVersion), - string.Format(CultureInfo.InvariantCulture, "MHF-Z Overlay Database Update ({0} to {1})", - previousVersion, - App.CurrentProgramVersion), - MessageBoxButton.OK, - MessageBoxImage.Information); - break; - case 0: // v0.22.0 or older (TODO: does this work with older or just v0.22.0?) - // change pin type to mode 'pin' for keyboard handling changes - // removing types from previous schema - // sqlite3_exec(db, "DELETE FROM types;", NULL, NULL, NULL); - // NSLog(@"installing current types"); - // [self loadInitialData]; + var runID = (long)reader["RunID"]; + var fieldValueString = (string)reader[fieldName]; + + if (string.IsNullOrEmpty(fieldValueString) || fieldValueString == "{}") { - this.PerformUpdateToVersion_0_23_0(conn); - newVersion++; - Logger.Info(CultureInfo.InvariantCulture, "Updated schema to version v0.23.0. newVersion {0}", newVersion); - goto case 1; + continue; } - case 1: // v0.25.0 - // adds support for recent view tracking - // sqlite3_exec(db, "ALTER TABLE entries ADD COLUMN touched_at TEXT;", NULL, NULL, NULL); + var fieldValue = JObject.Parse(fieldValueString) + .ToObject>>(); + if (fieldValue == null) { - this.PerformUpdateToVersion_0_25_0(conn); - this.EnforceForeignKeys(conn); - newVersion++; - Logger.Info(CultureInfo.InvariantCulture, "Updated schema to version v0.25.0. newVersion {0}", newVersion); - break; + return (lowestValue, lowestValueRunID); } - // goto case 2; - // } - // case 2://v0.24.0 - // sqlite3_exec(db, "ALTER TABLE categories ADD COLUMN image TEXT;", NULL, NULL, NULL); - // sqlite3_exec(db, "ALTER TABLE categories ADD COLUMN entry_count INTEGER;", NULL, NULL, NULL); - // sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS categories_id_idx ON categories(id);", NULL, NULL, NULL); - // sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS categories_name_id ON categories(name);", NULL, NULL, NULL); - // sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS entries_id_idx ON entries(id);", NULL, NULL, NULL); - - // etc... - // { - // PerformUpdateToVersion_0_25_0(conn); - // newVersion++; - // break; - // } + var value = fieldValue.Min(kv1 => kv1.Value.Min(kv2 => kv2.Value)); + if (value < lowestValue) + { + lowestValue = value; + lowestValueRunID = runID; + } } - - // [self setSchemaVersion]; - this.SetUserVersion(conn, newVersion); - - // 11. Commit the transaction started in step 2. - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } } - // 12. If foreign keys constraints were originally enabled, re-enable them now. - EnableForeignKeyConstraints(conn); - - // [self endTransaction]; - // Stop the stopwatch - stopwatch.Stop(); - - // Get the elapsed time in milliseconds - var elapsedTimeMs = stopwatch.Elapsed.TotalMilliseconds; - - // Print the elapsed time - Logger.Debug($"MigrateToSchemaFromVersion Elapsed Time: {elapsedTimeMs} ms"); + return (lowestValue, lowestValueRunID); } - /* TODO: - with previous installation (v0.22.0): - 1) database update without program update: did settings save? [X] no error logs? [X] database triggers work? [X] - 2) database update with program update: did settings save? [X] no error logs? [X] database triggers work? [X] - 3) program update without database update: did settings save? [X] no error logs? [X] database triggers work? [X] - 4) program update with database update: same as 2) - - without previous installation: - 5) database update without program update: did settings save? [?] no error logs? [X] database triggers work? [X] - 6) database update with program update: did settings save? [?] no error logs? [X] database triggers work? [X] - 7) program update without database update: did settings save? [?] no error logs? [X] database triggers work? [X] - 8) program update with database update: same as 6) - */ - private void CheckDatabaseVersion(SQLiteConnection connection, DataLoader dataLoader) + /// + /// Gets the most completed quest run. + /// + /// The connection. + /// + private static (long, long) GetMostCompletedQuestRun(SQLiteConnection conn) { - var s = (Settings)System.Windows.Application.Current.TryFindResource("Settings"); + long timesCompleted = 0; + long questId = 0; - using (var transaction = connection.BeginTransaction()) + var query = @" + SELECT QuestID, COUNT(*) as TimesCompleted + FROM Quests + GROUP BY QuestID + ORDER BY TimesCompleted DESC + LIMIT 1"; + + using (var cmd = new SQLiteCommand(query, conn)) { - try + using (var reader = cmd.ExecuteReader()) { - var currentUserVersion = this.GetUserVersion(connection); - - // this will always run the next time the user runs the program after a fresh install. So it always runs at least once. - if (App.IsClowdSquirrelUpdating == false && ((App.CurrentProgramVersion != null && App.CurrentProgramVersion.Trim() != previousVersion.Trim()) || currentUserVersion == 0)) + if (reader.Read()) { - Logger.Info(CultureInfo.InvariantCulture, "Found different program version or userVersion 0. Current: {0}, Previous: {1}, userVersion: {2}", App.CurrentProgramVersion, previousVersion, currentUserVersion); - - var result = MessageBox.Show( -@"A new version of the program has been installed. - -Do you want to perform the necessary database updates? A backup of your current MHFZ_Overlay.sqlite file will be done if you accept. + // The most completed quest ID + questId = (long)reader["QuestID"]; -Updating the database structure may take some time, it will transport all of your current data straight to the latest database structure, regardless of the previous database version.", -string.Format(CultureInfo.InvariantCulture, "MHF-Z Overlay Database Update ({0} to {1})", previousVersion, App.CurrentProgramVersion), MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); - if (result == MessageBoxResult.Yes) - { - this.UpdateDatabaseSchema(connection, dataLoader, currentUserVersion); - } - else - { - Logger.Fatal(CultureInfo.InvariantCulture, "Outdated database schema"); - MessageBox.Show("Cannot use the overlay with an outdated database schema", Messages.ErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); - ApplicationService.HandleShutdown(); - } + // The number of times the quest was completed + timesCompleted = (long)reader["TimesCompleted"]; } - - transaction.Commit(); - } - catch (Exception ex) - { - HandleError(transaction, ex); } } + + return (timesCompleted, questId); } - private void RemoveCurrentDatabaseTriggers(SQLiteConnection conn) + /// + /// Gets the most completed quest runs attempted. + /// + /// The connection. + /// + private static long GetMostCompletedQuestRunsAttempted(SQLiteConnection conn, long mostCompletedQuestRunsQuestID) { - using (var transaction = conn.BeginTransaction()) + long attempts = 0; + var query = @" + SELECT TOTAL(Attempts) AS TotalAttempts + FROM QuestAttempts + WHERE QuestID = @questId"; + + using (var cmd = new SQLiteCommand(query, conn)) { - try + cmd.Parameters.AddWithValue("@questId", mostCompletedQuestRunsQuestID); + using (var reader = cmd.ExecuteReader()) { - List triggersToRemove = new(); - - // SQL statement to retrieve all triggers in the database - string getTriggersSql = "SELECT name FROM sqlite_master WHERE type = 'trigger';"; - - // SQL statement to drop a trigger - string dropTriggerSql = "DROP TRIGGER IF EXISTS {0};"; - using (SQLiteCommand getTriggersCommand = new SQLiteCommand(getTriggersSql, conn)) - using (SQLiteDataReader reader = getTriggersCommand.ExecuteReader()) + if (reader.Read()) { - while (reader.Read()) - { - string triggerName = reader.GetString(0); - - using (SQLiteCommand dropTriggerCommand = new SQLiteCommand(string.Format(dropTriggerSql, triggerName), conn)) - { - triggersToRemove.Add(triggerName); - dropTriggerCommand.ExecuteNonQuery(); - } - } + attempts = Convert.ToInt64(reader["TotalAttempts"], CultureInfo.InvariantCulture); } - - transaction.Commit(); - Logger.Debug("Triggers to remove: {0}", triggersToRemove.Count); - } - catch (Exception ex) - { - HandleError(transaction, ex); } } + + return attempts; } - private void CheckDatesFormat(SQLiteConnection conn) + /// + /// Gets the most attempted quest run. + /// + /// The connection. + /// + private static (long, long) GetMostAttemptedQuestRun(SQLiteConnection conn) { - using (var transaction = conn.BeginTransaction()) + long questID = 0; + long attempts = 0; + var query = @" + SELECT q.QuestID, TOTAL(q.Attempts) AS Attempts + FROM QuestAttempts q + JOIN ( + SELECT QuestID, COUNT(*) AS TotalAttempts + FROM QuestAttempts + GROUP BY QuestID + ORDER BY TotalAttempts DESC + LIMIT 1 + ) m ON q.QuestID = m.QuestID + GROUP BY q.QuestID"; + + using (var cmd = new SQLiteCommand(query, conn)) { - try + using (var reader = cmd.ExecuteReader()) { - SQLiteCommand command = new SQLiteCommand("SELECT name FROM sqlite_master WHERE type='table'", conn); - SQLiteDataReader reader = command.ExecuteReader(); - - List tables = new List(); - while (reader.Read()) + if (reader.Read()) { - var name = reader["name"].ToString(); - if (name is not null) - { - tables.Add(name); - } + questID = (long)reader["QuestID"]; + attempts = Convert.ToInt64(reader["Attempts"], CultureInfo.InvariantCulture); } + } + } - if (tables.Count <= 5) + return (attempts, questID); + } + + /// + /// Gets the most attempted quest runs completed. + /// + /// The connection. + /// The most attempted quest runs quest identifier. + /// + private static long GetMostAttemptedQuestRunsCompleted(SQLiteConnection conn, long mostAttemptedQuestRunsQuestID) + { + long timesCompleted = 0; + + var query = @" + SELECT COUNT(*) AS TimesCompleted + FROM Quests + WHERE QuestID = @QuestID"; + + using (var cmd = new SQLiteCommand(query, conn)) + { + cmd.Parameters.AddWithValue("@QuestID", mostAttemptedQuestRunsQuestID); + + using (var reader = cmd.ExecuteReader()) + { + if (reader.Read()) { - Logger.Warn("Only 5 or less tables were found for date conversion, canceling process."); - return; + // The number of times the most attempted quest was completed + timesCompleted = (long)reader["TimesCompleted"]; } + } + } - List updatedFields = new List(); + return timesCompleted; + } - foreach (var table in tables) - { - SQLiteCommand command2 = new SQLiteCommand($"PRAGMA table_info({table})", conn); - SQLiteDataReader reader2 = command2.ExecuteReader(); - while (reader2.Read()) - { - var name2 = reader2["name"].ToString(); - if (name2 is null) - { - Logger.Warn("Column name is null."); - break; - } + /// + /// Takes the field parameter and returns the percentage of non-zero values in that field. + /// + /// The field. + /// The table. + /// The connection. + /// + private static double GetNonZeroPercentageOfField(string field, string table, SQLiteConnection conn) + { + // Initialize variables to hold the total number of entries and the number of entries with non-zero field values + long totalEntries = 0; + long nonZeroEntries = 0; - string columnName = name2; - if (columnName == "CreatedAt" || columnName == "CreationDate" || columnName == "Date" || columnName == "StartTime" || columnName == "EndTime") - { - SQLiteCommand selectCommand = new SQLiteCommand($"SELECT {columnName} FROM {table}", conn); - SQLiteDataReader selectReader = selectCommand.ExecuteReader(); - while (selectReader.Read()) - { - var selectedColumn = selectReader.GetString(selectReader.GetOrdinal(columnName)); - if (selectedColumn is null) - { - Logger.Warn("Selected column is null."); - break; - } + // Query to get the total number of entries and the number of entries with non-zero field values + var query = $"SELECT COUNT(*) AS TotalEntries, COUNT(CASE WHEN {field} != 0 THEN 1 ELSE NULL END) AS NonZeroEntries FROM {table}"; - string value = selectedColumn; - if (!value.EndsWith("Z")) - { - DateTime date; - if (DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out date)) - { - string utcDate = date.ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss.fffffffZ"); + using (var cmd = new SQLiteCommand(query, conn)) + { + using (var reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + totalEntries = (long)reader["TotalEntries"]; + nonZeroEntries = (long)reader["NonZeroEntries"]; + } + } + } - SQLiteCommand updateCommand = new SQLiteCommand($"UPDATE {table} SET {columnName} = '{utcDate}' WHERE {columnName} = '{value}'", conn); - updateCommand.ExecuteNonQuery(); + // Calculate the percentage of non-zero values + var percentage = nonZeroEntries * 100.0 / totalEntries; - updatedFields.Add($"{table}.{columnName}.{value}.{utcDate}"); - } - else - { - string utcDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToString("yyyy-MM-dd HH:mm:ss.fffffffZ"); + return percentage; + } - SQLiteCommand updateCommand = new SQLiteCommand($"UPDATE {table} SET {columnName} = '{utcDate}' WHERE {columnName} = '{value}'", conn); - updateCommand.ExecuteNonQuery(); + /// + /// Gets the solo quests percentage. + /// + /// The connection. + /// + private static double GetSoloQuestsPercentage(SQLiteConnection conn) + { + // Initialize variables to hold the total number of quests and the number of solo quests + long totalQuests = 0; + long soloQuests = 0; - updatedFields.Add($"{table}.{columnName}.{value}.{utcDate}"); - } - } - } - } - } + // Query to get the total number of quests and the number of solo quests + var query = @" + SELECT COUNT(*) AS TotalQuests, + COUNT(CASE WHEN PartySize = 1 THEN 1 ELSE NULL END) AS SoloQuests + FROM Quests + "; + + using (var cmd = new SQLiteCommand(query, conn)) + { + using (var reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + totalQuests = (long)reader["TotalQuests"]; + soloQuests = (long)reader["SoloQuests"]; } + } + } - transaction.Commit(); + // Calculate the percentage of solo quests + return soloQuests * 100.0 / totalQuests; + } - if (updatedFields.Count > 0) + public GearCompendium GetGearCompendium() + { + var gearCompendium = new GearCompendium(); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get gear compendium. dataSource: {0}", this.dataSource); + return gearCompendium; + } + + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try { - MessageBox.Show($"Found dates needed to convert to UTC. Updated date entries count: {updatedFields.Count}", Messages.InfoTitle, MessageBoxButton.OK, MessageBoxImage.Information); - Logger.Debug($"Updated date entries: {string.Join("\n", updatedFields)}"); - Logger.Debug($"Updated date entries count: {updatedFields.Count}"); + gearCompendium.MostUsedWeaponType = GetModeValue("WeaponTypeID", "PlayerGear", conn); + + gearCompendium.TotalUniqueArmorPiecesUsed = GetTotalUniqueArmorPieces(conn); + + gearCompendium.TotalUniqueWeaponsUsed = GetTotalUniqueWeaponIDs(conn); + + gearCompendium.TotalUniqueDecorationsUsed = GetTotalUniqueDecorations(conn); + + gearCompendium.MostCommonDecorationID = GetMostCommonDecorationID(conn); + + gearCompendium.LeastUsedArmorSkill = GetLeastUsedArmorSkillID(conn); + + transaction.Commit(); } - else + catch (Exception ex) { - Logger.Info("No date conversions needed in database."); + HandleError(transaction, ex); } } - catch (Exception ex) - { - HandleError(transaction, ex); - } } + + return gearCompendium; } - private void UpdateDatabaseSchema(SQLiteConnection connection, DataLoader dataLoader, int currentUserVersion) + public PerformanceCompendium GetPerformanceCompendium() { - dataLoader.Model.ShowSaveIcon = true; - - // Make a backup of the current SQLite file before updating the schema - // Get the process that is running "mhf.exe" - var processes = Process.GetProcessesByName("mhf"); - - if (processes.Length > 0) + var performanceCompendium = new PerformanceCompendium(); + if (string.IsNullOrEmpty(this.dataSource)) { - FileService.CreateDatabaseBackup(connection, BackupFolderName); + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get performance compendium. dataSource: {0}", this.dataSource); + return performanceCompendium; } - else + + using (var conn = new SQLiteConnection(this.dataSource)) { - // The "mhf.exe" process was not found - Logger.Fatal(CultureInfo.InvariantCulture, "mhf.exe not found"); - MessageBox.Show("The 'mhf.exe' process was not found.", Messages.ErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); - ApplicationService.HandleShutdown(); - } + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try + { + (performanceCompendium.HighestTrueRaw, performanceCompendium.HighestTrueRawRunID) = GetRowWithHighestDictionaryValue("Quests", "AttackBuffDictionary", conn); + performanceCompendium.TrueRawAverage = GetAverageOfDictionaryField("Quests", "AttackBuffDictionary", conn); + performanceCompendium.TrueRawMedian = GetMedianOfDictionaryField("Quests", "AttackBuffDictionary", conn); - this.MigrateToSchemaFromVersion(connection, currentUserVersion); - var referenceSchemaFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHFZ_Overlay\\reference_schema.json"); - FileService.DeleteFile(referenceSchemaFilePath); + (performanceCompendium.HighestActionsPerMinute, performanceCompendium.HighestActionsPerMinuteRunID) = GetRowWithHighestDictionaryValue("Quests", "ActionsPerMinuteDictionary", conn); + performanceCompendium.ActionsPerMinuteAverage = GetAverageOfDictionaryField("Quests", "ActionsPerMinuteDictionary", conn); + performanceCompendium.ActionsPerMinuteMedian = GetMedianOfDictionaryField("Quests", "ActionsPerMinuteDictionary", conn); - // later on it creates it - // see this comment: Check if the reference schema file exists - // MessageBox.Show("The current version and the previous version aren't the same, however no update was found", Messages.ErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); - // logger.Fatal(CultureInfo.InvariantCulture, "The current version and the previous version aren't the same, however no update was found"); - // ApplicationManager.HandleShutdown(MainWindow._notifyIcon); - Logger.Info(CultureInfo.InvariantCulture, "Database update process finished"); - dataLoader.Model.ShowSaveIcon = false; - MessageBox.Show("Database update process finished", Messages.InfoTitle, MessageBoxButton.OK, MessageBoxImage.Information); - } + (performanceCompendium.HighestDPS, performanceCompendium.HighestDPSRunID) = GetRowWithHighestDictionaryValue("Quests", "DamagePerSecondDictionary", conn); + performanceCompendium.DPSAverage = GetAverageOfDictionaryField("Quests", "DamagePerSecondDictionary", conn); + performanceCompendium.DPSMedian = GetMedianOfDictionaryField("Quests", "DamagePerSecondDictionary", conn); - // TODO: this is repeating code. also not sure if the data types handling is correct - // UPDATE: so it turns out, data types are suggestions, not rules. + (performanceCompendium.HighestHitCount, performanceCompendium.HighestHitCountRunID) = GetRowWithHighestDictionaryValue("Quests", "HitCountDictionary", conn); + performanceCompendium.HitCountAverage = GetAverageOfDictionaryField("Quests", "HitCountDictionary", conn); + performanceCompendium.HitCountMedian = GetMedianOfDictionaryField("Quests", "HitCountDictionary", conn); - /// - /// Performs the update to version 0 23 0. This is only meant to run after CreateDatabaseTables, CreateDatabaseTriggers and CreateDatabaseIndexes. - /// - /// The connection. - private void PerformUpdateToVersion_0_23_0(SQLiteConnection connection) - { - // Perform database updates for version 0.23.0 - // Since we already ran the previous functions as described in the docblock, - // we can skip most table creation and only focus on tables/views/indexes that need a modified schema. - var sql = string.Empty; - - // sql = @"ALTER TABLE Quests - // MODIFY COLUMN Monster1HPDictionary TEXT NOT NULL AFTER PartySize, - // MODIFY COLUMN Monster2HPDictionary TEXT NOT NULL AFTER Monster1HPDictionary, - // MODIFY COLUMN Monster3HPDictionary TEXT NOT NULL AFTER Monster2HPDictionary, - // MODIFY COLUMN Monster4HPDictionary TEXT NOT NULL AFTER Monster3HPDictionary, - // MODIFY COLUMN Monster1AttackMultiplierDictionary TEXT NOT NULL AFTER Monster4HPDictionary, - // MODIFY COLUMN Monster1DefenseRateDictionary TEXT NOT NULL AFTER Monster1AttackMultiplierDictionary, - // MODIFY COLUMN Monster1SizeMultiplierDictionary TEXT NOT NULL AFTER Monster1DefenseRateDictionary, - // MODIFY COLUMN Monster1PoisonThresholdDictionary TEXT NOT NULL AFTER Monster1SizeMultiplierDictionary, - // MODIFY COLUMN Monster1SleepThresholdDictionary TEXT NOT NULL AFTER Monster1PoisonThresholdDictionary, - // MODIFY COLUMN Monster1ParalysisThresholdDictionary TEXT NOT NULL AFTER Monster1SleepThresholdDictionary, - // MODIFY COLUMN Monster1BlastThresholdDictionary TEXT NOT NULL AFTER Monster1ParalysisThresholdDictionary, - // MODIFY COLUMN Monster1StunThresholdDictionary TEXT NOT NULL AFTER Monster1BlastThresholdDictionary; - // MODIFY COLUMN IsHighGradeEdition INTEGER NOT NULL CHECK (IsHighGradeEdition IN (0, 1)) AFTER Monster1StunThresholdDictionary; - // MODIFY COLUMN RefreshRate INTEGER NOT NULL CHECK (RefreshRate IN (1, 30)) AFTER IsHighGradeEdition; - // "; - // using (SQLiteCommand cmd = new SQLiteCommand(sql, connection)) - // { - // cmd.ExecuteNonQuery(); - // } - 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, - FOREIGN KEY(ObjectiveTypeID) REFERENCES ObjectiveType(ObjectiveTypeID) - -- FOREIGN KEY(RankNameID) REFERENCES RankName(RankNameID) - )"; + (performanceCompendium.HighestHitsPerSecond, performanceCompendium.HighestHitsPerSecondRunID) = GetRowWithHighestDictionaryValue("Quests", "HitsPerSecondDictionary", conn); + performanceCompendium.HitsPerSecondAverage = GetAverageOfDictionaryField("Quests", "HitsPerSecondDictionary", conn); + performanceCompendium.HitsPerSecondMedian = GetMedianOfDictionaryField("Quests", "HitsPerSecondDictionary", conn); + + (performanceCompendium.HighestHitsTakenBlockedPerSecond, performanceCompendium.HighestHitsTakenBlockedPerSecondRunID) = GetRowWithHighestDictionaryValue("Quests", "HitsTakenBlockedPerSecondDictionary", conn); + performanceCompendium.HitsTakenBlockedPerSecondAverage = GetAverageOfDictionaryField("Quests", "HitsTakenBlockedPerSecondDictionary", conn); + performanceCompendium.HitsTakenBlockedPerSecondMedian = GetMedianOfDictionaryField("Quests", "HitsTakenBlockedPerSecondDictionary", conn); + + (performanceCompendium.HighestSingleHitDamage, performanceCompendium.HighestSingleHitDamageRunID) = GetRowWithHighestDictionaryValue("Quests", "DamageDealtDictionary", conn); + performanceCompendium.SingleHitDamageAverage = GetAverageOfDictionaryField("Quests", "DamageDealtDictionary", conn); + performanceCompendium.SingleHitDamageMedian = GetMedianOfDictionaryField("Quests", "DamageDealtDictionary", conn); + + (performanceCompendium.HighestHitsTakenBlocked, performanceCompendium.HighestHitsTakenBlockedRunID) = GetQuestWithHighestHitsTakenBlocked(conn); + performanceCompendium.HitsTakenBlockedAverage = GetAverageHitsTakenBlockedCount(conn); + performanceCompendium.HitsTakenBlockedMedian = GetMedianHitsTakenBlockedCount(conn); + + performanceCompendium.TotalActions = GetTotalCountOfValueInDictionaryField("KeystrokesDictionary", "Quests", conn) + GetTotalCountOfValueInDictionaryField("MouseInputDictionary", "Quests", conn) + GetTotalCountOfValueInDictionaryField("GamepadInputDictionary", "Quests", conn); + performanceCompendium.TotalHitsCount = GetTotalCountOfValueInDictionaryField("HitCountDictionary", "Quests", conn); + performanceCompendium.TotalHitsTakenBlocked = GetTotalCountOfValueInDictionaryField("HitsTakenBlockedDictionary", "Quests", conn); + + performanceCompendium.HealthAverage = GetAverageOfDictionaryField("Quests", "PlayerHPDictionary", conn); + performanceCompendium.HealthMedian = GetMedianOfDictionaryField("Quests", "PlayerHPDictionary", conn); + performanceCompendium.HealthMode = GetModeOfDictionaryField("Quests", "PlayerHPDictionary", conn); + + performanceCompendium.StaminaAverage = GetAverageOfDictionaryField("Quests", "PlayerStaminaDictionary", conn); + performanceCompendium.StaminaMedian = GetMedianOfDictionaryField("Quests", "PlayerStaminaDictionary", conn); + performanceCompendium.StaminaMode = GetModeOfDictionaryField("Quests", "PlayerStaminaDictionary", conn); + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } + + return performanceCompendium; + } + + public MezFesCompendium GetMezFesCompendium() + { + var mezFesCompendium = new MezFesCompendium(); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get MezFes compendium. dataSource: {0}", this.dataSource); + return mezFesCompendium; + } + + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try + { + mezFesCompendium.MinigamesPlayed = GetTableRowCount("MezFesID", "MezFes", conn); + mezFesCompendium.UrukiPachinkoTimesPlayed = GetCountOfIntValue("MezFesMinigameID", "MezFes", 464, conn); + mezFesCompendium.UrukiPachinkoHighscore = GetMaxValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 464); + mezFesCompendium.UrukiPachinkoAverageScore = GetAverageValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 464); + mezFesCompendium.UrukiPachinkoMedianScore = GetMedianValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 464); + + mezFesCompendium.GuukuScoopTimesPlayed = GetCountOfIntValue("MezFesMinigameID", "MezFes", 466, conn); + mezFesCompendium.GuukuScoopHighscore = GetMaxValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 466); + mezFesCompendium.GuukuScoopAverageScore = GetAverageValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 466); + mezFesCompendium.GuukuScoopMedianScore = GetMedianValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 466); + + mezFesCompendium.NyanrendoTimesPlayed = GetCountOfIntValue("MezFesMinigameID", "MezFes", 467, conn); + mezFesCompendium.NyanrendoHighscore = GetMaxValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 467); + mezFesCompendium.NyanrendoAverageScore = GetAverageValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 467); + mezFesCompendium.NyanrendoMedianScore = GetMedianValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 467); + + mezFesCompendium.PanicHoneyTimesPlayed = GetCountOfIntValue("MezFesMinigameID", "MezFes", 468, conn); + mezFesCompendium.PanicHoneyHighscore = GetMaxValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 468); + mezFesCompendium.PanicHoneyAverageScore = GetAverageValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 468); + mezFesCompendium.PanicHoneyMedianScore = GetMedianValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 468); + + mezFesCompendium.DokkanBattleCatsTimesPlayed = GetCountOfIntValue("MezFesMinigameID", "MezFes", 469, conn); + mezFesCompendium.DokkanBattleCatsHighscore = GetMaxValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 469); + mezFesCompendium.DokkanBattleCatsAverageScore = GetAverageValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 469); + mezFesCompendium.DokkanBattleCatsMedianScore = GetMedianValueWithWhere("Score", "MezFes", conn, "MezFesMinigameID", 469); + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } + + return mezFesCompendium; + } + + public MonsterCompendium GetMonsterCompendium() + { + var monsterCompendium = new MonsterCompendium(); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get monster compendium. dataSource: {0}", this.dataSource); + return monsterCompendium; + } + + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try + { + (monsterCompendium.HighestMonsterAttackMultiplier, monsterCompendium.HighestMonsterAttackMultiplierRunID) = GetRecordWithHighestValueInField(conn, "Quests", "Monster1AttackMultiplierDictionary"); + (monsterCompendium.LowestMonsterAttackMultiplier, monsterCompendium.LowestMonsterAttackMultiplierRunID) = GetRecordWithLowestValueInField(conn, "Quests", "Monster1AttackMultiplierDictionary"); + + (monsterCompendium.HighestMonsterDefenseRate, monsterCompendium.HighestMonsterDefenseRateRunID) = GetRecordWithHighestValueInField(conn, "Quests", "Monster1DefenseRateDictionary"); + (monsterCompendium.LowestMonsterDefenseRate, monsterCompendium.LowestMonsterDefenseRateRunID) = GetRecordWithLowestValueInField(conn, "Quests", "Monster1DefenseRateDictionary"); + + (monsterCompendium.HighestMonsterSizeMultiplier, monsterCompendium.HighestMonsterSizeMultiplierRunID) = GetRecordWithHighestValueInField(conn, "Quests", "Monster1SizeMultiplierDictionary"); + (monsterCompendium.LowestMonsterSizeMultiplier, monsterCompendium.LowestMonsterSizeMultiplierRunID) = GetRecordWithLowestValueInField(conn, "Quests", "Monster1SizeMultiplierDictionary"); + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } + + return monsterCompendium; + } + + public MiscellaneousCompendium GetMiscellaneousCompendium() + { + var miscellaneousCompendium = new MiscellaneousCompendium(); + if (string.IsNullOrEmpty(this.dataSource)) + { + Logger.Warn(CultureInfo.InvariantCulture, "Cannot get miscellaneous compendium. dataSource: {0}", this.dataSource); + return miscellaneousCompendium; + } + + using (var conn = new SQLiteConnection(this.dataSource)) + { + conn.Open(); + using (var transaction = conn.BeginTransaction()) + { + try + { + miscellaneousCompendium.TotalOverlaySessions = GetTableRowCount("SessionID", "Session", conn); + miscellaneousCompendium.HighestSessionDuration = GetMaxValue("SessionDuration", "Session", conn); + miscellaneousCompendium.LowestSessionDuration = GetMinValue("SessionDuration", "Session", conn); + miscellaneousCompendium.AverageSessionDuration = GetAvgValue("SessionDuration", "Session", conn); + miscellaneousCompendium.MedianSessionDuration = GetMedianValue("SessionDuration", "Session", conn); + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } + + return miscellaneousCompendium; + } + + // Define a function that takes a connection string and the name of the table to check for foreign key violations + public static string CheckForeignKeys(SQLiteConnection connection, string tableName = "") + { + Logger.Debug("Checking foreign keys"); + + var query = "PRAGMA foreign_key_check"; + if (!string.IsNullOrEmpty(tableName)) + { + query += "('" + tableName + "')"; + } + + using (var command = new SQLiteCommand(query, connection)) + { + using (var reader = command.ExecuteReader()) + { + if (reader.HasRows) + { + // There are foreign key violations + var violations = new List>(); + + while (reader.Read()) + { + var violation = new Dictionary(); + for (var i = 0; i < reader.FieldCount; i++) + { + violation[reader.GetName(i)] = reader.GetValue(i); + } + + violations.Add(violation); + } + + Logger.Error(violations); + return JsonConvert.SerializeObject(violations); + } + else + { + // No foreign key violations + Logger.Debug("No foreign key violations found."); + return string.Empty; + } + } + } + } + + private int GetUserVersion(SQLiteConnection connection) + { + var version = 0; + using (var transaction = connection.BeginTransaction()) + { + try + { + var sql = @"PRAGMA user_version"; + using (var cmd = new SQLiteCommand(sql, connection)) + { + var result = cmd.ExecuteScalar(); + if (result != DBNull.Value) + { + version = Convert.ToInt32(result, CultureInfo.InvariantCulture); + Logger.Info(CultureInfo.InvariantCulture, "Current user_version is {0}", version); + } + } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + + return version; + } + + private void SetUserVersion(SQLiteConnection connection, int version) + { + using (var transaction = connection.BeginTransaction()) + { + try + { + var sql = $"PRAGMA user_version = {version}"; + using (var cmd = new SQLiteCommand(sql, connection)) + { + cmd.ExecuteNonQuery(); + } + + Logger.Info(CultureInfo.InvariantCulture, "Set user_version to {0}", version); + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } + + /// + /// Run as last update step. + /// + /// + private void EnforceForeignKeys(SQLiteConnection connection) + { + var foreignKeysViolations = CheckForeignKeys(connection); + + // 10. If foreign key constraints were originally enabled then run PRAGMA foreign_key_check to verify that the schema change did not break any foreign key constraints. + if (foreignKeysViolations != string.Empty) + { + Logger.Fatal(CultureInfo.InvariantCulture, "Foreign keys violations detected, closing program. Violations: {0}", foreignKeysViolations); + MessageBox.Show("Foreign keys violations detected, closing program.", Messages.ErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); + ApplicationService.HandleShutdown(); + } + else + { + Logger.Debug("No foreign keys violations found"); + } + } + + // TODO: i dont like using goto, but it seems to make code more succinct in this case. + // https://stackoverflow.com/questions/550662/database-schema-updates + // https://stackoverflow.com/questions/989558/best-practices-for-in-app-database-migration-for-sqlite + /* + The only schema altering commands directly supported by SQLite are the "rename table", "rename column", + "add column", "drop column" commands shown above. However, applications can make other arbitrary changes to + the format of a table using a simple sequence of operations. The steps to make arbitrary + changes to the schema design of some table X are as follows: + + 1. If foreign key constraints are enabled, disable them using PRAGMA foreign_keys=OFF. + + 2. Start a transaction. + + 3. Remember the format of all indexes, triggers, and views associated with table X. This information will be needed in step 8 below. One way to do this is to run a query like the following: SELECT type, sql FROM sqlite_schema WHERE tbl_name='X'. + + 4. Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X. Make sure that the name "new_X" does not collide with any existing table name, of course. + + 5. Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X. + + 6. Drop the old table X: DROP TABLE X. + + 7. Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X. + + 8. Use CREATE INDEX, CREATE TRIGGER, and CREATE VIEW to reconstruct indexes, triggers, and views associated with table X. Perhaps use the old format of the triggers, indexes, and views saved from step 3 above as a guide, making changes as appropriate for the alteration. + + 9. If any views refer to table X in a way that is affected by the schema change, then drop those views using DROP VIEW and recreate them with whatever changes are necessary to accommodate the schema change using CREATE VIEW. + + 10. If foreign key constraints were originally enabled then run PRAGMA foreign_key_check to verify that the schema change did not break any foreign key constraints. + + 11. Commit the transaction started in step 2. + + 12. If foreign keys constraints were originally enabled, re-enable them now. + */ + private void MigrateToSchemaFromVersion(SQLiteConnection conn, int fromVersion) + { + // Keep in mind if you manually change the user_version to 0 and then perform the updates to for example v0.25.0, you + // will lose the data for the fields that got updated to v0.25.0, because the program assumes that you are in + // user_version 1 + // Or put another way: + // PerformUpdateToVersion_0_23_0 updates x y z fields in Quests table. + // then PerformUpdateToVersion_0_25_0 updates a b c fields in Quests table taking into account the data + // that we now have after doing PerformUpdateToVersion_0_23_0. + // This means that, if you do a run with version + // v0.25.0, then set user_version to 0, then run MigrateToSchemaFromVersion, it will wipe Refresh Rate/etc + // because PerformUpdateToVersion_0_23_0 would put default values on new_Quests and PerformUpdateToVersion_0_25_0 + // would port those default values instead. + // But if you are from v0.25.0 then set user_version to 1 then run MigrateToSchemaFromVersion, your data will stay + // because PerformUpdateToVersion_0_25_0 does take into account the Refresh Rate field, and we skip + // PerformUpdateToVersion_0_23_0. + // Create a Stopwatch instance + var stopwatch = new Stopwatch(); + + // Start the stopwatch + stopwatch.Start(); + + // 1. If foreign key constraints are enabled, disable them using PRAGMA foreign_keys=OFF. + DisableForeignKeyConstraints(conn); + + // 2. Start a transaction. + using (var transaction = conn.BeginTransaction()) + { + try + { + var newVersion = fromVersion; + + // allow migrations to fall through switch cases to do a complete run + // start with current version + 1 + // [self beginTransaction]; + switch (fromVersion) + { + default: + Logger.Info(CultureInfo.InvariantCulture, "No new schema updates found. Schema version: {0}", fromVersion); + MessageBox.Show( + string.Format(CultureInfo.InvariantCulture, +@"No new schema updates found! Schema version: {0}", fromVersion), + string.Format(CultureInfo.InvariantCulture, "MHF-Z Overlay Database Update ({0} to {1})", + previousVersion, + App.CurrentProgramVersion), + MessageBoxButton.OK, + MessageBoxImage.Information); + break; + case 0: // v0.22.0 or older (TODO: does this work with older or just v0.22.0?) + // change pin type to mode 'pin' for keyboard handling changes + // removing types from previous schema + // sqlite3_exec(db, "DELETE FROM types;", NULL, NULL, NULL); + // NSLog(@"installing current types"); + // [self loadInitialData]; + { + this.PerformUpdateToVersion_0_23_0(conn); + newVersion++; + Logger.Info(CultureInfo.InvariantCulture, "Updated schema to version v0.23.0. newVersion {0}", newVersion); + goto case 1; + } + + case 1: // v0.25.0 + // adds support for recent view tracking + // sqlite3_exec(db, "ALTER TABLE entries ADD COLUMN touched_at TEXT;", NULL, NULL, NULL); + { + this.PerformUpdateToVersion_0_25_0(conn); + this.EnforceForeignKeys(conn); + newVersion++; + Logger.Info(CultureInfo.InvariantCulture, "Updated schema to version v0.25.0. newVersion {0}", newVersion); + goto case 2; + } + case 2: // 0.34.0 + // fix attempts and pb attempts, set partysize default to 1 for the extra attempts from 2p/3p/4p. + this.PerformUpdateToVersion_0_34_0(conn); + this.EnforceForeignKeys(conn); + newVersion++; + Logger.Info(CultureInfo.InvariantCulture, "Updated schema to version v0.34.0. newVersion {0}", newVersion); + break; + // case 2://v0.24.0 + // sqlite3_exec(db, "ALTER TABLE categories ADD COLUMN image TEXT;", NULL, NULL, NULL); + // sqlite3_exec(db, "ALTER TABLE categories ADD COLUMN entry_count INTEGER;", NULL, NULL, NULL); + // sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS categories_id_idx ON categories(id);", NULL, NULL, NULL); + // sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS categories_name_id ON categories(name);", NULL, NULL, NULL); + // sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS entries_id_idx ON entries(id);", NULL, NULL, NULL); + + // etc... + // { + // PerformUpdateToVersion_0_25_0(conn); + // newVersion++; + // break; + // } + } + + // [self setSchemaVersion]; + this.SetUserVersion(conn, newVersion); + + // 11. Commit the transaction started in step 2. + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + + // 12. If foreign keys constraints were originally enabled, re-enable them now. + EnableForeignKeyConstraints(conn); + + // [self endTransaction]; + // Stop the stopwatch + stopwatch.Stop(); + + // Get the elapsed time in milliseconds + var elapsedTimeMs = stopwatch.Elapsed.TotalMilliseconds; + + // Print the elapsed time + Logger.Debug($"MigrateToSchemaFromVersion Elapsed Time: {elapsedTimeMs} ms"); + } + + /* TODO: + with previous installation (v0.22.0): + 1) database update without program update: did settings save? [X] no error logs? [X] database triggers work? [X] + 2) database update with program update: did settings save? [X] no error logs? [X] database triggers work? [X] + 3) program update without database update: did settings save? [X] no error logs? [X] database triggers work? [X] + 4) program update with database update: same as 2) + + without previous installation: + 5) database update without program update: did settings save? [?] no error logs? [X] database triggers work? [X] + 6) database update with program update: did settings save? [?] no error logs? [X] database triggers work? [X] + 7) program update without database update: did settings save? [?] no error logs? [X] database triggers work? [X] + 8) program update with database update: same as 6) + */ + private void CheckDatabaseVersion(SQLiteConnection connection, DataLoader dataLoader) + { + var s = (Settings)System.Windows.Application.Current.TryFindResource("Settings"); + + using (var transaction = connection.BeginTransaction()) + { + try + { + var currentUserVersion = this.GetUserVersion(connection); + + // this will always run the next time the user runs the program after a fresh install. So it always runs at least once. + if (App.IsClowdSquirrelUpdating == false && ((App.CurrentProgramVersion != null && App.CurrentProgramVersion.Trim() != previousVersion.Trim()) || currentUserVersion == 0)) + { + Logger.Info(CultureInfo.InvariantCulture, "Found different program version or userVersion 0. Current: {0}, Previous: {1}, userVersion: {2}", App.CurrentProgramVersion, previousVersion, currentUserVersion); + + var result = MessageBox.Show( +@"A new version of the program has been installed. + +Do you want to perform the necessary database updates? A backup of your current MHFZ_Overlay.sqlite file will be done if you accept. + +Updating the database structure may take some time, it will transport all of your current data straight to the latest database structure, regardless of the previous database version.", +string.Format(CultureInfo.InvariantCulture, "MHF-Z Overlay Database Update ({0} to {1})", previousVersion, App.CurrentProgramVersion), MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); + if (result == MessageBoxResult.Yes) + { + this.UpdateDatabaseSchema(connection, dataLoader, currentUserVersion); + } + else + { + Logger.Fatal(CultureInfo.InvariantCulture, "Outdated database schema"); + MessageBox.Show("Cannot use the overlay with an outdated database schema", Messages.ErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); + ApplicationService.HandleShutdown(); + } + } + + transaction.Commit(); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } + + private void RemoveCurrentDatabaseTriggers(SQLiteConnection conn) + { + using (var transaction = conn.BeginTransaction()) + { + try + { + List triggersToRemove = new(); + + // SQL statement to retrieve all triggers in the database + string getTriggersSql = "SELECT name FROM sqlite_master WHERE type = 'trigger';"; + + // SQL statement to drop a trigger + string dropTriggerSql = "DROP TRIGGER IF EXISTS {0};"; + using (SQLiteCommand getTriggersCommand = new SQLiteCommand(getTriggersSql, conn)) + using (SQLiteDataReader reader = getTriggersCommand.ExecuteReader()) + { + while (reader.Read()) + { + string triggerName = reader.GetString(0); + + using (SQLiteCommand dropTriggerCommand = new SQLiteCommand(string.Format(dropTriggerSql, triggerName), conn)) + { + triggersToRemove.Add(triggerName); + dropTriggerCommand.ExecuteNonQuery(); + } + } + } + + transaction.Commit(); + Logger.Debug("Triggers to remove: {0}", triggersToRemove.Count); + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } + + private void CheckDatesFormat(SQLiteConnection conn) + { + using (var transaction = conn.BeginTransaction()) + { + try + { + SQLiteCommand command = new SQLiteCommand("SELECT name FROM sqlite_master WHERE type='table'", conn); + SQLiteDataReader reader = command.ExecuteReader(); + + List tables = new List(); + while (reader.Read()) + { + var name = reader["name"].ToString(); + if (name is not null) + { + tables.Add(name); + } + } + + if (tables.Count <= 5) + { + Logger.Warn("Only 5 or less tables were found for date conversion, canceling process."); + return; + } + + List updatedFields = new List(); + + foreach (var table in tables) + { + SQLiteCommand command2 = new SQLiteCommand($"PRAGMA table_info({table})", conn); + SQLiteDataReader reader2 = command2.ExecuteReader(); + while (reader2.Read()) + { + var name2 = reader2["name"].ToString(); + if (name2 is null) + { + Logger.Warn("Column name is null."); + break; + } + + string columnName = name2; + if (columnName == "CreatedAt" || columnName == "CreationDate" || columnName == "Date" || columnName == "StartTime" || columnName == "EndTime") + { + SQLiteCommand selectCommand = new SQLiteCommand($"SELECT {columnName} FROM {table}", conn); + SQLiteDataReader selectReader = selectCommand.ExecuteReader(); + while (selectReader.Read()) + { + var selectedColumn = selectReader.GetString(selectReader.GetOrdinal(columnName)); + if (selectedColumn is null) + { + Logger.Warn("Selected column is null."); + break; + } + + string value = selectedColumn; + if (!value.EndsWith("Z")) + { + DateTime date; + if (DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out date)) + { + string utcDate = date.ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss.fffffffZ"); + + SQLiteCommand updateCommand = new SQLiteCommand($"UPDATE {table} SET {columnName} = '{utcDate}' WHERE {columnName} = '{value}'", conn); + updateCommand.ExecuteNonQuery(); + + updatedFields.Add($"{table}.{columnName}.{value}.{utcDate}"); + } + else + { + string utcDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToString("yyyy-MM-dd HH:mm:ss.fffffffZ"); + + SQLiteCommand updateCommand = new SQLiteCommand($"UPDATE {table} SET {columnName} = '{utcDate}' WHERE {columnName} = '{value}'", conn); + updateCommand.ExecuteNonQuery(); + + updatedFields.Add($"{table}.{columnName}.{value}.{utcDate}"); + } + } + } + } + } + } + + transaction.Commit(); + + if (updatedFields.Count > 0) + { + MessageBox.Show($"Found dates needed to convert to UTC. Updated date entries count: {updatedFields.Count}", Messages.InfoTitle, MessageBoxButton.OK, MessageBoxImage.Information); + Logger.Debug($"Updated date entries: {string.Join("\n", updatedFields)}"); + Logger.Debug($"Updated date entries count: {updatedFields.Count}"); + } + else + { + Logger.Info("No date conversions needed in database."); + } + } + catch (Exception ex) + { + HandleError(transaction, ex); + } + } + } + + private void UpdateDatabaseSchema(SQLiteConnection connection, DataLoader dataLoader, int currentUserVersion) + { + dataLoader.Model.ShowSaveIcon = true; + + // Make a backup of the current SQLite file before updating the schema + // Get the process that is running "mhf.exe" + var processes = Process.GetProcessesByName("mhf"); + + if (processes.Length > 0) + { + FileService.CreateDatabaseBackup(connection, BackupFolderName); + } + else + { + // The "mhf.exe" process was not found + Logger.Fatal(CultureInfo.InvariantCulture, "mhf.exe not found"); + MessageBox.Show("The 'mhf.exe' process was not found.", Messages.ErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); + ApplicationService.HandleShutdown(); + } - 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) - WHERE EXISTS (SELECT 1 FROM Quests WHERE Quests.RunID = new_Quests.RunID)" - ; + this.MigrateToSchemaFromVersion(connection, currentUserVersion); + var referenceSchemaFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHFZ_Overlay\\reference_schema.json"); + FileService.DeleteFile(referenceSchemaFilePath); + + // later on it creates it + // see this comment: Check if the reference schema file exists + // MessageBox.Show("The current version and the previous version aren't the same, however no update was found", Messages.ErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); + // logger.Fatal(CultureInfo.InvariantCulture, "The current version and the previous version aren't the same, however no update was found"); + // ApplicationManager.HandleShutdown(MainWindow._notifyIcon); + Logger.Info(CultureInfo.InvariantCulture, "Database update process finished"); + dataLoader.Model.ShowSaveIcon = false; + MessageBox.Show("Database update process finished", Messages.InfoTitle, MessageBoxButton.OK, MessageBoxImage.Information); + } - // Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X. - AlterTableQuests(connection, sql, updateQuery); + // TODO: this is repeating code. also not sure if the data types handling is correct + // UPDATE: so it turns out, data types are suggestions, not rules. - sql = @" + /// + /// Performs the update to version 0 23 0. This is only meant to run after CreateDatabaseTables, CreateDatabaseTriggers and CreateDatabaseIndexes. + /// + /// The connection. + private void PerformUpdateToVersion_0_23_0(SQLiteConnection connection) + { + // Perform database updates for version 0.23.0 + // Since we already ran the previous functions as described in the docblock, + // we can skip most table creation and only focus on tables/views/indexes that need a modified schema. + var sql = @" CREATE TABLE IF NOT EXISTS new_GameFolder ( GameFolderHash TEXT NOT NULL DEFAULT '', CreatedAt TEXT NOT NULL DEFAULT '', @@ -14656,79 +15969,240 @@ CREATE TABLE IF NOT EXISTS new_GameFolder ( FOREIGN KEY(RunID) REFERENCES Quests(RunID) )"; - AlterTableGameFolder(connection, sql); + AlterTableGameFolder(connection, sql); + + // https://www.sqlite.org/lang_altertable.html#otheralter must read + // Repeat the same pattern for other version updates + // By using ALTER TABLE, you can make changes to the structure of a table + // without having to recreate the table and manually transfer all the data. + } + + private static void AlterTablePersonalBestAttempts(SQLiteConnection connection, string newSchema) + { + Logger.Info(CultureInfo.InvariantCulture, "Altering PersonalBestAttempts table"); + + var tableName = "PersonalBestAttempts"; + + try + { + // 4. Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X. Make sure that the name "new_X" does not collide with any existing table name, of course. + // Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X + using (var createTable = new SQLiteCommand(newSchema, connection)) + { + createTable.ExecuteNonQuery(); + } + + Logger.Debug(CultureInfo.InvariantCulture, "Created table if not exists new_{0}", tableName); + + // 5. Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X. + // Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X + + // Transfer data from the old table "PersonalBestAttempts" to the new table "new_PersonalBestAttempts". + var transferDataSql = @"INSERT INTO new_PersonalBestAttempts (QuestID, WeaponTypeID, ActualOverlayMode, Attempts, PartySize) + SELECT QuestID, WeaponTypeID, ActualOverlayMode, Attempts, 1 FROM PersonalBestAttempts;"; + using (var transferDataCmd = new SQLiteCommand(transferDataSql, connection)) + { + transferDataCmd.ExecuteNonQuery(); + } + + Logger.Debug(CultureInfo.InvariantCulture, "Transferred data from {0} to new_{1}", tableName, tableName); + + // 6. Drop the old table X: DROP TABLE X. + // Drop the old table X + using (var dropTable = new SQLiteCommand("DROP TABLE " + tableName + ";", connection)) + { + dropTable.ExecuteNonQuery(); + } + + Logger.Debug(CultureInfo.InvariantCulture, "Deleted table {0}", tableName); + + // 7. Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X. + // Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X + using (var renameTable = new SQLiteCommand("ALTER TABLE new_" + tableName + " RENAME TO " + tableName + ";", connection)) + { + renameTable.ExecuteNonQuery(); + } + + Logger.Debug(CultureInfo.InvariantCulture, "Renamed new_{0} to {1}", tableName, tableName); + + // 8. Use CREATE INDEX, CREATE TRIGGER, and CREATE VIEW to reconstruct indexes, triggers, and views associated with table X. Perhaps use the old format of the triggers, indexes, and views saved from step 3 above as a guide, making changes as appropriate for the alteration. + // Recreate indexes, triggers, and views associated with the original table "QuestAttempts". + // (Assuming you have stored the CREATE statements for indexes, triggers, and views separately) + + // TODO: since im not using any views this still needs testing in case i make views someday. + // 9. If any views refer to table X in a way that is affected by the schema change, then drop those views using DROP VIEW and recreate them with whatever changes are necessary to accommodate the schema change using CREATE VIEW. + // using (SQLite + // Check if any views refer to table X in a way that is affected by the schema change + + // Drop those views using DROP VIEW + + // TODO: test + // Recreate views with whatever changes are necessary to accommodate the schema change using CREATE VIEW + Logger.Info(CultureInfo.InvariantCulture, "Altered table {0} successfully", tableName); + } + catch (Exception ex) + { + // Roll back the transaction if any errors occur + Logger.Error(ex, "Could not alter table {0}", tableName); + LoggingService.WriteCrashLog(ex, string.Format(CultureInfo.InvariantCulture, "Could not alter table {0}", tableName)); + } + } + + private static void AlterTableQuestAttempts(SQLiteConnection connection, string newSchema) + { + Logger.Info(CultureInfo.InvariantCulture, "Altering QuestAttempts table"); + + var tableName = "QuestAttempts"; + + try + { + // 4. Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X. Make sure that the name "new_X" does not collide with any existing table name, of course. + // Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X + using (var createTable = new SQLiteCommand(newSchema, connection)) + { + createTable.ExecuteNonQuery(); + } + + Logger.Debug(CultureInfo.InvariantCulture, "Created table if not exists new_{0}", tableName); + + // 5. Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X. + // Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X + + // Transfer data from the old table "QuestAttempts" to the new table "new_QuestAttempts". + var transferDataSql = @"INSERT INTO new_QuestAttempts (QuestID, WeaponTypeID, ActualOverlayMode, Attempts, PartySize) + SELECT QuestID, WeaponTypeID, ActualOverlayMode, Attempts, 1 FROM QuestAttempts;"; + using (var transferDataCmd = new SQLiteCommand(transferDataSql, connection)) + { + transferDataCmd.ExecuteNonQuery(); + } + + Logger.Debug("Transferred data from {0} to new_{1}", tableName, tableName); + + // 6. Drop the old table X: DROP TABLE X. + // Drop the old table X + using (var dropTable = new SQLiteCommand("DROP TABLE " + tableName + ";", connection)) + { + dropTable.ExecuteNonQuery(); + } + + Logger.Debug(CultureInfo.InvariantCulture, "Deleted table {0}", tableName); + + // 7. Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X. + // Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X + using (var renameTable = new SQLiteCommand("ALTER TABLE new_" + tableName + " RENAME TO " + tableName + ";", connection)) + { + renameTable.ExecuteNonQuery(); + } + + Logger.Debug("Renamed new_{0} to {1}", tableName, tableName); + + // 8. Use CREATE INDEX, CREATE TRIGGER, and CREATE VIEW to reconstruct indexes, triggers, and views associated with table X. Perhaps use the old format of the triggers, indexes, and views saved from step 3 above as a guide, making changes as appropriate for the alteration. + // Recreate indexes, triggers, and views associated with the original table "QuestAttempts". + // (Assuming you have stored the CREATE statements for indexes, triggers, and views separately) + + // TODO: since im not using any views this still needs testing in case i make views someday. + // 9. If any views refer to table X in a way that is affected by the schema change, then drop those views using DROP VIEW and recreate them with whatever changes are necessary to accommodate the schema change using CREATE VIEW. + // using (SQLite + // Check if any views refer to table X in a way that is affected by the schema change + + // Drop those views using DROP VIEW + + // TODO: test + // Recreate views with whatever changes are necessary to accommodate the schema change using CREATE VIEW + Logger.Info(CultureInfo.InvariantCulture, "Altered table {0} successfully", tableName); + } + catch (Exception ex) + { + // Roll back the transaction if any errors occur + Logger.Error(ex, "Could not alter table {0}", tableName); + LoggingService.WriteCrashLog(ex, string.Format(CultureInfo.InvariantCulture, "Could not alter table {0}", tableName)); + } + } + + private static void AlterTablePersonalBests(SQLiteConnection connection, string newSchema) + { + Logger.Info(CultureInfo.InvariantCulture, "Altering PersonalBests table"); + + var tableName = "PersonalBests"; + + try + { + // 4. Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X. Make sure that the name "new_X" does not collide with any existing table name, of course. + // Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X + using (var createTable = new SQLiteCommand(newSchema, connection)) + { + createTable.ExecuteNonQuery(); + } + + Logger.Debug(CultureInfo.InvariantCulture, "Created table if not exists new_{0}", tableName); + + // 5. Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X. + // Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X + + // Transfer data from the old table "QuestAttempts" to the new table "new_QuestAttempts". + var transferDataSql = @"INSERT INTO new_PersonalBests (RunID, Attempts, PartySize) + SELECT PersonalBests.RunID, PersonalBests.Attempts, Quests.PartySize + FROM PersonalBests + INNER JOIN Quests ON PersonalBests.RunID = Quests.RunID"; + using (var transferDataCmd = new SQLiteCommand(transferDataSql, connection)) + { + transferDataCmd.ExecuteNonQuery(); + } + + Logger.Debug("Transferred data from {0} to new_{1}", tableName, tableName); + + // 6. Drop the old table X: DROP TABLE X. + // Drop the old table X + using (var dropTable = new SQLiteCommand("DROP TABLE " + tableName + ";", connection)) + { + dropTable.ExecuteNonQuery(); + } + + Logger.Debug(CultureInfo.InvariantCulture, "Deleted table {0}", tableName); + + // 7. Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X. + // Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X + using (var renameTable = new SQLiteCommand("ALTER TABLE new_" + tableName + " RENAME TO " + tableName + ";", connection)) + { + renameTable.ExecuteNonQuery(); + } + + Logger.Debug("Renamed new_{0} to {1}", tableName, tableName); + + // 8. Use CREATE INDEX, CREATE TRIGGER, and CREATE VIEW to reconstruct indexes, triggers, and views associated with table X. Perhaps use the old format of the triggers, indexes, and views saved from step 3 above as a guide, making changes as appropriate for the alteration. + // Recreate indexes, triggers, and views associated with the original table "QuestAttempts". + // (Assuming you have stored the CREATE statements for indexes, triggers, and views separately) + + // TODO: since im not using any views this still needs testing in case i make views someday. + // 9. If any views refer to table X in a way that is affected by the schema change, then drop those views using DROP VIEW and recreate them with whatever changes are necessary to accommodate the schema change using CREATE VIEW. + // using (SQLite + // Check if any views refer to table X in a way that is affected by the schema change - // https://www.sqlite.org/lang_altertable.html#otheralter must read - // Repeat the same pattern for other version updates - // By using ALTER TABLE, you can make changes to the structure of a table - // without having to recreate the table and manually transfer all the data. + // Drop those views using DROP VIEW + + // TODO: test + // Recreate views with whatever changes are necessary to accommodate the schema change using CREATE VIEW + Logger.Info(CultureInfo.InvariantCulture, "Altered table {0} successfully", tableName); + } + catch (Exception ex) + { + // Roll back the transaction if any errors occur + Logger.Error(ex, "Could not alter table {0}", tableName); + LoggingService.WriteCrashLog(ex, string.Format(CultureInfo.InvariantCulture, "Could not alter table {0}", tableName)); + } } // Define a function that takes a connection string, the name of the table to alter (X), and the new schema for the table (as a SQL string) - private static void AlterTableQuests(SQLiteConnection connection, string newSchema, string updateQuery) - { - /* - *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, - - FOREIGN KEY(ObjectiveTypeID) REFERENCES ObjectiveType(ObjectiveTypeID) - -- FOREIGN KEY(RankNameID) REFERENCES RankName(RankNameID) - )"; - */ - Logger.Info(CultureInfo.InvariantCulture, "Altering Quests table"); - - var tableName = "Quests"; + /// + /// TODO: Test. Alters table schema and fills it with default values. Does not support tables that have UNIQUE constraint, etc. + /// + /// + /// + /// + /// + private static void AlterTableData(SQLiteConnection connection, string newSchema, string updateQuery, string tableName) + { + Logger.Info(CultureInfo.InvariantCulture, $"Altering {tableName} table"); try { @@ -14743,7 +16217,21 @@ FOREIGN KEY(ObjectiveTypeID) REFERENCES ObjectiveType(ObjectiveTypeID) while (reader.Read()) { var type = reader.GetString(0); - var sql = reader.GetString(1); + var sql = string.Empty; + try + { + sql = reader.GetString(1); + } + catch + { + Logger.Info($"Table {tableName} does not contain type {type}"); + } + + if (sql == string.Empty) + { + continue; + } + if (type == "index") { indexSqls.Add(sql); @@ -14773,15 +16261,15 @@ FOREIGN KEY(ObjectiveTypeID) REFERENCES ObjectiveType(ObjectiveTypeID) // Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X // Get the number of rows in the Quests table - var countQuery = "SELECT COUNT(*) FROM Quests"; + var countQuery = $"SELECT COUNT(*) FROM {tableName}"; using (var command = new SQLiteCommand(countQuery, connection)) { var rowCount = Convert.ToInt32(command.ExecuteScalar(), CultureInfo.InvariantCulture); - Logger.Debug(CultureInfo.InvariantCulture, "Inserting default values into new_Quests"); + Logger.Debug(CultureInfo.InvariantCulture, $"Inserting default values into new_{tableName}"); // Insert rows with default values into new_Quests - var insertQuery = $"INSERT INTO new_Quests DEFAULT VALUES"; + var insertQuery = $"INSERT INTO new_{tableName} DEFAULT VALUES"; for (var i = 0; i < rowCount; i++) { using (var insertCommand = new SQLiteCommand(insertQuery, connection)) @@ -14790,7 +16278,7 @@ FOREIGN KEY(ObjectiveTypeID) REFERENCES ObjectiveType(ObjectiveTypeID) } } - Logger.Debug("Inserted default values into new_Quests"); + Logger.Debug($"Inserted default values into new_{tableName}"); // Update values from Quests to new_Quests /* @@ -14909,147 +16397,6 @@ v0.24 to v0.25 (fixing the refreshrate check is above, but the fix is implemente Logger.Info(CultureInfo.InvariantCulture, "Altered table {0} successfully", tableName); } catch (Exception ex) - { - // Roll back the transaction if any errors occur - Logger.Error(ex, "Could not alter table {0}", tableName); - } - } - - private static void AlterTablePersonalBestAttempts(SQLiteConnection connection, string newSchema) - { - Logger.Info(CultureInfo.InvariantCulture, "Altering PersonalBestAttempts table"); - - var tableName = "PersonalBestAttempts"; - - try - { - // 4. Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X. Make sure that the name "new_X" does not collide with any existing table name, of course. - // Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X - using (var createTable = new SQLiteCommand(newSchema, connection)) - { - createTable.ExecuteNonQuery(); - } - - Logger.Debug(CultureInfo.InvariantCulture, "Created table if not exists new_{0}", tableName); - - // 5. Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X. - // Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X - - // Transfer data from the old table "PersonalBestAttempts" to the new table "new_PersonalBestAttempts". - var transferDataSql = @"INSERT INTO new_PersonalBestAttempts (QuestID, WeaponTypeID, ActualOverlayMode, Attempts) - SELECT QuestID, WeaponTypeID, ActualOverlayMode, Attempts FROM PersonalBestAttempts;"; - using (var transferDataCmd = new SQLiteCommand(transferDataSql, connection)) - { - transferDataCmd.ExecuteNonQuery(); - } - - Logger.Debug(CultureInfo.InvariantCulture, "Transferred data from {0} to new_{1}", tableName, tableName); - - // 6. Drop the old table X: DROP TABLE X. - // Drop the old table X - using (var dropTable = new SQLiteCommand("DROP TABLE " + tableName + ";", connection)) - { - dropTable.ExecuteNonQuery(); - } - - Logger.Debug(CultureInfo.InvariantCulture, "Deleted table {0}", tableName); - - // 7. Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X. - // Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X - using (var renameTable = new SQLiteCommand("ALTER TABLE new_" + tableName + " RENAME TO " + tableName + ";", connection)) - { - renameTable.ExecuteNonQuery(); - } - - Logger.Debug(CultureInfo.InvariantCulture, "Renamed new_{0} to {1}", tableName, tableName); - - // 8. Use CREATE INDEX, CREATE TRIGGER, and CREATE VIEW to reconstruct indexes, triggers, and views associated with table X. Perhaps use the old format of the triggers, indexes, and views saved from step 3 above as a guide, making changes as appropriate for the alteration. - // Recreate indexes, triggers, and views associated with the original table "QuestAttempts". - // (Assuming you have stored the CREATE statements for indexes, triggers, and views separately) - - // TODO: since im not using any views this still needs testing in case i make views someday. - // 9. If any views refer to table X in a way that is affected by the schema change, then drop those views using DROP VIEW and recreate them with whatever changes are necessary to accommodate the schema change using CREATE VIEW. - // using (SQLite - // Check if any views refer to table X in a way that is affected by the schema change - - // Drop those views using DROP VIEW - - // TODO: test - // Recreate views with whatever changes are necessary to accommodate the schema change using CREATE VIEW - Logger.Info(CultureInfo.InvariantCulture, "Altered table {0} successfully", tableName); - } - catch (Exception ex) - { - // Roll back the transaction if any errors occur - Logger.Error(ex, "Could not alter table {0}", tableName); - LoggingService.WriteCrashLog(ex, string.Format(CultureInfo.InvariantCulture, "Could not alter table {0}", tableName)); - } - } - - private static void AlterTableQuestAttempts(SQLiteConnection connection, string newSchema) - { - Logger.Info(CultureInfo.InvariantCulture, "Altering QuestAttempts table"); - - var tableName = "QuestAttempts"; - - try - { - // 4. Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X. Make sure that the name "new_X" does not collide with any existing table name, of course. - // Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X - using (var createTable = new SQLiteCommand(newSchema, connection)) - { - createTable.ExecuteNonQuery(); - } - - Logger.Debug(CultureInfo.InvariantCulture, "Created table if not exists new_{0}", tableName); - - // 5. Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X. - // Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X - - // Transfer data from the old table "QuestAttempts" to the new table "new_QuestAttempts". - var transferDataSql = @"INSERT INTO new_QuestAttempts (QuestID, WeaponTypeID, ActualOverlayMode, Attempts) - SELECT QuestID, WeaponTypeID, ActualOverlayMode, Attempts FROM QuestAttempts;"; - using (var transferDataCmd = new SQLiteCommand(transferDataSql, connection)) - { - transferDataCmd.ExecuteNonQuery(); - } - - Logger.Debug("Transferred data from {0} to new_{1}", tableName, tableName); - - // 6. Drop the old table X: DROP TABLE X. - // Drop the old table X - using (var dropTable = new SQLiteCommand("DROP TABLE " + tableName + ";", connection)) - { - dropTable.ExecuteNonQuery(); - } - - Logger.Debug(CultureInfo.InvariantCulture, "Deleted table {0}", tableName); - - // 7. Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X. - // Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X - using (var renameTable = new SQLiteCommand("ALTER TABLE new_" + tableName + " RENAME TO " + tableName + ";", connection)) - { - renameTable.ExecuteNonQuery(); - } - - Logger.Debug("Renamed new_{0} to {1}", tableName, tableName); - - // 8. Use CREATE INDEX, CREATE TRIGGER, and CREATE VIEW to reconstruct indexes, triggers, and views associated with table X. Perhaps use the old format of the triggers, indexes, and views saved from step 3 above as a guide, making changes as appropriate for the alteration. - // Recreate indexes, triggers, and views associated with the original table "QuestAttempts". - // (Assuming you have stored the CREATE statements for indexes, triggers, and views separately) - - // TODO: since im not using any views this still needs testing in case i make views someday. - // 9. If any views refer to table X in a way that is affected by the schema change, then drop those views using DROP VIEW and recreate them with whatever changes are necessary to accommodate the schema change using CREATE VIEW. - // using (SQLite - // Check if any views refer to table X in a way that is affected by the schema change - - // Drop those views using DROP VIEW - - // TODO: test - // Recreate views with whatever changes are necessary to accommodate the schema change using CREATE VIEW - Logger.Info(CultureInfo.InvariantCulture, "Altered table {0} successfully", tableName); - } - catch (Exception ex) { // Roll back the transaction if any errors occur Logger.Error(ex, "Could not alter table {0}", tableName); @@ -15564,41 +16911,7 @@ UPDATE new_Quests WHERE EXISTS (SELECT 1 FROM Quests WHERE Quests.RunID = new_Quests.RunID)" ; - AlterTableQuests(connection, sql, updateQuery); - - sql = @"CREATE TABLE IF NOT EXISTS new_QuestAttempts( - QuestAttemptsID INTEGER PRIMARY KEY AUTOINCREMENT, - QuestID INTEGER NOT NULL, - WeaponTypeID INTEGER NOT NULL, - ActualOverlayMode TEXT NOT NULL, - Attempts INTEGER NOT NULL, - FOREIGN KEY(WeaponTypeID) REFERENCES WeaponType(WeaponTypeID), - UNIQUE (QuestID, WeaponTypeID, ActualOverlayMode) - ) - "; - using (var cmd = new SQLiteCommand(sql, connection)) - { - cmd.ExecuteNonQuery(); - } - - AlterTableQuestAttempts(connection, sql); - - sql = @"CREATE TABLE IF NOT EXISTS new_PersonalBestAttempts( - PersonalBestAttemptsID INTEGER PRIMARY KEY AUTOINCREMENT, - QuestID INTEGER NOT NULL, - WeaponTypeID INTEGER NOT NULL, - ActualOverlayMode TEXT NOT NULL, - Attempts INTEGER NOT NULL, - FOREIGN KEY(WeaponTypeID) REFERENCES WeaponType(WeaponTypeID), - UNIQUE (QuestID, WeaponTypeID, ActualOverlayMode) - ) - "; - using (var cmd = new SQLiteCommand(sql, connection)) - { - cmd.ExecuteNonQuery(); - } - - AlterTablePersonalBestAttempts(connection, sql); + AlterTableData(connection, sql, updateQuery, "Quests"); sql = @"DROP TABLE GachaCard"; using (var cmd = new SQLiteCommand(sql, connection)) @@ -15650,6 +16963,64 @@ Score INTEGER NOT NULL DEFAULT 0 Logger.Debug("Recreated Bingo table"); } + /// + /// Performs the update to version 0 25 0. + /// + /// The connection. + private void PerformUpdateToVersion_0_34_0(SQLiteConnection connection) + { + var sql = @"CREATE TABLE IF NOT EXISTS new_PersonalBestAttempts + ( + PersonalBestAttemptsID INTEGER PRIMARY KEY AUTOINCREMENT, + QuestID INTEGER NOT NULL CHECK (QuestID >= 0) DEFAULT 0, + WeaponTypeID INTEGER NOT NULL DEFAULT 0, + ActualOverlayMode TEXT NOT NULL DEFAULT 'Standard', + Attempts INTEGER NOT NULL DEFAULT 0, + PartySize INTEGER NOT NULL DEFAULT 1, + FOREIGN KEY(WeaponTypeID) REFERENCES WeaponType(WeaponTypeID), + UNIQUE (QuestID, WeaponTypeID, ActualOverlayMode, PartySize) + )"; + using (var cmd = new SQLiteCommand(sql, connection)) + { + cmd.ExecuteNonQuery(); + } + + AlterTablePersonalBestAttempts(connection, sql); + + sql = @"CREATE TABLE IF NOT EXISTS new_QuestAttempts( + QuestAttemptsID INTEGER PRIMARY KEY AUTOINCREMENT, + QuestID INTEGER NOT NULL CHECK (QuestID >= 0) DEFAULT 0, + WeaponTypeID INTEGER NOT NULL DEFAULT 0, + ActualOverlayMode TEXT NOT NULL DEFAULT 'Standard', + Attempts INTEGER NOT NULL DEFAULT 1, + PartySize INTEGER NOT NULL DEFAULT 1, + FOREIGN KEY(WeaponTypeID) REFERENCES WeaponType(WeaponTypeID), + UNIQUE (QuestID, WeaponTypeID, ActualOverlayMode, PartySize) + ) + "; + using (var cmd = new SQLiteCommand(sql, connection)) + { + cmd.ExecuteNonQuery(); + } + + AlterTableQuestAttempts(connection, sql); + + sql = @"CREATE TABLE IF NOT EXISTS new_PersonalBests( + PersonalBestsID INTEGER PRIMARY KEY AUTOINCREMENT, + RunID INTEGER NOT NULL DEFAULT 0, + Attempts INTEGER NOT NULL DEFAULT 1, + PartySize INTEGER NOT NULL DEFAULT 1, + FOREIGN KEY(RunID) REFERENCES Quests(RunID) + ) + "; + using (var cmd = new SQLiteCommand(sql, connection)) + { + cmd.ExecuteNonQuery(); + } + + AlterTablePersonalBests(connection, sql); + } + // TODO: should i put this in FileManager? private static void CheckPreviousVersionFile() { diff --git a/MHFZ_Overlay/Services/DiscordService.cs b/MHFZ_Overlay/Services/DiscordService.cs index 39607a17..d0f6c145 100644 --- a/MHFZ_Overlay/Services/DiscordService.cs +++ b/MHFZ_Overlay/Services/DiscordService.cs @@ -392,7 +392,7 @@ public void UpdateDiscordRPC(DataLoader dataLoader) stateString = string.Format(CultureInfo.InvariantCulture, "GR: {0} | GCP: {1} | Guild Food: {2} | Poogie Item: {3}", dataLoader.Model.GRankNumber(), dataLoader.Model.GCP(), ViewModels.Windows.AddressModel.GetArmorSkill(dataLoader.Model.GuildFoodSkill()), ViewModels.Windows.AddressModel.GetItemName(dataLoader.Model.PoogieItemUseID())); PresenceTemplate.State = stateString.Length <= MaxDiscordRPCStringLength ? stateString : string.Concat(stateString.AsSpan(0, MaxDiscordRPCStringLength - 3), "..."); break; - case 205: // Pugi Farm + case 205: // Poogie Farm stateString = string.Format(CultureInfo.InvariantCulture, "GR: {0} | Poogie Points: {1} | Poogie Clothes: {2} | Poogie Item: {3}", dataLoader.Model.GRankNumber(), dataLoader.Model.PoogiePoints(), ViewModels.Windows.AddressModel.GetPoogieClothes(dataLoader.Model.PoogieCostume()), ViewModels.Windows.AddressModel.GetItemName(dataLoader.Model.PoogieItemUseID())); PresenceTemplate.State = stateString.Length <= MaxDiscordRPCStringLength ? stateString : string.Concat(stateString.AsSpan(0, MaxDiscordRPCStringLength - 3), "..."); break; diff --git a/MHFZ_Overlay/Services/OverlaySettingsService.cs b/MHFZ_Overlay/Services/OverlaySettingsService.cs index 9ea4f2d1..e3fd4e95 100644 --- a/MHFZ_Overlay/Services/OverlaySettingsService.cs +++ b/MHFZ_Overlay/Services/OverlaySettingsService.cs @@ -72,12 +72,12 @@ public static void SetConfigurationPreset(Settings s, ConfigurationPreset preset s.EnableMap = false; s.PersonalBestTimePercentShown = false; s.EnablePersonalBestPaceColor = false; + s.PlayerPositionShown = false; s.TimerInfoShown = true; s.EnableInputLogging = true; s.EnableQuestLogging = true; s.OverlayModeWatermarkShown = true; - break; case ConfigurationPreset.Zen: @@ -108,6 +108,7 @@ public static void SetConfigurationPreset(Settings s, ConfigurationPreset preset s.TimerInfoShown = false; s.EnableInputLogging = false; s.EnableMap = false; + s.PlayerPositionShown = false; s.OverlayModeWatermarkShown = false; @@ -138,6 +139,7 @@ public static void SetConfigurationPreset(Settings s, ConfigurationPreset preset s.EnableMap = false; s.ActionsPerMinuteShown = false; s.PersonalBestShown = false; + s.PlayerPositionShown = false; s.OverlayModeWatermarkShown = false; diff --git a/MHFZ_Overlay/Settings.Designer.cs b/MHFZ_Overlay/Settings.Designer.cs index b6af8b36..33bcda60 100644 --- a/MHFZ_Overlay/Settings.Designer.cs +++ b/MHFZ_Overlay/Settings.Designer.cs @@ -3958,5 +3958,125 @@ public bool EnableAttemptsPerPersonalBest { this["EnableAttemptsPerPersonalBest"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool PlayerPositionShown { + get { + return ((bool)(this["PlayerPositionShown"])); + } + set { + this["PlayerPositionShown"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5")] + public double PlayerPositionX { + get { + return ((double)(this["PlayerPositionX"])); + } + set { + this["PlayerPositionX"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("140")] + public double PlayerPositionY { + get { + return ((double)(this["PlayerPositionY"])); + } + set { + this["PlayerPositionY"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Automatic")] + public string PlayerPositionMode { + get { + return ((string)(this["PlayerPositionMode"])); + } + set { + this["PlayerPositionMode"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool DivaSongTimerShown { + get { + return ((bool)(this["DivaSongTimerShown"])); + } + set { + this["DivaSongTimerShown"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5")] + public double DivaSongTimerX { + get { + return ((double)(this["DivaSongTimerX"])); + } + set { + this["DivaSongTimerX"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("100")] + public double DivaSongTimerY { + get { + return ((double)(this["DivaSongTimerY"])); + } + set { + this["DivaSongTimerY"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool GuildFoodTimerShown { + get { + return ((bool)(this["GuildFoodTimerShown"])); + } + set { + this["GuildFoodTimerShown"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5")] + public double GuildFoodTimerX { + get { + return ((double)(this["GuildFoodTimerX"])); + } + set { + this["GuildFoodTimerX"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public double GuildFoodTimerY { + get { + return ((double)(this["GuildFoodTimerY"])); + } + set { + this["GuildFoodTimerY"] = value; + } + } } } diff --git a/MHFZ_Overlay/Settings.settings b/MHFZ_Overlay/Settings.settings index fe3a8861..77ffc685 100644 --- a/MHFZ_Overlay/Settings.settings +++ b/MHFZ_Overlay/Settings.settings @@ -986,5 +986,35 @@ False + + False + + + 5 + + + 140 + + + Automatic + + + True + + + 5 + + + 100 + + + True + + + 5 + + + 60 + \ No newline at end of file diff --git a/MHFZ_Overlay/ViewModels/Windows/AddressModel.cs b/MHFZ_Overlay/ViewModels/Windows/AddressModel.cs index 31f56a88..51e3d2f8 100644 --- a/MHFZ_Overlay/ViewModels/Windows/AddressModel.cs +++ b/MHFZ_Overlay/ViewModels/Windows/AddressModel.cs @@ -9,6 +9,7 @@ namespace MHFZ_Overlay.ViewModels.Windows; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; +using System.Drawing.Imaging; using System.Globalization; using System.IO; using System.Linq; @@ -28,6 +29,7 @@ namespace MHFZ_Overlay.ViewModels.Windows; using MHFZ_Overlay.Models.Structures; using MHFZ_Overlay.Services; using MHFZ_Overlay.Services.Converter; +using NLog; using RESTCountries.NET.Models; using RESTCountries.NET.Services; using SkiaSharp; @@ -46,9 +48,9 @@ public abstract class AddressModel : INotifyPropertyChanged private static readonly DatabaseService DatabaseManagerInstance = DatabaseService.GetInstance(); - public ObservableCollection AttackBuffCollection { get; set; } = new (); + public ObservableCollection AttackBuffCollection { get; set; } = new(); - private readonly Mem m = new (); + private readonly Mem m = new(); private int savedMonster1MaxHP; @@ -125,6 +127,12 @@ public AddressModel(Mem m) public bool ShowSessionTimeInfo { get; set; } + public bool ShowPlayerPositionInfo { get; set; } + + public bool ShowDivaSongTimer { get; set; } + + public bool ShowGuildFoodTimer { get; set; } + public bool ShowMonsterPartHP { get; set; } public bool ShowKBMLayout { get; set; } @@ -326,6 +334,42 @@ 21747 or public abstract int HalkFullness(); + public abstract int HalkLevel(); + + public abstract int HalkIntimacy(); + + public abstract int HalkHealth(); + + public abstract int HalkAttack(); + + public abstract int HalkDefense(); + + public abstract int HalkIntellect(); + + public abstract int HalkSkill1(); + + public abstract int HalkSkill2(); + + public abstract int HalkSkill3(); + + public abstract int HalkElementNone(); + + public abstract int HalkFire(); + + public abstract int HalkThunder(); + + public abstract int HalkWater(); + + public abstract int HalkIce(); + + public abstract int HalkDragon(); + + public abstract int HalkSleep(); + + public abstract int HalkParalysis(); + + public abstract int HalkPoison(); + public abstract int RankBand(); public abstract int PartnyaRankPoints(); @@ -1264,16 +1308,84 @@ 21747 or /// public abstract int QuestToggleMonsterMode(); + /// + /// Course rights. 14 is hl+ex + /// + /// + public abstract int Rights(); + + public abstract decimal PlayerPositionX(); + + public abstract decimal PlayerPositionY(); + + public abstract decimal PlayerPositionZ(); + + public abstract decimal PlayerPositionInQuestX(); + + public abstract decimal PlayerPositionInQuestY(); + + public abstract decimal PlayerPositionInQuestZ(); + + public abstract int ActiveFeature1(); + + /// + /// Alternative. + /// + /// + public abstract int ActiveFeature2(); + + /// + /// Alternative. + /// + /// + public abstract int ActiveFeature3(); + + /// + /// Updates every 11 seconds + /// + /// + public abstract int ServerHeartbeat(); + + public abstract int GuildFoodStart(); + + public abstract int DivaSongStart(); + + public abstract int GuildPoogie1Skill(); + + public abstract int GuildPoogie2Skill(); + + public abstract int GuildPoogie3Skill(); + + public abstract int DivaPrayerGemRedSkill(); + + public abstract int DivaPrayerGemRedLevel(); + + public abstract int DivaPrayerGemYellowSkill(); + + public abstract int DivaPrayerGemYellowLevel(); + + public abstract int DivaPrayerGemGreenSkill(); + + public abstract int DivaPrayerGemGreenLevel(); + + public abstract int DivaPrayerGemBlueSkill(); + + public abstract int DivaPrayerGemBlueLevel(); + + public abstract bool HalkOn(); + + public abstract bool HalkPotEffectOn(); + + /// /// [] Not Done /// [X] Done /// [O] WIP - /// [] Prayer gems, /// [] bento, /// [] sharpness table, /// [] pvp, - /// [] zenith in road, guild pugi, gear rarity colors. - /// [] Database would store prayer gems, bento, sharpness table, pvp, guild pugi. Should i use separate table? + /// [] zenith in road, gear rarity colors. + /// [] Database would store bento, sharpness table, pvp. Should i use separate table? /// public bool HasMonster2 @@ -1666,13 +1778,17 @@ public OverlayMode GetOverlayMode() || s.Monster4HealthBarShown || s.EnableMap || s.PersonalBestTimePercentShown - || s.EnablePersonalBestPaceColor) // TODO monster 1 overview? and update README + || s.EnablePersonalBestPaceColor + || s.PlayerPositionShown) // TODO monster 1 overview? and update README { return OverlayMode.Standard; } else if (s.TimerInfoShown && s.EnableInputLogging && s.EnableQuestLogging && s.OverlayModeWatermarkShown) { - if (this.DivaSkillUsesLeft() == 0 && this.StyleRank1() != 15 && this.StyleRank2() != 15) + var gems = new List { DivaPrayerGemRedSkill(), DivaPrayerGemRedLevel(), DivaPrayerGemYellowSkill(), DivaPrayerGemYellowLevel(), DivaPrayerGemGreenSkill(), DivaPrayerGemGreenLevel(), DivaPrayerGemBlueSkill(), DivaPrayerGemBlueLevel() }; + var guildPoogies = new List { GuildPoogie1Skill(), GuildPoogie2Skill(), GuildPoogie3Skill() }; + // TODO test + if (!HalkPotEffectOn() && GetGuildPoogieEffect(guildPoogies) == "No Poogie" && this.GuildFoodSkill() == 0 && !HalkOn() && GetDivaPrayerGems(gems) == "None" && !IsActiveFeatureOn(GetActiveFeature(), this.WeaponType()) && !IsBitfieldContainingFlag((uint)this.Rights(), CourseRightsFirstByte.Support, (uint)CourseRightsFirstByte.All, true, 1) && this.DivaSkillUsesLeft() == 0 && this.StyleRank1() != 15 && this.StyleRank2() != 15) { return OverlayMode.TimeAttack; } @@ -1691,6 +1807,49 @@ public OverlayMode GetOverlayMode() } } + /// + /// TODO needs real testing + /// + /// + /// + public int GetActiveFeature() + { + var activeFeatures = new[] { this.ActiveFeature1(), this.ActiveFeature2(), this.ActiveFeature3() }; + + foreach (var activeFeature in activeFeatures) + { + if (IsValidBitfield((uint)activeFeature, (uint)ActiveFeature.All)) + { + return activeFeature; + } + } + + LoggerInstance.Warn("Active feature not found: {0} {1} {2}", activeFeatures[0], activeFeatures[1], activeFeatures[2]); + return 0; + } + + public bool IsBitfieldContainingFlag(uint bitfield, T flag, uint all, bool extractByte = false, int bytePosition = 0) where T : Enum + { + byte value = extractByte ? (byte)(((uint)bitfield >> (bytePosition * 8)) & 0xFF) : (byte)bitfield; + + // Validate + if (!IsValidBitfield(value, all)) + { + return false; + } + + // Convert + T convertedValue = (T)Enum.ToObject(typeof(T), value); + + var isFlagSet = convertedValue.HasFlag(flag); + return isFlagSet; + } + + public bool IsValidBitfield(uint value, uint all) + { + return (value & all) == value; + } + public bool CaravanOverride() { var s = (Settings)Application.Current.TryFindResource("Settings"); @@ -2427,6 +2586,88 @@ public string IsPlayerHit } } + public bool DivaSongEnding + { + get + { + if (DivaSongStart() <= 0) + { + return true; + } + + var expiry = DivaSongStart() + (60 * 90); + double secondsLeft = expiry - ServerHeartbeat(); + + return secondsLeft <= 60*10; + } + } + + public bool DivaSongEnded + { + get + { + if (DivaSongStart() <= 0) + { + return true; + } + + var expiry = DivaSongStart() + (60 * 90); + double secondsLeft = expiry - ServerHeartbeat(); + + return secondsLeft <= 0; + } + } + + /// + /// Whether the buff is still active even if it expired inside quest but after quest start. + /// + public bool DivaSongActive { get; set; } + + /// + /// Whether the buff is still active even if it expired inside quest but after quest start. + /// + public bool GuildFoodActive { get; set; } + + public bool GuildFoodEnding + { + get + { + if (GuildFoodStart() <= 0) + { + return true; + } + + var expiry = GuildFoodStart() + (60 * 90); + double secondsLeft = expiry - ServerHeartbeat(); + + return secondsLeft <= 60 * 10; + } + } + + public bool GuildFoodEnded + { + get + { + if (GuildFoodStart() <= 0) + { + return true; + } + + var expiry = GuildFoodStart() + (60 * 90); + double secondsLeft = expiry - ServerHeartbeat(); + + return secondsLeft <= 0; + } + } + + public string DivaSongFill => DivaSongEnding ? CatppuccinMochaColors.NameHex["Red"] : CatppuccinMochaColors.NameHex["Rosewater"]; + + public string GuildFoodFill => GuildFoodEnding ? CatppuccinMochaColors.NameHex["Red"] : CatppuccinMochaColors.NameHex["Rosewater"]; + + public double DivaSongOpacity => DivaSongEnding ? 1 : .5; + + public double GuildFoodOpacity => GuildFoodEnding ? 1 : .5; + public string IsOnPace { get @@ -2656,6 +2897,10 @@ public string IsLowestMonsterDefrate public string PersonalBestIcon { get; set; } = "../../Assets/Icons/png/quest_clock.png"; + public string DivaSongIcon { get; set; } = "../../Assets/Icons/png/diva_fountain.png"; + + public string GuildFoodIcon { get; set; } = "../../Assets/Icons/png/guild_hall.png"; + /// /// Gets player true raw. ///
Attack addition is added as either Attack A or Attack B and is before or after Hunting Horn buffs respectively.
@@ -4459,7 +4704,7 @@ public string QuestToggleModeSelectedShown { return "Visible"; } - else{ + else { return "Collapsed"; } } @@ -4506,13 +4751,13 @@ public string GetRealWeaponName WeaponBlademaster.IDName.TryGetValue(this.BlademasterWeaponID(), out var wepname); var address = this.BlademasterWeaponID().ToString("X4", CultureInfo.InvariantCulture).ToUpperInvariant(); // gives you hex 4 digit "007B" - return string.Format(CultureInfo.InvariantCulture, "{0}{1} ({2}) | {3}\n{4} | {5} | {6}", wepname, lv, address, style, this.GetDecoName(this.WeaponDeco1ID(), 1), this.GetDecoName(this.WeaponDeco2ID(), 2), this.GetDecoName(this.WeaponDeco3ID(), 3)); + return string.Format(CultureInfo.InvariantCulture, "{0}{1} ({2}) | {3}\n{4} | {5} | {6}", wepname, lv, address, style, this.GetDecoName(this.WeaponDeco1ID(), EquipmentSlot.One), this.GetDecoName(this.WeaponDeco2ID(), EquipmentSlot.Two), this.GetDecoName(this.WeaponDeco3ID(), EquipmentSlot.Three)); } else if (className == "Gunner") { WeaponGunner.IDName.TryGetValue(this.GunnerWeaponID(), out var wepname); var address = this.GunnerWeaponID().ToString("X4", CultureInfo.InvariantCulture).ToUpperInvariant(); // gives you hex 4 digit "007B" - return string.Format(CultureInfo.InvariantCulture, "{0}{1} ({2}) | {3}\n{4} | {5} | {6}", wepname, lv, address, style, this.GetDecoName(this.WeaponDeco1ID(), 1), this.GetDecoName(this.WeaponDeco2ID(), 2), this.GetDecoName(this.WeaponDeco3ID(), 3)); + return string.Format(CultureInfo.InvariantCulture, "{0}{1} ({2}) | {3}\n{4} | {5} | {6}", wepname, lv, address, style, this.GetDecoName(this.WeaponDeco1ID(), EquipmentSlot.One), this.GetDecoName(this.WeaponDeco2ID(), EquipmentSlot.Two), this.GetDecoName(this.WeaponDeco3ID(), EquipmentSlot.Three)); } else { @@ -5115,7 +5360,7 @@ public string GetArmorLegNameForRunID(int armorID, int slot1ID, int slot2ID, int /// The decos. /// /// - public string GetDecoName(int id, int slot = 0, bool isForImage = false) + public string GetDecoName(int id, EquipmentSlot slot = EquipmentSlot.None, bool isForImage = false) { var decoName = string.Empty; @@ -5135,7 +5380,7 @@ public string GetDecoName(int id, int slot = 0, bool isForImage = false) decoName += string.Empty; } - if (decoName == "Empty" && slot != 0) + if (decoName == "Empty" && slot != EquipmentSlot.None) { return this.GetSigilName(slot); } @@ -5160,8 +5405,25 @@ public string GetDecoName(int id, int slot = 0, bool isForImage = false) /// The sigils. /// /// - public string GetSigilName(int slot) + public string GetSigilName(EquipmentSlot slot) { + if (slot == EquipmentSlot.None) + { + return "Empty"; + } + + var decoSlot1Occupied = WeaponDeco1ID() > 0; + var decoSlot2Occupied = WeaponDeco2ID() > 0; + var decoSlot3Occupied = WeaponDeco3ID() > 0; + + if ((decoSlot1Occupied && decoSlot2Occupied && decoSlot3Occupied) + || (decoSlot1Occupied && slot == EquipmentSlot.One) + || (decoSlot2Occupied && slot == EquipmentSlot.Two) + || (decoSlot3Occupied && slot == EquipmentSlot.Three)) + { + return "Empty"; + } + var sigilSkillList = SkillSigil.IDName; var sigilNames = new int[] { this.Sigil1Name1(), this.Sigil1Name2(), this.Sigil1Name3(), this.Sigil2Name1(), this.Sigil2Name2(), this.Sigil2Name3(), this.Sigil3Name1(), this.Sigil3Name2(), this.Sigil3Name3() }; var sigilValues = new int[] { this.Sigil1Value1(), this.Sigil1Value2(), this.Sigil1Value3(), this.Sigil2Value1(), this.Sigil2Value2(), this.Sigil2Value3(), this.Sigil3Value1(), this.Sigil3Value2(), this.Sigil3Value3() }; @@ -5172,7 +5434,32 @@ public string GetSigilName(int slot) sigilTypes[i] = type ?? string.Empty; } - var index = (slot - 1) * 3; + var index = 0; + var slotNumber = 1; + + switch (slot) + { + case EquipmentSlot.Two: + slotNumber = 2; + break; + case EquipmentSlot.Three: + slotNumber = 3; + break; + } + + if ((decoSlot1Occupied && decoSlot2Occupied) + || (decoSlot1Occupied && !decoSlot2Occupied && !decoSlot3Occupied && slot == EquipmentSlot.Two)) + { + index = 0; + } + else if (decoSlot1Occupied && !decoSlot2Occupied && !decoSlot3Occupied && slot == EquipmentSlot.Three){ + index = 3; + } + else if (!decoSlot1Occupied && !decoSlot2Occupied && !decoSlot3Occupied) + { + index = (slotNumber - 1) * 3; + } + if (sigilValues[index] == 0 || sigilNames[index] == 0) { return "Empty"; @@ -7515,6 +7802,7 @@ public static string GetGRWeaponLevel(int level) /// public string GenerateGearStats(long? runID = null) { + // TODO: update if (runID == null) { // save gear to variable @@ -7549,6 +7837,13 @@ public string GenerateGearStats(long? runID = null) var styleRankSkills = DatabaseManagerInstance.GetStyleRankSkills((long)runID); var zenithSkills = DatabaseManagerInstance.GetZenithSkills((long)runID); var quest = DatabaseManagerInstance.GetQuest((long)runID); + var diva = DatabaseManagerInstance.GetDiva((long)runID); + var activeFeature = DatabaseManagerInstance.GetActiveFeature((long)runID); + var courses = DatabaseManagerInstance.GetCourses((long)runID); + var guildPoogie = DatabaseManagerInstance.GetGuildPoogie((long)runID); + var halk = DatabaseManagerInstance.GetHalk((long)runID); + var toggleMode = DatabaseManagerInstance.GetQuestToggleMode((long)runID); + var overlayHash = DatabaseManagerInstance.GetOverlayHash((long)runID); var createdBy = playerGear.CreatedBy; var weaponClass = this.GetWeaponClass((int?)playerGear.WeaponClassID); @@ -7577,7 +7872,7 @@ public string GenerateGearStats(long? runID = null) var armorSkills = GetArmorSkillsForRunID((int)activeSkills.ActiveSkill1ID, (int)activeSkills.ActiveSkill2ID, (int)activeSkills.ActiveSkill3ID, (int)activeSkills.ActiveSkill4ID, (int)activeSkills.ActiveSkill5ID, (int)activeSkills.ActiveSkill6ID, (int)activeSkills.ActiveSkill7ID, (int)activeSkills.ActiveSkill8ID, (int)activeSkills.ActiveSkill9ID, (int)activeSkills.ActiveSkill10ID, (int)activeSkills.ActiveSkill11ID, (int)activeSkills.ActiveSkill12ID, (int)activeSkills.ActiveSkill13ID, (int)activeSkills.ActiveSkill14ID, (int)activeSkills.ActiveSkill15ID, (int)activeSkills.ActiveSkill16ID, (int)activeSkills.ActiveSkill17ID, (int)activeSkills.ActiveSkill18ID, (int)activeSkills.ActiveSkill19ID); var caravanSkillsList = GetCaravanSkillsForRunID((int)caravanSkills.CaravanSkill1ID, (int)caravanSkills.CaravanSkill2ID, (int)caravanSkills.CaravanSkill3ID); var divaSkill = GetDivaSkillNameFromID((int)playerGear.DivaSkillID); - var guildFood = GetArmorSkill((int)playerGear.GuildFoodID); + var guildFood = GetArmorSkill((int)playerGear.GuildFoodID) == "None" ? "No Food" : GetArmorSkill((int)playerGear.GuildFoodID); var styleRankSkillsList = GetGSRSkillsForRunID((int)styleRankSkills.StyleRankSkill1ID, (int)styleRankSkills.StyleRankSkill2ID); var inventory = GetItemsForRunID(new int[] { (int)playerInventory.Item1ID, (int)playerInventory.Item2ID, (int)playerInventory.Item3ID, (int)playerInventory.Item4ID, (int)playerInventory.Item5ID, (int)playerInventory.Item6ID, (int)playerInventory.Item7ID, (int)playerInventory.Item8ID, (int)playerInventory.Item9ID, (int)playerInventory.Item10ID, (int)playerInventory.Item11ID, (int)playerInventory.Item12ID, (int)playerInventory.Item13ID, (int)playerInventory.Item14ID, (int)playerInventory.Item15ID, (int)playerInventory.Item16ID, (int)playerInventory.Item17ID, (int)playerInventory.Item18ID, (int)playerInventory.Item19ID, (int)playerInventory.Item20ID }); var ammo = GetItemsForRunID(new int[] { (int)ammoPouch.Item1ID, (int)ammoPouch.Item2ID, (int)ammoPouch.Item3ID, (int)ammoPouch.Item4ID, (int)ammoPouch.Item5ID, (int)ammoPouch.Item6ID, (int)ammoPouch.Item7ID, (int)ammoPouch.Item8ID, (int)ammoPouch.Item9ID, (int)ammoPouch.Item10ID }); @@ -7590,6 +7885,12 @@ public string GenerateGearStats(long? runID = null) var questCategory = quest.ActualOverlayMode; var partySize = quest.PartySize; + var toggleModeName = toggleMode.QuestToggleMode == null ? "Normal" : EZlion.Mapper.QuestToggleMode.IDName[(int)toggleMode.QuestToggleMode]; + var activeFeatureState = activeFeature.ActiveFeature == null ? false : IsActiveFeatureOn((long)activeFeature.ActiveFeature, playerGear.WeaponTypeID); + var halkSkill1 = halk.HalkSkill1 == null ? "None" : EZlion.Mapper.SkillHalk.IDName[(int)halk.HalkSkill1]; + var halkSkill2 = halk.HalkSkill2 == null ? "None" : EZlion.Mapper.SkillHalk.IDName[(int)halk.HalkSkill2]; + var halkSkill3 = halk.HalkSkill3 == null ? "None" : EZlion.Mapper.SkillHalk.IDName[(int)halk.HalkSkill3]; + // TODO: fix // var partnyaBagItems = GetItemsForRunID(new int[] { (int)partnyaBag.Item1ID, (int)partnyaBag.Item2ID, (int)partnyaBag.Item3ID, (int)partnyaBag.Item4ID, (int)partnyaBag.Item5ID, (int)partnyaBag.Item6ID, (int)partnyaBag.Item7ID, (int)partnyaBag.Item8ID, (int)partnyaBag.Item9ID, (int)partnyaBag.Item10ID }); return string.Format( @@ -7619,31 +7920,57 @@ public string GenerateGearStats(long? runID = null) Caravan Skills: {19} -Diva Skill: +Diva: {20} +Song {21} -Guild Food: -{21} +Diva Prayer Gems: +{22} + +Guild: +{23} +{24} Style Rank: -{22} +{25} Items: -{23} +{26} Ammo: -{24} +{27} Poogie Item: -{25} +{28} Road/Duremudira Skills: -{26} - -Quest: {27} -{28} {29} {30} -Category: {31} -Party Size: {32}", +{29} + +Quest: {30} +{31} {32} {33} +Category: {34} +Party Size: {35} +Mode: {36} +Active Feature {37} + +Courses: +{38} + +Halk: +{39} +Halk Pot {40} +LV{41} +Element Type {42} +Status Type {43} +Intimacy {44} +Health {45} +Attack {46} +Defense {47} +Intellect {48} +{49} | {50} | {51} + +Overlay Hash: {52} +", createdBy, weaponClass, gender, @@ -7664,22 +7991,295 @@ public string GenerateGearStats(long? runID = null) gouBoost, armorSkills, caravanSkillsList, - divaSkill, + divaSkill == "None" ? "No Skill" : divaSkill, + diva.DivaSongBuffOn > 0 ? "ON" : "OFF", + GetDivaPrayerGems(diva), guildFood, + GetGuildPoogieEffect(guildPoogie), styleRankSkillsList, inventory, ammo, poogieItem, roadDureSkillsList, - // partnyaBagItems + // TODO partnyaBagItems questName, questObjectiveType, questObjectiveQuantity, questObjectiveName, questCategory, - partySize); + partySize, + toggleModeName, + activeFeatureState == true ? "ON" : "OFF", + $"Main: {GetMainCourses(courses.Rights)}\nAdditional: {GetAdditionalCourses(courses.Rights)}", + halk.HalkOn > 0 ? "Active" : "Inactive", + halk.HalkPotEffectOn > 0 ? "ON" : "OFF", + halk.HalkLevel, + GetHalkElement(halk), + GetHalkStatus(halk), + halk.HalkIntimacy, + halk.HalkHealth, + halk.HalkAttack, + halk.HalkDefense, + halk.HalkIntellect, + halkSkill1, + halkSkill2, + halkSkill3, + overlayHash.OverlayHash + ); + } + } + + public string GetMainCourses(long? rights) + { + if (rights == null || rights <= 0 || rights > 0xFFFF) + { + return "None"; } + + // Map the first and second bytes to course rights names + var courseNames = MapCourseRightsToNames((long)rights, 0, typeof(CourseRightsSecondByte)); + // Join the names with commas + return string.Join(", ", courseNames).Trim(); + } + + public string GetAdditionalCourses(long? rights) + { + if (rights == null || rights <= 0 || rights > 0xFFFF) + { + return "None"; + } + + // Map the first and second bytes to course rights names + var courseNames = MapCourseRightsToNames((long)rights, 1, typeof(CourseRightsFirstByte)); + // Join the names with commas + return string.Join(", ", courseNames).Trim(); + } + + private IEnumerable MapCourseRightsToNames(long rights, int bytePosition, Type enumType) + { + if ((rights < 0x0100 && bytePosition == 1) || rights == 0) + { + yield return "None"; + yield break; + } + + foreach (var name in Enum.GetNames(enumType)) + { + if (name == "None") + continue; + + if (enumType == typeof(CourseRightsFirstByte)) + { + if (IsBitfieldContainingFlag((uint)rights, (CourseRightsFirstByte)Enum.Parse(enumType, name), (uint)CourseRightsFirstByte.All, true, bytePosition)) + { + yield return name; + } + } + else if (enumType == typeof(CourseRightsSecondByte)) + { + if (IsBitfieldContainingFlag((uint)rights, (CourseRightsSecondByte)Enum.Parse(enumType, name), (uint)CourseRightsSecondByte.All, true, bytePosition)) + { + yield return name; + } + } + } + } + + public bool IsActiveFeatureOn(long activeFeature, long weaponTypeID) + { + if (activeFeature <= 0) + { + return false; + } + + uint weaponFlag = (uint)Math.Pow(2, weaponTypeID); + return IsBitfieldContainingFlag((uint)activeFeature, (ActiveFeature)weaponFlag, (uint)ActiveFeature.All); + } + + public string GetHalkElement(QuestsHalk halk) + { + var element = "None"; + + if (halk.HalkFire >= 100) + { + return "Fire"; + } + if (halk.HalkWater >= 100) + { + return "Water"; + } + if (halk.HalkThunder >= 100) + { + return "Thunder"; + } + if (halk.HalkIce >= 100) + { + return "Ice"; + } + if (halk.HalkDragon >= 100) + { + return "Dragon"; + } + + return element; + } + + public string GetHalkStatus(QuestsHalk halk) + { + var status = "None"; + + if (halk.HalkPoison >= 100) + { + return "Poison"; + } + if (halk.HalkSleep >= 100) + { + return "Sleep"; + } + if (halk.HalkParalysis >= 100) + { + return "Paralysis"; + } + + return status; + } + + public string GetGuildPoogieEffect(QuestsGuildPoogie poogie) + { + var effect = "No Poogie"; + + if (poogie.GuildPoogie1Skill >= 1) + { + return EZlion.Mapper.SkillGuildPoogie.IDName[(int)poogie.GuildPoogie1Skill]; + } + + if (poogie.GuildPoogie2Skill >= 1) + { + return EZlion.Mapper.SkillGuildPoogie.IDName[(int)poogie.GuildPoogie2Skill]; + } + + if (poogie.GuildPoogie3Skill >= 1) + { + return EZlion.Mapper.SkillGuildPoogie.IDName[(int)poogie.GuildPoogie3Skill]; + } + + return effect; + } + + public string GetGuildPoogieEffect(List poogie) + { + var effect = "No Poogie"; + + if (poogie[0] >= 1) + { + return EZlion.Mapper.SkillGuildPoogie.IDName[(int)poogie[0]]; + } + + if (poogie[1] >= 1) + { + return EZlion.Mapper.SkillGuildPoogie.IDName[(int)poogie[1]]; + } + + if (poogie[2] >= 1) + { + return EZlion.Mapper.SkillGuildPoogie.IDName[(int)poogie[2]]; + } + + return effect; + } + + public string GetDivaPrayerGems(QuestsDiva diva) + { + var gems = string.Empty; + var gemTypes = new long?[] { diva.DivaPrayerGemRedSkill, diva.DivaPrayerGemYellowSkill, diva.DivaPrayerGemGreenSkill, diva.DivaPrayerGemBlueSkill }; + var gemLevels = new long?[] { diva.DivaPrayerGemRedLevel, diva.DivaPrayerGemYellowLevel, diva.DivaPrayerGemGreenLevel, diva.DivaPrayerGemBlueLevel }; + + for (var i = 0; i < gemTypes.Length; i++) + { + var skillId = gemTypes[i]; + + if (skillId == null) + { + continue; + } + + if (SkillDivaPrayerGem.IDName.TryGetValue((int)skillId, out var skillName) + && skillName != "None" && skillName != string.Empty) + { + var gemColor = string.Empty; + + switch (i){ + case 0: + gemColor = "Red"; + break; + case 1: + gemColor = "Yellow"; + break; + case 2: + gemColor = "Green"; + break; + case 3: + gemColor = "Blue"; + break; + } + + gems += $"{gemColor} 💎 {skillName} LV{gemLevels[i]}"; + if (i != gemTypes.Length - 1) + { + gems += "\n"; + } + } + } + + return string.IsNullOrEmpty(gems) ? "None" : gems; + } + + /// + /// red yellow green blue. type level. + /// + /// + /// + public string GetDivaPrayerGems(List diva) + { + var gems = string.Empty; + var gemTypes = new int[] { diva[0], diva[2], diva[4], diva[6] }; + var gemLevels = new int[] { diva[1], diva[3], diva[4], diva[5] }; + + for (var i = 0; i < gemTypes.Length; i++) + { + var skillId = gemTypes[i]; + + if (SkillDivaPrayerGem.IDName.TryGetValue((int)skillId, out var skillName) + && skillName != "None" && skillName != string.Empty) + { + var gemColor = string.Empty; + + switch (i) + { + case 0: + gemColor = "Red"; + break; + case 1: + gemColor = "Yellow"; + break; + case 2: + gemColor = "Green"; + break; + case 3: + gemColor = "Blue"; + break; + } + + gems += $"{gemColor} 💎 {skillName} LV{gemLevels[i]}"; + if (i != gemTypes.Length - 1) + { + gems += "\n"; + } + } + } + + return string.IsNullOrEmpty(gems) ? "None" : gems; } public int CalculateTotalLargeMonstersHunted() => this.RoadFatalisSlain() + @@ -9419,7 +10019,7 @@ public string GetWeaponForGuildCard } } - public string GetWeaponDecos => string.Format(CultureInfo.InvariantCulture, "{0}, {1}, {2}", this.GetDecoName(this.WeaponDeco1ID(), 1, true), this.GetDecoName(this.WeaponDeco2ID(), 2, true), this.GetDecoName(this.WeaponDeco3ID(), 3, true)); + public string GetWeaponDecos => string.Format(CultureInfo.InvariantCulture, "{0}, {1}, {2}", this.GetDecoName(this.WeaponDeco1ID(), EquipmentSlot.One, true), this.GetDecoName(this.WeaponDeco2ID(), EquipmentSlot.Two, true), this.GetDecoName(this.WeaponDeco3ID(), EquipmentSlot.Three, true)); public string GetArmorHeadNameForGuildCard { @@ -9513,7 +10113,7 @@ public string GetWeaponDecosForImage if (className is "Blademaster" or "Gunner") { - return string.Format(CultureInfo.InvariantCulture, "{0} | {1} | {2}", this.GetDecoName(this.WeaponDeco1ID(), 1), this.GetDecoName(this.WeaponDeco2ID(), 2), this.GetDecoName(this.WeaponDeco3ID(), 3)); + return string.Format(CultureInfo.InvariantCulture, "{0} | {1} | {2}", this.GetDecoName(this.WeaponDeco1ID(), EquipmentSlot.One), this.GetDecoName(this.WeaponDeco2ID(), EquipmentSlot.Two), this.GetDecoName(this.WeaponDeco3ID(), EquipmentSlot.Three)); } else { @@ -12487,6 +13087,79 @@ public TimeSpan CurrentSessionTime } } + public string GuildFoodTimeLeft + { + get + { + if (GuildFoodStart() <= 0) + { + return "0m"; + } + + var expiry = GuildFoodStart() + (60 * 90); + double secondsLeft = expiry - ServerHeartbeat(); + + if (secondsLeft <= 0) + { + return "0m"; + } + + return $"{Math.Truncate(secondsLeft/60.0)}m"; + } + } + + public string DivaSongTimeLeft + { + get + { + if (DivaSongStart() <= 0) + { + return "0m"; + } + + var expiry = DivaSongStart() + (60 * 90); + double secondsLeft = expiry - ServerHeartbeat(); + + if (secondsLeft <= 0) + { + return "0m"; + } + + return $"{Math.Truncate(secondsLeft / 60.0)}m"; + } + } + + public string CurrentPlayerPosition + { + get + { + var s = (Settings)Application.Current.TryFindResource("Settings"); + + if (s.PlayerPositionMode == "Automatic") + { + if (this.QuestID() != 0) + { + return $"{decimal.Truncate(PlayerPositionInQuestX())}, {decimal.Truncate(PlayerPositionInQuestY())}, {decimal.Truncate(PlayerPositionInQuestZ())}"; + } + else + { + return $"{decimal.Truncate(PlayerPositionX())}, {decimal.Truncate(PlayerPositionY())}, {decimal.Truncate(PlayerPositionZ())}"; + } + + } else if (s.PlayerPositionMode == "Lobby") + { + return $"{decimal.Truncate(PlayerPositionX())}, {decimal.Truncate(PlayerPositionY())}, {decimal.Truncate(PlayerPositionZ())}"; + + } else if (s.PlayerPositionMode == "Quest") + { + return $"{decimal.Truncate(PlayerPositionInQuestX())}, {decimal.Truncate(PlayerPositionInQuestY())}, {decimal.Truncate(PlayerPositionInQuestZ())}"; + + } + + return "?, ?, ?"; + } + } + public int CurrentMonster1MaxHP { get; set; } /// diff --git a/MHFZ_Overlay/Views/Windows/ConfigWindow.xaml b/MHFZ_Overlay/Views/Windows/ConfigWindow.xaml index 728b558f..be7b2a2f 100644 --- a/MHFZ_Overlay/Views/Windows/ConfigWindow.xaml +++ b/MHFZ_Overlay/Views/Windows/ConfigWindow.xaml @@ -258,7 +258,7 @@ - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2342,6 +2409,13 @@ + + + + + + + @@ -2659,6 +2733,52 @@ + + + + + + + + + + + + + + + Automatic + Lobby + Quest + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MHFZ_Overlay/Views/Windows/ConfigWindow.xaml.cs b/MHFZ_Overlay/Views/Windows/ConfigWindow.xaml.cs index 5eba38c1..2e29717b 100644 --- a/MHFZ_Overlay/Views/Windows/ConfigWindow.xaml.cs +++ b/MHFZ_Overlay/Views/Windows/ConfigWindow.xaml.cs @@ -126,7 +126,7 @@ public partial class ConfigWindow : FluentWindow new MonsterLog(29, "Rocks", @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/monster/random.png", 0), new MonsterLog(30, "Ioprey", @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/monster/ioprey.png", 0), new MonsterLog(31, "Iodrome", @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/monster/iodrome.png", 0, true), - new MonsterLog(32, "Pugis", @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/monster/random.png", 0), + new MonsterLog(32, "Poogies", @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/monster/random.png", 0), new MonsterLog(33, "Kirin", @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/monster/kirin.png", 0, true), new MonsterLog(34, "Cephalos", @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/monster/cephalos.png", 0), new MonsterLog(35, "Giaprey / Giadrome", @"pack://application:,,,/MHFZ_Overlay;component/Assets/Icons/png/monster/giaprey.png", 0), @@ -1476,14 +1476,21 @@ private void QuestPaceWeaponComboBox_SelectionChanged(object sender, SelectionCh this.questPaceWeaponSelected = selectedWeapon; - var quests = DatabaseManager.GetQuests(long.Parse(this.QuestIDTextBox.Text.Trim()), this.questPaceWeaponSelected, this.OverlayModeComboBox.Text.Trim()); + var allQuestsRuns = DatabaseManager.GetQuests(long.Parse(this.QuestIDTextBox.Text.Trim()), this.questPaceWeaponSelected, this.OverlayModeComboBox.Text.Trim()); - if (quests.Count == 0) + if (allQuestsRuns.Count == 0) { return; } - - List monster1HPList = GetMonster1HPListForQuestPace(quests); + + List soloQuests = allQuestsRuns.Where(q => q.PartySize == 1).ToList(); + + if (soloQuests.Count == 0) + { + return; + } + + List monster1HPList = GetMonster1HPListForQuestPace(soloQuests); // Filter the quest runs where the monster ID stayed the same var consistentMonsterIdRuns = monster1HPList.Where(questRun => @@ -1525,7 +1532,7 @@ private void QuestPaceWeaponComboBox_SelectionChanged(object sender, SelectionCh // Now, fastestSplitTimes contains the fastest split times for each property var sumOfMedian = medianSplitTimes.Sum() ?? 0; - List finalTimeValues = quests + List finalTimeValues = soloQuests .Select(quest => quest.FinalTimeValue) .ToList(); @@ -1570,7 +1577,7 @@ private void QuestPaceWeaponComboBox_SelectionChanged(object sender, SelectionCh // Now, fastestSplitTimes contains the fastest split times for each property var sumOfMedian = medianSplitTimes.Sum() ?? 0; - List finalTimeValues = quests + List finalTimeValues = soloQuests .Select(quest => quest.FinalTimeValue) .ToList(); @@ -1927,7 +1934,7 @@ private List GetQuestRunPaces(List questsSplits, QuestObje // the pace at x% hp is its value in the current run + all of the rest median values for the other %. paceSplits.FortyPercentRemainingHPFrames = runPace.FortyPercentRemainingHPFrames + medianSplitTimes.TwentyPercentRemainingHPFrames + medianSplitTimes.ZeroPercentRemainingHPFrames; - paceSplits.TwentyPercentRemainingHPFrames = runPace.FortyPercentRemainingHPFrames + runPace.TwentyPercentRemainingHPFrames + + medianSplitTimes.ZeroPercentRemainingHPFrames; + paceSplits.TwentyPercentRemainingHPFrames = runPace.FortyPercentRemainingHPFrames + runPace.TwentyPercentRemainingHPFrames + medianSplitTimes.ZeroPercentRemainingHPFrames; paceSplits.ZeroPercentRemainingHPFrames = runPace.FortyPercentRemainingHPFrames + runPace.TwentyPercentRemainingHPFrames + runPace.ZeroPercentRemainingHPFrames; @@ -2532,6 +2539,7 @@ private void WeaponUsageChart_Loaded(object sender, RoutedEventArgs e) private TextBox? youtubeLinkTextBox; private DataGrid? mostRecentRunsDataGrid; private DataGrid? top20RunsDataGrid; + private DataGrid? uneditedYouTubeLinkRunsDataGrid; private TextBlock? questLogGearStatsTextBlock; private TextBlock? compendiumTextBlock; private CartesianChart? graphChart; @@ -2631,6 +2639,22 @@ private void Top20Runs_DataGridLoaded(object sender, RoutedEventArgs e) this.top20RunsDataGrid.Items.Refresh(); } + private void UneditedYouTubeLinkRuns_DataGridLoaded(object sender, RoutedEventArgs e) + { + this.uneditedYouTubeLinkRunsDataGrid = (DataGrid)sender; + } + + private void YouTubeFindRuns_Click(object sender, RoutedEventArgs e) + { + if (this.uneditedYouTubeLinkRunsDataGrid == null) + { + return; + } + + this.uneditedYouTubeLinkRunsDataGrid.ItemsSource = DatabaseManager.GetUneditedYouTubeLinkRuns(); + this.uneditedYouTubeLinkRunsDataGrid.Items.Refresh(); + } + private string statsGraphsSelectedOption = string.Empty; private string statsTextSelectedOption = string.Empty; @@ -2652,7 +2676,7 @@ public static void SaveCSVFromListOfRecentRuns(List recentRuns, stri { var objectiveImage = run.ObjectiveImage.Replace(",", string.Empty); var questName = run.QuestName.Replace(",", string.Empty); - var youtubeID = run.YoutubeID.Replace(",", string.Empty); + var youtubeID = run.YouTubeID.Replace(",", string.Empty); var finalTimeDisplay = run.FinalTimeDisplay.Replace(",", string.Empty); var actualOverlayMode = run.ActualOverlayMode.Replace(",", string.Empty); @@ -2854,7 +2878,7 @@ public static void SaveCSVFromListOfRecentRuns(ObservableCollection { var objectiveImage = run.ObjectiveImage.Replace(",", string.Empty); var questName = run.QuestName.Replace(",", string.Empty); - var youtubeID = run.YoutubeID.Replace(",", string.Empty); + var youtubeID = run.YouTubeID.Replace(",", string.Empty); var finalTimeDisplay = run.FinalTimeDisplay.Replace(",", string.Empty); var actualOverlayMode = run.ActualOverlayMode.Replace(",", string.Empty); @@ -2874,7 +2898,7 @@ public static void SaveCSVFromListOfFastestRuns(List fastestRuns, st { var objectiveImage = run.ObjectiveImage.Replace(",", string.Empty); var questName = run.QuestName.Replace(",", string.Empty); - var youtubeID = run.YoutubeID.Replace(",", string.Empty); + var youtubeID = run.YouTubeID.Replace(",", string.Empty); var finalTimeDisplay = run.FinalTimeDisplay.Replace(",", string.Empty); var line = $"{objectiveImage},{questName},{run.RunID},{run.QuestID},{youtubeID},{finalTimeDisplay},{run.Date}"; diff --git a/MHFZ_Overlay/Views/Windows/MainWindow.xaml b/MHFZ_Overlay/Views/Windows/MainWindow.xaml index 03455acb..c5517b0c 100644 --- a/MHFZ_Overlay/Views/Windows/MainWindow.xaml +++ b/MHFZ_Overlay/Views/Windows/MainWindow.xaml @@ -149,6 +149,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MHFZ_Overlay/Views/Windows/MainWindow.xaml.cs b/MHFZ_Overlay/Views/Windows/MainWindow.xaml.cs index fbc3f737..f5c96b10 100644 --- a/MHFZ_Overlay/Views/Windows/MainWindow.xaml.cs +++ b/MHFZ_Overlay/Views/Windows/MainWindow.xaml.cs @@ -1306,6 +1306,9 @@ private void SetPlayerStatsVisibility(bool v, Settings s) this.DataLoader.Model.ShowPlayerHitsTakenBlockedInfo = v && s.TotalHitsTakenBlockedShown; this.DataLoader.Model.ShowSharpness = v && s.EnableSharpness; this.DataLoader.Model.ShowSessionTimeInfo = v && s.SessionTimeShown; + this.DataLoader.Model.ShowPlayerPositionInfo = v && s.PlayerPositionShown; + this.DataLoader.Model.ShowDivaSongTimer = v && s.DivaSongTimerShown && this.DataLoader.Model.DivaSongActive; + this.DataLoader.Model.ShowGuildFoodTimer = v && s.GuildFoodTimerShown && this.DataLoader.Model.GuildFoodActive; this.DataLoader.Model.ShowMap = v && s.EnableMap; this.DataLoader.Model.ShowFrameCounter = v && s.FrameCounterShown; @@ -1334,8 +1337,6 @@ private void SetPlayerStatsVisibility(bool v, Settings s) private bool clickThrough { get; set; } = true; - private bool calculatedPersonalBest { get; set; } - private ConfigWindow? ConfigWindow { get; set; } public void EnableDragAndDrop() @@ -1472,6 +1473,18 @@ private void MainGrid_DragOver(object sender, DragEventArgs e) s.SessionTimeX = (double)(pos.X - this.xOffset); s.SessionTimeY = (double)(pos.Y - this.yOffset); break; + case "PlayerPositionInfo": + s.PlayerPositionX = (double)(pos.X - this.xOffset); + s.PlayerPositionY = (double)(pos.Y - this.yOffset); + break; + case "DivaSongTimer": + s.DivaSongTimerX = (double)(pos.X - this.xOffset); + s.DivaSongTimerY = (double)(pos.Y - this.yOffset); + break; + case "GuildFoodTimer": + s.GuildFoodTimerX = (double)(pos.X - this.xOffset); + s.GuildFoodTimerY = (double)(pos.Y - this.yOffset); + break; case "LocationTextInfo": s.LocationTextX = (double)(pos.X - this.xOffset); s.LocationTextY = (double)(pos.Y - this.yOffset); @@ -1681,6 +1694,9 @@ private void ToggleOverlayBorders() this.PersonalBestAttemptsInfoBorder.BorderThickness = thickness; this.QuestNameInfoBorder.BorderThickness = thickness; this.SessionTimeInfoBorder.BorderThickness = thickness; + this.PlayerPositionInfoBorder.BorderThickness = thickness; + this.DivaSongTimerBorder.BorderThickness = thickness; + this.GuildFoodTimerBorder.BorderThickness = thickness; this.SharpnessInfoBorder.BorderThickness = thickness; this.TimerInfoBorder.BorderThickness = thickness; this.GamepadGridBorder.BorderThickness = thickness; @@ -1733,8 +1749,6 @@ private void ToggleClickThrough() private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) => this.DisableDragAndDrop(); - private bool calculatedQuestAttempts { get; set; } - // TODO fix alt tab issues? private IKeyboardMouseEvents GlobalHook { get; set; } @@ -1776,66 +1790,41 @@ public void Unsubscribe() this.GlobalHook.Dispose(); } - private async Task UpdateQuestAttempts() + private void UpdateQuestDataForDisplay() { var category = this.DataLoader.Model.GetOverlayModeForStorage(); var weaponType = this.DataLoader.Model.WeaponType(); long questID = this.DataLoader.Model.QuestID(); + long partySize = this.DataLoader.Model.PartySize(); - var attempts = await Task.Run(() => DatabaseManagerInstance.UpsertQuestAttemptsAsync(questID, weaponType, category)); var s = (Settings)Application.Current.TryFindResource("Settings"); var completions = string.Empty; + var attemptsPerPersonalBest = 0.0; + + var pbAttempts = DatabaseManagerInstance.UpsertPersonalBestAttempts(questID, weaponType, category, partySize); + var questAttempts = DatabaseManagerInstance.UpsertQuestAttempts(questID, weaponType, category, partySize); + if (s.EnableQuestCompletionsCounter) { - completions = await Task.Run(() => DatabaseManagerInstance.GetQuestCompletionsAsync(questID, category, weaponType)) + "/"; + completions = DatabaseManagerInstance.GetQuestCompletions(questID, category, weaponType, partySize) + "/"; } - var _ = Dispatcher.BeginInvoke((Action)(() => + if (s.EnableAttemptsPerPersonalBest) { - this.questAttemptsTextBlock.Text = $"{completions}{attempts}"; - })); - } + attemptsPerPersonalBest = DatabaseManagerInstance.GetQuestAttemptsPerPersonalBest(questID, weaponType, category, questAttempts.ToString(CultureInfo.InvariantCulture), partySize); + } - private async Task UpdatePersonalBestDisplay() - { - var category = this.DataLoader.Model.GetOverlayModeForStorage(); - var weaponType = this.DataLoader.Model.WeaponType(); - long questID = this.DataLoader.Model.QuestID(); + // TODO putting this first before the others triggers "database is locked" error + this.DataLoader.Model.PersonalBestLoaded = DatabaseManagerInstance.GetPersonalBest(questID, weaponType, category, ViewModels.Windows.AddressModel.QuestTimeMode, this.DataLoader, partySize); - this.DataLoader.Model.PersonalBestLoaded = await Task.Run(() => DatabaseManagerInstance.GetPersonalBestAsync(questID, weaponType, category, ViewModels.Windows.AddressModel.QuestTimeMode, this.DataLoader)); var _ = Dispatcher.BeginInvoke((Action)(() => { + this.questAttemptsTextBlock.Text = $"{completions}{questAttempts}"; this.personalBestTextBlock.Text = this.DataLoader.Model.PersonalBestLoaded; + this.personalBestAttemptsTextBlock.Text = s.EnableAttemptsPerPersonalBest ? string.Format(CultureInfo.InvariantCulture, "{0} ({1}/PB)", pbAttempts, Math.Truncate(attemptsPerPersonalBest)) : string.Format(CultureInfo.InvariantCulture, "{0}", pbAttempts); })); } - private async Task UpdatePersonalBestAttempts() - { - var category = this.DataLoader.Model.GetOverlayModeForStorage(); - var weaponType = this.DataLoader.Model.WeaponType(); - long questID = this.DataLoader.Model.QuestID(); - - var attempts = await Task.Run(() => DatabaseManagerInstance.UpsertPersonalBestAttemptsAsync(questID, weaponType, category)); - var attemptsPerPersonalBest = 0.0; - var s = (Settings)Application.Current.TryFindResource("Settings"); - - if (s.EnableAttemptsPerPersonalBest) - { - attemptsPerPersonalBest = await Task.Run(() => DatabaseManagerInstance.GetQuestAttemptsPerPersonalBestAsync(questID, weaponType, category)); - var _ = Dispatcher.BeginInvoke((Action)(() => - { - this.personalBestAttemptsTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "{0} ({1}/PB)", attempts, Math.Truncate(attemptsPerPersonalBest)); - })); - } - else - { - var _ = Dispatcher.BeginInvoke((Action)(() => - { - this.personalBestAttemptsTextBlock.Text = string.Format(CultureInfo.InvariantCulture, "{0}", attempts); - })); - } - } - /// /// Gets the mezeporta festival minigame score depending on area id. /// @@ -1916,6 +1905,13 @@ private void CheckMezFesScore() } } + private bool CalculatedQuestDataForDisplay { get; set; } + + private bool DivaSongActive { get; set; } + + private bool GuildFoodActive { get; set; } + + // TODO: optimization private Task CheckQuestStateForDatabaseLogging() { @@ -1942,23 +1938,15 @@ private Task CheckQuestStateForDatabaseLogging() this.DataLoader.Model.InsertQuestInfoIntoDictionaries(); // TODO: test on dure/etc - if (!this.calculatedPersonalBest - && this.DataLoader.Model.TimeDefInt() > this.DataLoader.Model.TimeInt() - && playerAtk > 0 - && this.DataLoader.Model.TimeDefInt() - this.DataLoader.Model.TimeInt() >= 30) - { - this.calculatedPersonalBest = true; - var updatePersonalBestDisplayTask = this.UpdatePersonalBestDisplay(); // Start the task without awaiting - } - - if (!this.calculatedQuestAttempts + if (!this.CalculatedQuestDataForDisplay && this.DataLoader.Model.TimeDefInt() > this.DataLoader.Model.TimeInt() && playerAtk > 0 && this.DataLoader.Model.TimeDefInt() - this.DataLoader.Model.TimeInt() >= 30) { - this.calculatedQuestAttempts = true; - var updateQuestAttemptsTask = this.UpdateQuestAttempts(); // Start the task without awaiting - var updatePersonalBestAttemptsTask = this.UpdatePersonalBestAttempts(); // Start the task without awaiting + this.CalculatedQuestDataForDisplay = true; + this.UpdateQuestDataForDisplay(); + this.DataLoader.Model.DivaSongActive = this.DataLoader.Model.DivaSongEnded ? false : true; + this.DataLoader.Model.GuildFoodActive = this.DataLoader.Model.GuildFoodEnded ? false : true; } } @@ -1971,8 +1959,10 @@ private Task CheckQuestStateForDatabaseLogging() this.DataLoader.Model.ResetQuestInfoVariables(); this.DataLoader.Model.PreviousRoadFloor = 0; this.personalBestTextBlock.Text = Messages.TimerNotLoaded; - this.calculatedPersonalBest = false; - this.calculatedQuestAttempts = false; + this.CalculatedQuestDataForDisplay = false; + // TODO test + this.DataLoader.Model.DivaSongActive = this.DataLoader.Model.DivaSongEnded ? false : true; + this.DataLoader.Model.GuildFoodActive = this.DataLoader.Model.GuildFoodEnded ? false : true; return Task.CompletedTask; } else if (!this.DataLoader.Model.LoadedItemsAtQuestStart && this.DataLoader.Model.QuestState() == 0 && this.DataLoader.Model.QuestID() != 0) @@ -2004,7 +1994,7 @@ private Task CheckQuestStateForDatabaseLogging() this.DataLoader.Model.LoadedItemsAtQuestStart = false; if (s.EnableQuestLogging) { - DatabaseManagerInstance.InsertQuestData(this.DataLoader, (int)DatabaseManagerInstance.GetQuestAttempts(this.DataLoader.Model.QuestID(), this.DataLoader.Model.WeaponType(), this.OverlayModeWatermarkTextBlock.Text)); + DatabaseManagerInstance.InsertQuestData(this.DataLoader, (int)DatabaseManagerInstance.GetQuestAttempts(this.DataLoader.Model.QuestID(), this.DataLoader.Model.WeaponType(), this.DataLoader.Model.GetOverlayModeForStorage())); } } diff --git a/README.md b/README.md index a7291df5..7bedebce 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,10 @@ Includes icons! - [x] [Discord Rich Presence](./FAQ.md/#how-to-enable-discord-rich-presence) (custom monster icons, colored weapons, quest tier, current area, [speedrun mode, zen mode](./FAQ.md/#how-to-enable-speedrun-categories--zen-mode), and more!). +![Buffs with Timers](./demo/buffs.png) + +- [x] Diva Song and Guild Food timers. Keep track of your buffs! + ![Discord Rich Presence](./demo/discord5.png) ![Discord Rich Presence](./demo/discord10.png) @@ -320,7 +324,6 @@ Some sections have context menus where you can save the section contents to a fi - Handle multiple objectives information. - Zenith information in Road. - Raviente Support Part Info. -- Guild Pugi address. - Armor Set Website links. - Sky Corridor. - Drag and Drop multiple selection. diff --git a/demo/buffs.png b/demo/buffs.png new file mode 100644 index 00000000..03e49281 Binary files /dev/null and b/demo/buffs.png differ diff --git a/package-lock.json b/package-lock.json index 90b528a1..e27a4e5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mhfz_overlay", - "version": "0.33.0", + "version": "0.34.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mhfz_overlay", - "version": "0.33.0", + "version": "0.34.0", "license": "MIT", "dependencies": { "@release-it/conventional-changelog": "^7.0.1", diff --git a/package.json b/package.json index fd2783fd..f17a7eb8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mhfz_overlay", - "version": "0.33.0", + "version": "0.34.0", "description": "[![Monster Hunter Frontier Z Overlay v0.21.0 Preview](./demo/youtubepreview1.jpg)](https://www.youtube.com/watch?v=A9ffbRICqZY \"Monster Hunter Frontier Z Overlay v0.21.0 Preview\")", "main": "MHFZ_Overlay.sln", "scripts": {