diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ee5385 --- /dev/null +++ b/.gitignore @@ -0,0 +1,362 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd diff --git a/README.md b/README.md new file mode 100644 index 0000000..4f015fc --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +## 一、功能概述 + +懒得添加桌面图标,下载发布的exe直接双击运行,配置后会在同目录产生config.db配置数据文件 + +| 功能概述 | 功能概述 | +| -- | -------- | +| 🎉 自动接受对局 | 自动接受对局 | +| 🎉 自动禁用英雄 | 按照列表顺序优先禁用英雄,也可根据列表随机禁用英雄 | +| 🎉 自动选择英雄 | 排位赛根据游戏中真实位置选择英雄,匹配模式根据设置的位置选择英雄,根据列表顺序选择,也可根据位置列表随机选择 | +| 🎉 自动设置预设符文 | 根据选择英雄的配置预设符文,预设符文由符文页页面管理 | + +![助手.png](助手.png) + +## 二、目的 + +​ 编写这个工具的主要原因有: + +​ 1、已经接受对局了,老婆让我去晾衣服🫠🫠🫠 + +​ 2、和朋友玩儿匹配的时候抢英雄😁😁😁 + +​ 3、基本没有画过桌面客户端,随便回忆下学的内容🤔🤔🤔 + +## 三、资料查询 + +​ [Github LCU 主题](https://github.com/topics/lcu-api)、[官网文档](https://developer.riotgames.com/)、 [社区文档](https://riot-api-libraries.readthedocs.io/en/latest/index.html) + +​ https://discord.com/channels/ + +​ 博客:https://hextechdocs.dev/ + +​ swagger:https://www.mingweisamuel.com/lcu-schema/tool/#/ + +### 1、websocket + +​ https://hextechdocs.dev/getting-started-with-the-lcu-websocket/ + +### 2、项目参考 + +​ PoniLCU:https://github.com/Ponita0/PoniLCU + +​ https://github.com/real-web-world/hh-lol-prophet/blob/main/services/lcu/api.go + +​ https://github.com/Terevenen2/LOL-Client-TOOL/blob/avalonia/MainWindow.axaml.cs + +​ https://github.com/XHXIAIEIN/LeagueCustomLobby + +### 4、数据信息 + +​ 英雄列表:https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js + +​ 头像地址:https://game.gtimg.cn/images/lol/act/img/champion/Annie.png + +​ 英雄详情信息:https://game.gtimg.cn/images/lol/act/img/js/hero/1.js + +​ 装备信息:https://game.gtimg.cn/images/lol/act/img/js/items_ext/items_ext.js + +​ 符文信息:https://game.gtimg.cn/images/lol/act/img/js/runeList/rune_list.js + +​ 数据分析:https://www.op.gg/ + + +## 四、获取端点信息 + +获取启动信息 `wmic process where name='LeagueClientUx.exe' GET commandline ` + +找到参数--remoting-auth-token、--app-port + + + diff --git a/lol-assist.sln b/lol-assist.sln new file mode 100644 index 0000000..076c032 --- /dev/null +++ b/lol-assist.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{72564A64-8F77-4221-9E81-6F08A3B2F73E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LOL.Assist.App", "src\LOL.Assist.App\LOL.Assist.App.csproj", "{6E9E2817-D723-49D1-9F7E-AECB8454015F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LOL.Assist.Core", "src\LOL.Assist.Core\LOL.Assist.Core.csproj", "{315A3128-E7DD-4709-89CF-745FE8E2642F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LOL.Assist.Swagger", "src\LOL.Assist.Swagger\LOL.Assist.Swagger.csproj", "{7BE0491E-00EA-4517-9B28-F229BE9DCFAE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6E9E2817-D723-49D1-9F7E-AECB8454015F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E9E2817-D723-49D1-9F7E-AECB8454015F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E9E2817-D723-49D1-9F7E-AECB8454015F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E9E2817-D723-49D1-9F7E-AECB8454015F}.Release|Any CPU.Build.0 = Release|Any CPU + {315A3128-E7DD-4709-89CF-745FE8E2642F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {315A3128-E7DD-4709-89CF-745FE8E2642F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {315A3128-E7DD-4709-89CF-745FE8E2642F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {315A3128-E7DD-4709-89CF-745FE8E2642F}.Release|Any CPU.Build.0 = Release|Any CPU + {7BE0491E-00EA-4517-9B28-F229BE9DCFAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7BE0491E-00EA-4517-9B28-F229BE9DCFAE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BE0491E-00EA-4517-9B28-F229BE9DCFAE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7BE0491E-00EA-4517-9B28-F229BE9DCFAE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {6E9E2817-D723-49D1-9F7E-AECB8454015F} = {72564A64-8F77-4221-9E81-6F08A3B2F73E} + {315A3128-E7DD-4709-89CF-745FE8E2642F} = {72564A64-8F77-4221-9E81-6F08A3B2F73E} + {7BE0491E-00EA-4517-9B28-F229BE9DCFAE} = {72564A64-8F77-4221-9E81-6F08A3B2F73E} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A4E8E314-8CEE-426F-A541-D14273BB85D4} + EndGlobalSection +EndGlobal diff --git a/src/LOL.Assist.App/App.xaml b/src/LOL.Assist.App/App.xaml new file mode 100644 index 0000000..eb9f5a7 --- /dev/null +++ b/src/LOL.Assist.App/App.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/src/LOL.Assist.App/App.xaml.cs b/src/LOL.Assist.App/App.xaml.cs new file mode 100644 index 0000000..2f1c044 --- /dev/null +++ b/src/LOL.Assist.App/App.xaml.cs @@ -0,0 +1,60 @@ +using CommunityToolkit.Mvvm.DependencyInjection; +using FreeSql; +using LOL.Assist.App.ViewModels; +using LOL.Assist.App.Views; +using LOL.Assist.Core; +using LOL.Assist.Core.DbModels; +using LOL.Assist.Core.Enums; +using LOL.Assist.Core.IServices; +using LOL.Assist.Core.Models; +using LOL.Assist.Core.Services; +using Microsoft.Extensions.DependencyInjection; +using Refit; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Navigation; +using Wpf.Ui.Mvvm.Contracts; +using Wpf.Ui.Mvvm.Services; + +namespace LOL.Assist.App +{ + + /// + /// Interaction logic for App.xaml + /// + public partial class App + { + private static LolConnect _lolConnect=new LolConnect(); + protected override void OnStartup(StartupEventArgs e) + { + IServiceCollection services = new ServiceCollection() + .AddSingleton(); + + services.AddSingleton(RestService.For(_lolConnect.GetHttpClient())); + RefitSettings settings = new RefitSettings(new NewtonsoftJsonContentSerializer()); + services.AddSingleton(RestService.For("https://lol.qq.com", settings)); + services.AddSingleton(RestService.For("https://game.gtimg.cn", settings)); + services.AddSingleton(new FreeSqlBuilder() + .UseConnectionString(DataType.Sqlite, $"Data Source={AppContext.BaseDirectory}config.db") + .UseAutoSyncStructure(true).Build()); + services.AddScoped(); + services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + Ioc.Default.ConfigureServices(services.BuildServiceProvider()); + base.OnStartup(e); + } + protected override void OnLoadCompleted(NavigationEventArgs e) + { + _lolConnect=_lolConnect.Connect(); + } + + } + +} diff --git a/src/LOL.Assist.App/AssemblyInfo.cs b/src/LOL.Assist.App/AssemblyInfo.cs new file mode 100644 index 0000000..2211234 --- /dev/null +++ b/src/LOL.Assist.App/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly:ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/src/LOL.Assist.App/Global.cs b/src/LOL.Assist.App/Global.cs new file mode 100644 index 0000000..14d5cf0 --- /dev/null +++ b/src/LOL.Assist.App/Global.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using LOL.Assist.Core.Enums; +using LOL.Assist.Core.Models; +using LOL.Assist.Core.Models.SubscribeMessage; +using Newtonsoft.Json.Linq; + +namespace LOL.Assist.App; + +/// +/// 全局配置信息 +/// +public static class Global +{ + /// + /// 角色列表 + /// + public static List Heroes { get; set; } = new List(); + + /// + /// 位置信息 + /// + public static List Positions = new List + { + new Position("上路","/Resources/Position_Challenger-Top.png","top"), + new Position("打野","/Resources/Position_Challenger-Jungle.png","jungle"), + new Position("中路","/Resources/Position_Challenger-Mid.png","middle"), + new Position("下路","/Resources/Position_Challenger-Bot.png","bottom"), + new Position("辅助","/Resources/Position_Challenger-Support.png","utility"), + }; + + /// + /// 符文列表 + /// + public static List Runes { get; set; } = new List(); + + /// + /// 当前游戏开始cell id + /// + public static int? LocalPlayerCellId { get; set; } + /// + /// 存储会话信息 + /// + /// + public static void GameSessionHandle(JToken gameFlow, string uri, string type) + { + LocalPlayerCellId = gameFlow.ToObject()?.LocalPlayerCellId; + } + +} \ No newline at end of file diff --git a/src/LOL.Assist.App/LOL.Assist.App.csproj b/src/LOL.Assist.App/LOL.Assist.App.csproj new file mode 100644 index 0000000..998b619 --- /dev/null +++ b/src/LOL.Assist.App/LOL.Assist.App.csproj @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + WinExe + net7.0-windows + enable + true + + + diff --git a/src/LOL.Assist.App/Model/HeroRuneGroup.cs b/src/LOL.Assist.App/Model/HeroRuneGroup.cs new file mode 100644 index 0000000..32ec225 --- /dev/null +++ b/src/LOL.Assist.App/Model/HeroRuneGroup.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using CommunityToolkit.Mvvm.ComponentModel; +using LOL.Assist.Core.DbModels; +using LOL.Assist.Core.Models; + +namespace LOL.Assist.App.Model; + +public partial class HeroRuneGroup:ObservableObject +{ + public HeroRune HeroRuneBase{get;set;} + /// + /// 主要系别符文信息 + /// + public List Primary { get; set; } + + /// + /// 副系及成长符文信息 + /// + public List SecondGrowing { get; set; } +} \ No newline at end of file diff --git a/src/LOL.Assist.App/Model/RuneGroup.cs b/src/LOL.Assist.App/Model/RuneGroup.cs new file mode 100644 index 0000000..11d0cfe --- /dev/null +++ b/src/LOL.Assist.App/Model/RuneGroup.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Windows; +using CommunityToolkit.Mvvm.ComponentModel; +using LOL.Assist.Core.Models; + +namespace LOL.Assist.App.Model; +/// +/// 符文分组信息 +/// +public partial class RuneGroup:ObservableObject +{ + /// + /// 主系选中状态 + /// + [ObservableProperty] + private double _primaryOpacity = 0.4; + /// + /// 副系显隐藏 + /// Visible + /// Hidden + /// Collapsed + /// + [ObservableProperty] + private Visibility _secondVisibility; + /// + /// 副系选中状态 + /// + [ObservableProperty] + private double _secondOpacity = 0.4; + /// + /// 系别 + /// + [ObservableProperty] private RuneResponse _root; + /// + /// 基石信息 + /// + public List Base { get; set; } + + /// + /// 一级符文信息 + /// + public List Valiant { get; set; } + /// + /// 二级符文信息 + /// + public List Legend { get; set; } + /// + /// 三级符文信息 + /// + public List Combat { get; set; } +} diff --git a/src/LOL.Assist.App/Resources/Position_Challenger-Bot.png b/src/LOL.Assist.App/Resources/Position_Challenger-Bot.png new file mode 100644 index 0000000..48d922a Binary files /dev/null and b/src/LOL.Assist.App/Resources/Position_Challenger-Bot.png differ diff --git a/src/LOL.Assist.App/Resources/Position_Challenger-Jungle.png b/src/LOL.Assist.App/Resources/Position_Challenger-Jungle.png new file mode 100644 index 0000000..1643e54 Binary files /dev/null and b/src/LOL.Assist.App/Resources/Position_Challenger-Jungle.png differ diff --git a/src/LOL.Assist.App/Resources/Position_Challenger-Mid.png b/src/LOL.Assist.App/Resources/Position_Challenger-Mid.png new file mode 100644 index 0000000..3166174 Binary files /dev/null and b/src/LOL.Assist.App/Resources/Position_Challenger-Mid.png differ diff --git a/src/LOL.Assist.App/Resources/Position_Challenger-Support.png b/src/LOL.Assist.App/Resources/Position_Challenger-Support.png new file mode 100644 index 0000000..a61e184 Binary files /dev/null and b/src/LOL.Assist.App/Resources/Position_Challenger-Support.png differ diff --git a/src/LOL.Assist.App/Resources/Position_Challenger-Top.png b/src/LOL.Assist.App/Resources/Position_Challenger-Top.png new file mode 100644 index 0000000..45f9b0e Binary files /dev/null and b/src/LOL.Assist.App/Resources/Position_Challenger-Top.png differ diff --git a/src/LOL.Assist.App/Resources/Resources.Designer.cs b/src/LOL.Assist.App/Resources/Resources.Designer.cs new file mode 100644 index 0000000..8f6662a --- /dev/null +++ b/src/LOL.Assist.App/Resources/Resources.Designer.cs @@ -0,0 +1,113 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace LOL.Assist.App.Resources { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LOL.Assist.App.Resources.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap Position_Challenger_Bot { + get { + object obj = ResourceManager.GetObject("Position_Challenger-Bot", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap Position_Challenger_Jungle { + get { + object obj = ResourceManager.GetObject("Position_Challenger-Jungle", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap Position_Challenger_Mid { + get { + object obj = ResourceManager.GetObject("Position_Challenger-Mid", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap Position_Challenger_Support { + get { + object obj = ResourceManager.GetObject("Position_Challenger-Support", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap Position_Challenger_Top { + get { + object obj = ResourceManager.GetObject("Position_Challenger-Top", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/src/LOL.Assist.App/Resources/Resources.resx b/src/LOL.Assist.App/Resources/Resources.resx new file mode 100644 index 0000000..dcf4bdc --- /dev/null +++ b/src/LOL.Assist.App/Resources/Resources.resx @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\Position_Challenger-Bot.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Position_Challenger-Jungle.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Position_Challenger-Mid.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Position_Challenger-Support.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Position_Challenger-Top.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/src/LOL.Assist.App/ViewModels/RunePopupViewModel.cs b/src/LOL.Assist.App/ViewModels/RunePopupViewModel.cs new file mode 100644 index 0000000..cd5e3a5 --- /dev/null +++ b/src/LOL.Assist.App/ViewModels/RunePopupViewModel.cs @@ -0,0 +1,204 @@ +using System; +using CommunityToolkit.Mvvm.ComponentModel; +using LOL.Assist.Core.DbModels; +using LOL.Assist.Core.Models; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Newtonsoft.Json; +using RuneGroup = LOL.Assist.App.Model.RuneGroup; +using System.Windows.Controls; +using CommunityToolkit.Mvvm.DependencyInjection; +using LOL.Assist.Core.IServices; + +namespace LOL.Assist.App.ViewModels +{ + public partial class RunePopupViewModel : ObservableObject + { + private static readonly IEnumerable ValiantStyleNames = new List { "英武", "预谋", "宝物", "巧具", "蛮力" }; + private static readonly IEnumerable LegendStyleNames = new List { "传说", "追踪", "卓越", "未来", "抵抗" }; + private static readonly IEnumerable CombatStyleNames = new List { "战斗", "狩猎", "威能", "超越", "生机" }; + /// + /// 符文 + /// + [ObservableProperty] + private ObservableCollection _showRunes; + /// + /// 成长符文 + /// + [ObservableProperty] + private RuneGroup _showGrowingRunes; + + [ObservableProperty] + private HeroRune _selectHeroRune; + + private readonly IDbService _dbService; + /// + /// + /// + public RunePopupViewModel() + { + _dbService = Ioc.Default.GetService()!; + RefreshReloadRune(); + } + + public void RefreshReloadRune() + { + IEnumerable runeGroup = Global.Runes + .Where(p => !string.IsNullOrWhiteSpace(p.Key) && string.IsNullOrWhiteSpace(p.SlotLabel) && + string.IsNullOrWhiteSpace(p.StyleName)).Select(runeResponse => + { + List> groupBy = Global.Runes + .Where(p => p.StyleName == runeResponse.Name) + .GroupBy(p => p.SlotLabel) + .ToList(); + + return new RuneGroup + { + Root = runeResponse, + Base = groupBy.FirstOrDefault(p => p.Key == "基石")!.ToList(), + Valiant = groupBy.FirstOrDefault(p => ValiantStyleNames.Contains(p.Key))!.ToList(), + Legend = groupBy.FirstOrDefault(p => LegendStyleNames.Contains(p.Key))!.ToList(), + Combat = groupBy.FirstOrDefault(p => CombatStyleNames.Contains(p.Key))!.ToList(), + }; + }); + ShowRunes = new ObservableCollection(runeGroup); + ShowGrowingRunes = new RuneGroup + { + Valiant = GetGrowingRues(new int[] { 5008, 5005, 5007 }), + Legend = GetGrowingRues(new int[] { 5008, 5002, 5003 }), + Combat = GetGrowingRues(new int[] { 5001, 5002, 5003 }), + }; + SelectHeroRune = null; + } + + private List GetGrowingRues(int[] ids) + { + return ids.Select(p => + { + RuneResponse source = Global.Runes.FirstOrDefault(px => px.Id == p)!; + return new RuneResponse + { + GroupName = source.GroupName, + Icon = source.Icon, + Id = source.Id, + IsChecked = source.IsChecked, + Key = source.Key, + LongDesc = source.LongDesc, + Name = source.Name, + Opacity = source.Opacity, + ShortDesc = source.ShortDesc, + SlotLabel = source.SlotLabel, + StyleName = source.StyleName, + Tooltip = source.Tooltip, + }; + }).ToList(); + } + + /// + /// 获取英雄选中服务信息 + /// + /// + public void SaveSelectHeroRune() + { + if (SelectHeroRune == null|| SelectHeroRune.ChampionId <=0) + return; + RuneGroup defaultRuneGroup = new RuneGroup() { Root = new RuneResponse() }; + RuneGroup primaryOpacity = ShowRunes.FirstOrDefault(p => Math.Abs(p.PrimaryOpacity - 1) == 0) ?? defaultRuneGroup; + RuneGroup secondRunes = ShowRunes.FirstOrDefault(p => Math.Abs(p.SecondOpacity - 1) == 0) ?? defaultRuneGroup; + Dictionary selectRune = new Dictionary() + { + { + primaryOpacity.Root.Id, new int[4] + { + GetRuneCheckId(primaryOpacity.Base), + GetRuneCheckId(primaryOpacity.Valiant), + GetRuneCheckId(primaryOpacity.Legend), + GetRuneCheckId(primaryOpacity.Combat), + } + }, + { + secondRunes.Root.Id==0?1:secondRunes.Root.Id, new int[3] + { + GetRuneCheckId(secondRunes.Valiant), + GetRuneCheckId(secondRunes.Legend), + GetRuneCheckId(secondRunes.Combat), + } + }, + { + 2, new int[3] + { + GetRuneCheckId(ShowGrowingRunes.Valiant), + GetRuneCheckId(ShowGrowingRunes.Legend), + GetRuneCheckId(ShowGrowingRunes.Combat), + } + } + }; + string runeJson = JsonConvert.SerializeObject(selectRune); + SelectHeroRune!.RuneJson = runeJson; + _dbService.InsertOrUpdate(SelectHeroRune); + } + /// + /// 设置英雄选中服务信息 + /// + /// + public void SetSelectHeroRune(HeroRune? heroRune, TabControl primaryTab, TabControl secondTab) + { + SelectHeroRune = heroRune; + if (heroRune == null || string.IsNullOrWhiteSpace(heroRune.RuneJson)) + return; + Dictionary? selectRune = JsonConvert.DeserializeObject>(heroRune.RuneJson); + if (selectRune is not { Count: 3 }) + return; + foreach (var kvp in selectRune) + { + //成长符文 + if (kvp is { Key: 2, Value.Length: 3 }) + { + SetRuneCheckId(ShowGrowingRunes.Valiant, kvp.Value[0]); + SetRuneCheckId(ShowGrowingRunes.Legend, kvp.Value[1]); + SetRuneCheckId(ShowGrowingRunes.Combat, kvp.Value[2]); + continue; + } + + RuneGroup? rootRune = ShowRunes.FirstOrDefault(p => p.Root.Id == kvp.Key); + if (rootRune == null) + continue; + switch (kvp.Value.Length) + { + //主系 + case 4: + primaryTab.SelectedItem = rootRune; + SetRuneCheckId(rootRune.Base, kvp.Value[0]); + SetRuneCheckId(rootRune.Valiant, kvp.Value[1]); + SetRuneCheckId(rootRune.Legend, kvp.Value[2]); + SetRuneCheckId(rootRune.Combat, kvp.Value[3]); + break; + //副系 + case 3: + secondTab.SelectedItem = rootRune; + SetRuneCheckId(rootRune.Valiant, kvp.Value[0]); + SetRuneCheckId(rootRune.Legend, kvp.Value[1]); + SetRuneCheckId(rootRune.Combat, kvp.Value[2]); + break; + } + } + } + + private int GetRuneCheckId(IEnumerable? runes) + { + if (runes == null) + return 0; + return runes.FirstOrDefault(p => p.IsChecked)?.Id ?? 0; + } + private void SetRuneCheckId(IEnumerable? runes, int id) + { + if (runes == null || id == 0) + return; + RuneResponse? setRune = runes.FirstOrDefault(p => p.Id == id); + if (setRune == null) + return; + setRune.IsChecked = true; + } + } +} diff --git a/src/LOL.Assist.App/ViewModels/RuneViewModel.cs b/src/LOL.Assist.App/ViewModels/RuneViewModel.cs new file mode 100644 index 0000000..d98751d --- /dev/null +++ b/src/LOL.Assist.App/ViewModels/RuneViewModel.cs @@ -0,0 +1,91 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.DependencyInjection; +using LOL.Assist.App.Model; +using LOL.Assist.Core.DbModels; +using LOL.Assist.Core.IServices; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Newtonsoft.Json; +using LOL.Assist.Core.Models; + +namespace LOL.Assist.App.ViewModels +{ + public partial class RuneViewModel : ObservableObject + { + [ObservableProperty] private string _searchText = string.Empty; + [ObservableProperty] private ObservableCollection? _heroRunes; + private readonly IDbService _dbService; + + public RuneViewModel() + { + _dbService = Ioc.Default.GetService()!; + RefreshHeroRune(); + } + partial void OnSearchTextChanged(string value) + { + RefreshHeroRune(); + } + + public void RemoveHeroRune(int championId) + { + _dbService.Delete(p => p.ChampionId == championId); + HeroRuneGroup? removeHero = HeroRunes?.FirstOrDefault(p => p.HeroRuneBase.ChampionId == championId); + if (removeHero == null) + return; + HeroRunes!.Remove(removeHero); + } + public void RefreshHeroRune() + { + IList data = _dbService.GetAll(string.IsNullOrWhiteSpace(SearchText) ? p => true : p => p.KeyWords.Contains(SearchText) || p.Name.Contains(SearchText)); + HeroRunes = new ObservableCollection(data.Select(p => + { + HeroRuneGroup heroRuneGroup = new HeroRuneGroup + { + HeroRuneBase = p, + Primary = new List(), + SecondGrowing = new List(), + }; + if (string.IsNullOrWhiteSpace(p.RuneJson)) + return heroRuneGroup; + Dictionary? selectRune = JsonConvert.DeserializeObject>(p.RuneJson); + if (selectRune is not { Count: 3 }) + return heroRuneGroup; + + foreach (var kvp in selectRune) + { + //成长符文 + if (kvp is { Key: 2, Value.Length: 3 }) + { + heroRuneGroup.SecondGrowing.AddRange(kvp.Value + .Where(runeId => runeId != 0) + .Select(px => Global.Runes.FirstOrDefault(pj => px == pj.Id) ?? new RuneResponse())); + continue; + } + + RuneResponse? rootRune = Global.Runes.FirstOrDefault(px => px.Id == kvp.Key); + if (rootRune == null) + continue; + switch (kvp.Value.Length) + { + //主系 + case 4: + heroRuneGroup.Primary.Add(rootRune); + heroRuneGroup.Primary.AddRange(kvp.Value + .Where(runeId => runeId != 0) + .Select(px => Global.Runes.FirstOrDefault(pj => px == pj.Id) ?? new RuneResponse())); + break; + //副系 + case 3: + heroRuneGroup.SecondGrowing.Add(rootRune); + heroRuneGroup.SecondGrowing.AddRange(kvp.Value + .Where(runeId => runeId != 0) + .Select(px => Global.Runes.FirstOrDefault(pj => px == pj.Id) ?? new RuneResponse())); + break; + } + } + return heroRuneGroup; + })); + } + } +} diff --git a/src/LOL.Assist.App/ViewModels/SelectHeroViewModel.cs b/src/LOL.Assist.App/ViewModels/SelectHeroViewModel.cs new file mode 100644 index 0000000..fffd83f --- /dev/null +++ b/src/LOL.Assist.App/ViewModels/SelectHeroViewModel.cs @@ -0,0 +1,56 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using LOL.Assist.Core.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; + +namespace LOL.Assist.App.ViewModels; + +public partial class SelectHeroViewModel : ObservableObject +{ + + [ObservableProperty] private string _searchText = string.Empty; + + [ObservableProperty] private int _showHeroColumns = 6; + + [ObservableProperty] private List _showHeroes = new(); + + [ObservableProperty] private int _showHeroRows; + + [ObservableProperty] private string? _selectPosition = string.Empty; + + + [ObservableProperty] private Visibility _showLoading = Visibility.Visible; + + + /// + /// 位置信息 + /// + [ObservableProperty] + private List _positions = Global.Positions; + + + partial void OnSearchTextChanged(string value) => LoadShowHeroes(); + partial void OnSelectPositionChanged(string value) => LoadShowHeroes(); + public void LoadShowHeroes() + { + ShowLoading = Visibility.Visible; + IEnumerable positionRecommend = SelectPosition switch + { + "top" => Global.Heroes.Where(p => p.PositionRecommend.Top >= 100).OrderByDescending(px => px.PositionRecommend.Top), + "jungle" => Global.Heroes.Where(p => p.PositionRecommend.Jungle >= 100).OrderByDescending(px => px.PositionRecommend.Jungle), + "middle" => Global.Heroes.Where(p => p.PositionRecommend.Mid >= 100).OrderByDescending(px => px.PositionRecommend.Mid), + "bottom" => Global.Heroes.Where(p => p.PositionRecommend.Bottom >= 100).OrderByDescending(px => px.PositionRecommend.Bottom), + "utility" => Global.Heroes.Where(p => p.PositionRecommend.Support >= 100).OrderByDescending(px => px.PositionRecommend.Support), + _ => Global.Heroes, + }; + ShowHeroes = (string.IsNullOrWhiteSpace(SearchText) ? + positionRecommend : + positionRecommend.Where(p => p.Name.Contains(SearchText) || p.KeyWords.Contains(SearchText))) + .ToList(); + var rows = (int)Math.Ceiling(1d * ShowHeroes.Count / ShowHeroColumns); + ShowHeroRows = rows <= 5 ? 5 : rows; + ShowLoading = Visibility.Collapsed; + } +} \ No newline at end of file diff --git a/src/LOL.Assist.App/ViewModels/SettingViewModel.cs b/src/LOL.Assist.App/ViewModels/SettingViewModel.cs new file mode 100644 index 0000000..865b458 --- /dev/null +++ b/src/LOL.Assist.App/ViewModels/SettingViewModel.cs @@ -0,0 +1,436 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.DependencyInjection; +using LOL.Assist.Core; +using LOL.Assist.Core.DbModels; +using LOL.Assist.Core.Enums; +using LOL.Assist.Core.IServices; +using LOL.Assist.Core.Models; +using LOL.Assist.Core.Models.SubscribeMessage; +using Newtonsoft.Json.Linq; +using Refit; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using Newtonsoft.Json; +using System.Diagnostics; + +namespace LOL.Assist.App.ViewModels; + +public partial class SettingViewModel : ObservableObject +{ + /// + /// 自动接受 + /// + [ObservableProperty] + private bool _autoAccept; + /// + /// 自动接受联动组件显隐 + /// + [ObservableProperty] + private Visibility _autoAcceptVisibility = Visibility.Collapsed; + /// + /// 自动接受延迟时间 + /// + [ObservableProperty] + private int _autoAcceptDelayTime; + + /// + /// 自动禁用 + /// + [ObservableProperty] + private bool _autoAcceptBan; + /// + /// 自动禁用联动组件显隐 + /// + [ObservableProperty] + private Visibility _autoAcceptBanHeroVisibility = Visibility.Collapsed; + /// + /// 自动禁用延迟时间 + /// + [ObservableProperty] + private int _autoAcceptBanDelayTime; + /// + /// 自动根据列表随机禁用 + /// + [ObservableProperty] + private bool _autoAcceptBanRandom; + /// + /// 自动禁用英雄列表 + /// + [ObservableProperty] + private ObservableCollection _autoAcceptBanHeroes = new(Enumerable.Range(0, 5).Select(i => new SelectHero + { + ChampionId = -1, + HeadPortrait = _positions[i].Image, + Name = "无", + Priority = i, + })); + + /// + /// 自动配置符文天赋 + /// + [ObservableProperty] + private bool _autoPerk; + + /// + /// 自动选择 + /// + [ObservableProperty] + private bool _autoAcceptSelect; + /// + /// 自动选择联动组件显隐 + /// + [ObservableProperty] + private Visibility _autoAcceptSelectHeroVisibility = Visibility.Hidden; + + /// + /// 自动选择延迟时间 + /// + [ObservableProperty] private int _autoAcceptSelectDelayTime; + /// + /// 自动根据列表随机选用 + /// + [ObservableProperty] + private bool _autoAcceptSelectRandom; + + + /// + /// 非排位模式,自动优先选择对应位置英雄 + /// + [ObservableProperty] + private int _unRankedSelectPosition; + + /// + /// 位置信息 + /// + [ObservableProperty] + private static List _positions = Global.Positions; + + /// + /// 自动选用英雄列表 + /// + [ObservableProperty] + private ObservableCollection _autoAcceptSelectHeroGroups = new(_positions.Select( + p => new SelectHeroGroup + { + HeroPosition = p, + SelectHeroes = new ObservableCollection(Enumerable.Range(0, 3).Select(i => new SelectHero + { + ChampionId = -1, + HeadPortrait = p.Image, + Name = "无", + Priority = i, + Portrait = p.Portrait, + Type = ConfigKeyEnum.AutoSelect, + })) + })); + /// + /// 设置头像或背景id值 + /// + [ObservableProperty] + private string _profileIconId; + + /// + /// 自动禁用或选用英雄状态值 + /// 0、1:禁用中、2:选用中 + /// + private static int _autoHeroState; + + private static readonly object AutoHeroLock = new(); + + private readonly ILcuService _lcuService; + private readonly IDbService _dbService; + private readonly IWindowService _windowService; + public SettingViewModel() + { + _lcuService = Ioc.Default.GetService()!; + _dbService = Ioc.Default.GetService()!; + _windowService = Ioc.Default.GetService()!; + _autoAcceptBanHeroes.CollectionChanged += SaveSelectHeroesChanged!; + foreach (var selectHeroGroup in _autoAcceptSelectHeroGroups) + { + selectHeroGroup.SelectHeroes.CollectionChanged += SaveSelectHeroesChanged!; + } + } + + + private void SessionActionBing(bool value, ConfigKeyEnum configKey) + { + + int delayTime = 0; + Visibility visibility = value ? Visibility.Visible : Visibility.Collapsed; + switch (configKey) + { + case ConfigKeyEnum.AutoAccept: + delayTime = AutoAcceptDelayTime; + AutoAcceptVisibility = visibility; + LolConnect.ManageEventAction(value, SubscribeEventUri.GameReadyCheck, AutoAcceptHandle!); + break; + case ConfigKeyEnum.AutoBan: + delayTime = AutoAcceptBanDelayTime; + AutoAcceptBanHeroVisibility = visibility; + LolConnect.ManageEventAction(value, SubscribeEventUri.GameSessionChamp, AutoAcceptBanHandleAsync!); + break; + case ConfigKeyEnum.AutoSelect: + delayTime = AutoAcceptSelectDelayTime; + AutoAcceptSelectHeroVisibility = visibility; + LolConnect.ManageEventAction(value, SubscribeEventUri.GameSessionChamp, AutoAcceptSelectHandleAsync!); + break; + case ConfigKeyEnum.AutoPerk: + LolConnect.ManageEventAction(value, SubscribeEventUri.SummonersChamp, AutoPerkSetHandleAsync!); + break; + case ConfigKeyEnum.AutoBanRandom: + case ConfigKeyEnum.AutoSelectRandom: + break; + default: + throw new ArgumentOutOfRangeException(nameof(configKey), configKey, null); + } + _dbService.InsertOrUpdate(new Config + { + Key = configKey, + Value = value.ToString(), + DelayTime = delayTime + }); + } + + + #region 自动接受对局 + + partial void OnAutoAcceptChanged(bool value) => SessionActionBing(value, ConfigKeyEnum.AutoAccept); + + partial void OnAutoAcceptDelayTimeChanged(int value) => _dbService.UpdateConfigDelayTime(ConfigKeyEnum.AutoAccept, value); + + private void AutoAcceptHandle(JToken gameFlow, string uri, string type) + { + ReadyCheckMessage? message = gameFlow.ToObject(); + if (message == null) + return; + if (string.Compare(message.PlayerResponse, "None", StringComparison.OrdinalIgnoreCase) != 0) + return; + if (!AutoAccept || message.Timer != AutoAcceptDelayTime) + return; + _ = _lcuService.TeamBuilderReadyCheckAsync().Result; + + //重置选择状态 + _autoHeroState = 0; + } + #endregion + + #region 自动禁用或选择英雄 + /// + /// 自动禁用英雄 + /// + /// + partial void OnAutoAcceptBanChanged(bool value) => SessionActionBing(value, ConfigKeyEnum.AutoBan); + partial void OnAutoAcceptBanDelayTimeChanged(int value) => _dbService.UpdateConfigDelayTime(ConfigKeyEnum.AutoBan, value); + partial void OnAutoAcceptBanRandomChanged(bool value) => SessionActionBing(value, ConfigKeyEnum.AutoBanRandom); + /// + /// 禁用英雄处理 + /// + /// + private void AutoAcceptBanHandleAsync(JToken gameFlow, string uri, string type) => AutoAcceptHeroesHandleAsync(gameFlow, ConfigKeyEnum.AutoBan); + + /// + /// 自动配置天赋符文 + /// + /// + partial void OnAutoPerkChanged(bool value) => SessionActionBing(value, ConfigKeyEnum.AutoPerk); + + /// + /// 自动选择英雄 + /// + /// + partial void OnAutoAcceptSelectChanged(bool value) => SessionActionBing(value, ConfigKeyEnum.AutoSelect); + partial void OnAutoAcceptSelectDelayTimeChanged(int value) => _dbService.UpdateConfigDelayTime(ConfigKeyEnum.AutoSelect, value); + partial void OnAutoAcceptSelectRandomChanged(bool value) => SessionActionBing(value, ConfigKeyEnum.AutoSelectRandom); + /// + /// 非排位模式,位置设置 + /// + /// + partial void OnUnRankedSelectPositionChanged(int value) + { + _dbService.InsertOrUpdate(new Config + { + Key = ConfigKeyEnum.UnRankedSelectPosition, + Value = value.ToString(), + }); + } + /// + /// 选用英雄处理 + /// + /// + private void AutoAcceptSelectHandleAsync(JToken gameFlow, string uri, string type) => AutoAcceptHeroesHandleAsync(gameFlow, ConfigKeyEnum.AutoSelect); + + + /// + /// 保存英雄选择信息 + /// + /// + /// + private void SaveSelectHeroesChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) + { + SelectHero newValue = notifyCollectionChangedEventArgs.NewItems!.OfType().First(); + _dbService.InsertOrUpdate(newValue); + } + + /// + /// 自动禁用或选择英雄回调处理 + /// + /// + /// + private async ValueTask AutoAcceptHeroesHandleAsync(JToken gameFlow, ConfigKeyEnum configKey) + { + //NORMAL 匹配 + //RANKED_SOLO_5x5 单双排 + //RANKED_FLEX_SR 组排 + //ARAM_UNRANKED_5x5 大乱斗5v5 + //URF 无限火力 + //BOT 人机 + //PRACTICETOOL 自定义 + ChampSelectMessage? message = gameFlow.ToObject(); + if (message == null) + return; + await AutoAcceptBanPackHeroAsync(message, configKey); + } + /// + /// 自动禁用或选择英雄 + /// 预选:15s,禁用、选用:30s + /// + /// + /// + /// + private async ValueTask AutoAcceptBanPackHeroAsync(ChampSelectMessage champ, ConfigKeyEnum sessionType) + { + //当前会话类型 + //NORMAL + //Ban:禁用 + //Pick:选择 + if (champ?.Actions == null) + return; + string type = sessionType == ConfigKeyEnum.AutoBan ? "ban" : "pick"; + ActionsItem? action = champ.Actions + .SelectMany(p => p).Where(p => p.ActorCellId == champ.LocalPlayerCellId) + .FirstOrDefault(p => p.Type == type); + if (action is not { IsInProgress: true }) + return; + int sessionTypeInt = (int)sessionType; + IEnumerable selectHeroes = _dbService.GetAll(p => p.Type == sessionType) + .Where(p => p.ChampionId >= 0) + .ToList(); + if (sessionTypeInt == _autoHeroState || !selectHeroes.Any()) + return; + //是否随机禁用列表英雄 + bool isRandom; + //延迟选择时间,秒 + int delayTime; + switch (sessionType) + { + case ConfigKeyEnum.AutoBan: + isRandom = AutoAcceptBanRandom; + delayTime = AutoAcceptBanDelayTime; + break; + case ConfigKeyEnum.AutoSelect: + isRandom = AutoAcceptSelectRandom; + delayTime = AutoAcceptSelectDelayTime; + //获取位置信息,根据位置信息选择英雄 + string position = Positions[UnRankedSelectPosition].Portrait; + if (champ.MyTeam != null) + { + TeamInfo? myInfo = champ.MyTeam.FirstOrDefault(p => p.CellId == champ.LocalPlayerCellId); + if (myInfo != null) + position = myInfo.AssignedPosition; + } + selectHeroes = selectHeroes.Where(p => p.Portrait == position); + break; + default: + return; + } + selectHeroes = isRandom ? selectHeroes.OrderBy(_ => Random.Shared.Next()) : selectHeroes.OrderBy(p => p.Priority); + lock (AutoHeroLock) + { + if (sessionTypeInt == _autoHeroState) + return; + Thread.Sleep(delayTime * 1000); + if (sessionType == ConfigKeyEnum.AutoBan && !AutoAcceptBan || sessionType == ConfigKeyEnum.AutoSelect && !AutoAcceptSelect) + return; + foreach (SelectHero selectSessionHero in selectHeroes) + { + ApiResponse result = _lcuService.ChampSelectSessionActionsAsync(action.Id, new SessionActionsRequest(selectSessionHero.ChampionId, action.Type)).Result; + if (result.StatusCode != HttpStatusCode.OK) + continue; + _autoHeroState = sessionTypeInt; + break; + } + } + await Task.CompletedTask; + } + + #endregion + + #region 自动配置符文信息 + + /// + /// 选用英雄配置符文信息 + /// + /// + private void AutoPerkSetHandleAsync(JToken gameFlow, string uri, string type) + { + SelectSummonersMessage? message = gameFlow.ToObject(); + if (!AutoPerk || message == null || Global.LocalPlayerCellId != message.CellId) + return; + SetPerkPage(message.ChampionId); + Global.LocalPlayerCellId = null; + } + + /// + /// 设置符文页 + /// + /// + private void SetPerkPage(int championId) + { + HeroRune? heroRune = _dbService.Get(p => p.ChampionId == championId); + if (heroRune == null || string.IsNullOrWhiteSpace(heroRune.RuneJson)) + return; + //获取符文信息 + ApiResponse getPerkResponse = _lcuService.GetPerkCurrentPageAsync().Result; + long? perkPageId = getPerkResponse?.Content?.Id; + //新符文页 + Dictionary? selectRune = JsonConvert.DeserializeObject>(heroRune.RuneJson); + if (selectRune == null) + return; + PerkPageRequest request = new PerkPageRequest() + { + Name = heroRune.Name, + SelectedPerkIds = selectRune.SelectMany(p => p.Value).Where(p => p > 0).ToList() + }; + foreach (var item in selectRune.Where(item => item.Key != 2)) + { + switch (item.Value.Length) + { + case 4: + request.PrimaryStyleId = item.Key; + break; + case 3: + request.SubStyleId = item.Key; + break; + } + } + _ = perkPageId == null ? _lcuService.CreatePerkPageAsync(request).Result : _lcuService.UpdatePerkPageAsync(perkPageId.Value, request).Result; + } + + #endregion + + /// + /// 重置客户端窗口 + /// + public void ResetClientWindow() + { + _windowService.SetWindowsSize(1280, 720); + } +} diff --git a/src/LOL.Assist.App/Views/Controls/DelayTime.xaml b/src/LOL.Assist.App/Views/Controls/DelayTime.xaml new file mode 100644 index 0000000..335be93 --- /dev/null +++ b/src/LOL.Assist.App/Views/Controls/DelayTime.xaml @@ -0,0 +1,22 @@ + + + + diff --git a/src/LOL.Assist.App/Views/Controls/DelayTime.xaml.cs b/src/LOL.Assist.App/Views/Controls/DelayTime.xaml.cs new file mode 100644 index 0000000..1760a80 --- /dev/null +++ b/src/LOL.Assist.App/Views/Controls/DelayTime.xaml.cs @@ -0,0 +1,53 @@ +using System.Windows; + +namespace LOL.Assist.App.Views.Controls +{ + /// + /// DelayTime.xaml 的交互逻辑 + /// + public partial class DelayTime + { + public DelayTime() + { + InitializeComponent(); + DelayTimePanel.DataContext = this; + } + public int Value + { + get => (int)GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } + + // Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ValueProperty = + DependencyProperty.Register(nameof(Value), typeof(int), typeof(DelayTime), new PropertyMetadata(0)); + + + + public int MaxValue + { + get => (int)GetValue(MaxValueProperty); + set => SetValue(MaxValueProperty, value); + } + + // Using a DependencyProperty as the backing store for MaxValue. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MaxValueProperty = + DependencyProperty.Register(nameof(MaxValue), typeof(int), typeof(DelayTime), new PropertyMetadata(0)); + + + + public int MinValue + { + get => (int)GetValue(MinValueProperty); + set => SetValue(MinValueProperty, value); + } + + // Using a DependencyProperty as the backing store for MinValue. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MinValueProperty = + DependencyProperty.Register(nameof(MinValue), typeof(int), typeof(DelayTime), new PropertyMetadata(0)); + + + + + } +} diff --git a/src/LOL.Assist.App/Views/Controls/HeroControl.xaml b/src/LOL.Assist.App/Views/Controls/HeroControl.xaml new file mode 100644 index 0000000..4cb796f --- /dev/null +++ b/src/LOL.Assist.App/Views/Controls/HeroControl.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + diff --git a/src/LOL.Assist.App/Views/Controls/HeroControl.xaml.cs b/src/LOL.Assist.App/Views/Controls/HeroControl.xaml.cs new file mode 100644 index 0000000..8c80039 --- /dev/null +++ b/src/LOL.Assist.App/Views/Controls/HeroControl.xaml.cs @@ -0,0 +1,85 @@ +using System.Windows; +using System.Windows.Media; + +namespace LOL.Assist.App.Views.Controls +{ + /// + /// HeroControl.xaml 的交互逻辑 + /// + public partial class HeroControl + { + public HeroControl() + { + InitializeComponent(); + HeroPhotoRoot.DataContext = this; + } + + + public ImageSource Image + { + get => (ImageSource)GetValue(ImageProperty); + set => SetValue(ImageProperty, value); + } + + public static readonly DependencyProperty ImageProperty = DependencyProperty.Register(nameof(Image), + typeof(ImageSource), + typeof(HeroControl), + new PropertyMetadata(null)); + + /// + /// 标题 + /// + public string Title + { + get => (string)GetValue(TitleProperty); + set => SetValue(TitleProperty, value); + } + + public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), + typeof(string), + typeof(HeroControl), + new PropertyMetadata(string.Empty)); + + /// + /// 英雄id + /// + public int ChampionId + { + get => (int)GetValue(ChampionIdProperty); + set => SetValue(ChampionIdProperty, value); + } + + public static readonly DependencyProperty ChampionIdProperty = DependencyProperty.Register(nameof(ChampionId), + typeof(int), + typeof(HeroControl), + new PropertyMetadata(0)); + + /// + /// 优先权限 + /// + public int Priority + { + get => (int)GetValue(PriorityProperty); + set => SetValue(PriorityProperty, value); + } + + public static readonly DependencyProperty PriorityProperty = DependencyProperty.Register(nameof(Priority), + typeof(int), + typeof(HeroControl), + new PropertyMetadata(0)); + + /// + /// 位置信息 + /// + public string Position + { + get => (string)GetValue(PositionProperty); + set => SetValue(PositionProperty, value); + } + + public static readonly DependencyProperty PositionProperty = DependencyProperty.Register(nameof(Position), + typeof(string), + typeof(HeroControl), + new PropertyMetadata(string.Empty)); + } +} diff --git a/src/LOL.Assist.App/Views/MainWindow.xaml b/src/LOL.Assist.App/Views/MainWindow.xaml new file mode 100644 index 0000000..565eaf1 --- /dev/null +++ b/src/LOL.Assist.App/Views/MainWindow.xaml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/LOL.Assist.App/Views/MainWindow.xaml.cs b/src/LOL.Assist.App/Views/MainWindow.xaml.cs new file mode 100644 index 0000000..b1830d1 --- /dev/null +++ b/src/LOL.Assist.App/Views/MainWindow.xaml.cs @@ -0,0 +1,169 @@ +using System; +using CommunityToolkit.Mvvm.DependencyInjection; +using LOL.Assist.App.ViewModels; +using LOL.Assist.Core.DbModels; +using LOL.Assist.Core.Enums; +using LOL.Assist.Core.IServices; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; +using LOL.Assist.Core.Models; +using Wpf.Ui.Appearance; +using Wpf.Ui.Controls; +using Wpf.Ui.Mvvm.Contracts; +using LOL.Assist.Core; +using Newtonsoft.Json.Linq; + +namespace LOL.Assist.App.Views +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : UiWindow + { + private readonly IThemeService _themeService; + private bool _initialized; + + public MainWindow() + { + InitializeComponent(); + + _themeService = Ioc.Default.GetService()!; + SetThemeLabel(); + Loaded += (_, _) => Initialize(); + LolConnect.ConnectNotificationAction += SetWindowLocation; + } + + private void Initialize() + { + if (_initialized) + return; + + _initialized = true; + Loading.Visibility = Visibility.Visible; + FramePage.Visibility = Visibility.Hidden; + Task.Run(async () => + { + IDbService dbService = Ioc.Default.GetService()!; + IGameService gameService = Ioc.Default.GetService()!; + ILOLService lolService = Ioc.Default.GetService()!; + //加载默认监听事件 + LolConnect.ManageEventAction(true, SubscribeEventUri.GameSessionChamp, Global.GameSessionHandle!); + //加载全局数据 + Global.Heroes = (await gameService.GetHeroListAsync()).Hero; + string heroPosition = await lolService.GetHeroPositionAsync(); + if (!string.IsNullOrWhiteSpace(heroPosition)) + { + string heroPositionJson = heroPosition.Split('=', ';')[1]; + Dictionary? heroPositionRecommend = JObject.Parse(heroPositionJson)["list"]?.ToObject>(); + if (heroPositionRecommend != null) + Global.Heroes.ForEach(p => p.PositionRecommend = heroPositionRecommend.GetValueOrDefault(p.HeroId) ?? new HeroPositionRecommend()); + } + Global.Runes = (await gameService.GetRuneListAsync()).Rune.Select(p => + { + p.Value.Id = p.Key; + return p.Value; + }).ToList(); + + //加载配置内容 + SettingViewModel settingsViewModel = Ioc.Default.GetService()!; + IList configs = dbService.GetAll(); + foreach (var config in configs) + { + switch (config.Key) + { + case ConfigKeyEnum.AutoAccept: + settingsViewModel.AutoAccept = bool.Parse(config.Value); + settingsViewModel.AutoAcceptDelayTime = config.DelayTime; + break; + case ConfigKeyEnum.AutoBan: + settingsViewModel.AutoAcceptBan = bool.Parse(config.Value); + settingsViewModel.AutoAcceptBanDelayTime = config.DelayTime; + IList selectSessionBanHeroes = dbService.GetAll(p => p.Type == ConfigKeyEnum.AutoBan); + foreach (SelectHero selectHero in selectSessionBanHeroes.OrderBy(p => p.Priority)) + { + await Dispatcher.InvokeAsync(() => + { + settingsViewModel.AutoAcceptBanHeroes[selectHero.Priority] = selectHero; + }); + } + break; + case ConfigKeyEnum.AutoSelect: + settingsViewModel.AutoAcceptSelect = bool.Parse(config.Value); + settingsViewModel.AutoAcceptSelectDelayTime = config.DelayTime; + IList selectSessionSelectHeroes = dbService.GetAll(p => p.Type == ConfigKeyEnum.AutoSelect); + foreach (var selectHeroGroup in selectSessionSelectHeroes.GroupBy(p => p.Portrait)) + { + SelectHeroGroup? heroGroup = settingsViewModel.AutoAcceptSelectHeroGroups + .FirstOrDefault(p => p.HeroPosition.Portrait == selectHeroGroup.Key); + if (heroGroup == null) + continue; + await Dispatcher.InvokeAsync(() => + { + selectHeroGroup.ToList().ForEach(selectHero => + heroGroup.SelectHeroes[selectHero.Priority] = selectHero); + }); + + } + break; + case ConfigKeyEnum.AutoBanRandom: + settingsViewModel.AutoAcceptBanRandom = bool.Parse(config.Value); + break; + case ConfigKeyEnum.AutoSelectRandom: + settingsViewModel.AutoAcceptSelectRandom = bool.Parse(config.Value); + break; + case ConfigKeyEnum.UnRankedSelectPosition: + settingsViewModel.UnRankedSelectPosition = int.Parse(config.Value); + break; + case ConfigKeyEnum.AutoPerk: + settingsViewModel.AutoPerk = bool.Parse(config.Value); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + await Dispatcher.InvokeAsync(() => + { + Loading.Visibility = Visibility.Hidden; + FramePage.Visibility = Visibility.Visible; + }); + + return true; + }); + } + + /// + /// 设置窗口位置 + /// + private void SetWindowLocation() + { + IWindowService windowService = Ioc.Default.GetService()!; + var rect = windowService.GetWindowsRectLocation(); + if (rect == null) + return; + Dispatcher.Invoke(() => + { + Left = rect.Value.Left - Width; + Top = rect.Value.Top; + }); + } + private void NotifyBarExit_Click(object sender, RoutedEventArgs e) + { + Application.Current.Shutdown(); + NotifyMenuBar = null; + } + + private void ButtonTheme_OnClick(object sender, RoutedEventArgs e) + { + _themeService.SetTheme(_themeService.GetTheme() == ThemeType.Dark ? ThemeType.Light : ThemeType.Dark); + SetThemeLabel(); + } + + private void SetThemeLabel(ThemeType? themeType = null) + { + themeType ??= _themeService.GetTheme(); + // ThemeLabel.Content = themeType == ThemeType.Dark ? "黑暗" : "明亮"; + } + } +} diff --git a/src/LOL.Assist.App/Views/Pages/RunePage.xaml b/src/LOL.Assist.App/Views/Pages/RunePage.xaml new file mode 100644 index 0000000..c2cfcd4 --- /dev/null +++ b/src/LOL.Assist.App/Views/Pages/RunePage.xaml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + +