初始化提交

This commit is contained in:
vectorluo 2023-09-22 11:00:20 +08:00
parent 568544a38a
commit b0d09e5f35
83 changed files with 80862 additions and 0 deletions

362
.gitignore vendored Normal file
View File

@ -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

70
README.md Normal file
View File

@ -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/
swaggerhttps://www.mingweisamuel.com/lcu-schema/tool/#/
### 1、websocket
https://hextechdocs.dev/getting-started-with-the-lcu-websocket/
### 2、项目参考
PoniLCUhttps://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

44
lol-assist.sln Normal file
View File

@ -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

View File

@ -0,0 +1,16 @@
<Application
x:Class="LOL.Assist.App.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LOL.Assist.App"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
StartupUri="/Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ui:ControlsDictionary />
<ui:ThemesDictionary Theme="Dark" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@ -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
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App
{
private static LolConnect _lolConnect=new LolConnect();
protected override void OnStartup(StartupEventArgs e)
{
IServiceCollection services = new ServiceCollection()
.AddSingleton<IThemeService, ThemeService>();
services.AddSingleton(RestService.For<ILcuService>(_lolConnect.GetHttpClient()));
RefitSettings settings = new RefitSettings(new NewtonsoftJsonContentSerializer());
services.AddSingleton(RestService.For<ILOLService>("https://lol.qq.com", settings));
services.AddSingleton(RestService.For<IGameService>("https://game.gtimg.cn", settings));
services.AddSingleton(new FreeSqlBuilder()
.UseConnectionString(DataType.Sqlite, $"Data Source={AppContext.BaseDirectory}config.db")
.UseAutoSyncStructure(true).Build());
services.AddScoped<IDbService, DbService>();
services.AddScoped<IWindowService, WindowService>();
services.AddScoped<SettingViewModel>();
services.AddScoped<SelectHeroViewModel>();
services.AddScoped<RuneViewModel>();
services.AddScoped<RunePopupViewModel>();
Ioc.Default.ConfigureServices(services.BuildServiceProvider());
base.OnStartup(e);
}
protected override void OnLoadCompleted(NavigationEventArgs e)
{
_lolConnect=_lolConnect.Connect();
}
}
}

View File

@ -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)
)]

View File

@ -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;
/// <summary>
/// 全局配置信息
/// </summary>
public static class Global
{
/// <summary>
/// 角色列表
/// </summary>
public static List<Hero> Heroes { get; set; } = new List<Hero>();
/// <summary>
/// 位置信息
/// </summary>
public static List<Position> Positions = new List<Position>
{
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"),
};
/// <summary>
/// 符文列表
/// </summary>
public static List<RuneResponse> Runes { get; set; } = new List<RuneResponse>();
/// <summary>
/// 当前游戏开始cell id
/// </summary>
public static int? LocalPlayerCellId { get; set; }
/// <summary>
/// 存储会话信息
/// </summary>
/// <param name="gameFlow"></param>
public static void GameSessionHandle(JToken gameFlow, string uri, string type)
{
LocalPlayerCellId = gameFlow.ToObject<ChampSelectMessage>()?.LocalPlayerCellId;
}
}

View File

@ -0,0 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<None Remove="Resources\Position_Challenger-Bot.png" />
<None Remove="Resources\Position_Challenger-Jungle.png" />
<None Remove="Resources\Position_Challenger-Mid.png" />
<None Remove="Resources\Position_Challenger-Support.png" />
<None Remove="Resources\Position_Challenger-Top.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Position_Challenger-Mid.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LOL.Assist.Core\LOL.Assist.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="WPF-UI" Version="2.0.3" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Position_Challenger-Bot.png" />
<Resource Include="Resources\Position_Challenger-Jungle.png" />
<Resource Include="Resources\Position_Challenger-Support.png" />
<Resource Include="Resources\Position_Challenger-Top.png" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>

View File

@ -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;}
/// <summary>
/// 主要系别符文信息
/// </summary>
public List<RuneResponse> Primary { get; set; }
/// <summary>
/// 副系及成长符文信息
/// </summary>
public List<RuneResponse> SecondGrowing { get; set; }
}

View File

@ -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;
/// <summary>
/// 符文分组信息
/// </summary>
public partial class RuneGroup:ObservableObject
{
/// <summary>
/// 主系选中状态
/// </summary>
[ObservableProperty]
private double _primaryOpacity = 0.4;
/// <summary>
/// 副系显隐藏
/// Visible
/// Hidden
/// Collapsed
/// </summary>
[ObservableProperty]
private Visibility _secondVisibility;
/// <summary>
/// 副系选中状态
/// </summary>
[ObservableProperty]
private double _secondOpacity = 0.4;
/// <summary>
/// 系别
/// </summary>
[ObservableProperty] private RuneResponse _root;
/// <summary>
/// 基石信息
/// </summary>
public List<RuneResponse> Base { get; set; }
/// <summary>
/// 一级符文信息
/// </summary>
public List<RuneResponse> Valiant { get; set; }
/// <summary>
/// 二级符文信息
/// </summary>
public List<RuneResponse> Legend { get; set; }
/// <summary>
/// 三级符文信息
/// </summary>
public List<RuneResponse> Combat { get; set; }
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,113 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace LOL.Assist.App.Resources {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 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() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[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;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap Position_Challenger_Bot {
get {
object obj = ResourceManager.GetObject("Position_Challenger-Bot", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap Position_Challenger_Jungle {
get {
object obj = ResourceManager.GetObject("Position_Challenger-Jungle", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap Position_Challenger_Mid {
get {
object obj = ResourceManager.GetObject("Position_Challenger-Mid", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap Position_Challenger_Support {
get {
object obj = ResourceManager.GetObject("Position_Challenger-Support", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap Position_Challenger_Top {
get {
object obj = ResourceManager.GetObject("Position_Challenger-Top", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

View File

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Position_Challenger-Bot" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Position_Challenger-Bot.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Position_Challenger-Jungle" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Position_Challenger-Jungle.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Position_Challenger-Mid" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Position_Challenger-Mid.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Position_Challenger-Support" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Position_Challenger-Support.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Position_Challenger-Top" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Position_Challenger-Top.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@ -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<string> ValiantStyleNames = new List<string> { "英武", "预谋", "宝物", "巧具", "蛮力" };
private static readonly IEnumerable<string> LegendStyleNames = new List<string> { "传说", "追踪", "卓越", "未来", "抵抗" };
private static readonly IEnumerable<string> CombatStyleNames = new List<string> { "战斗", "狩猎", "威能", "超越", "生机" };
/// <summary>
/// 符文
/// </summary>
[ObservableProperty]
private ObservableCollection<RuneGroup> _showRunes;
/// <summary>
/// 成长符文
/// </summary>
[ObservableProperty]
private RuneGroup _showGrowingRunes;
[ObservableProperty]
private HeroRune _selectHeroRune;
private readonly IDbService _dbService;
/// <summary>
///
/// </summary>
public RunePopupViewModel()
{
_dbService = Ioc.Default.GetService<IDbService>()!;
RefreshReloadRune();
}
public void RefreshReloadRune()
{
IEnumerable<RuneGroup> runeGroup = Global.Runes
.Where(p => !string.IsNullOrWhiteSpace(p.Key) && string.IsNullOrWhiteSpace(p.SlotLabel) &&
string.IsNullOrWhiteSpace(p.StyleName)).Select(runeResponse =>
{
List<IGrouping<string, RuneResponse>> 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>(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<RuneResponse> 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();
}
/// <summary>
/// 获取英雄选中服务信息
/// </summary>
/// <returns></returns>
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<int, int[]> selectRune = new Dictionary<int, int[]>()
{
{
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);
}
/// <summary>
/// 设置英雄选中服务信息
/// </summary>
/// <returns></returns>
public void SetSelectHeroRune(HeroRune? heroRune, TabControl primaryTab, TabControl secondTab)
{
SelectHeroRune = heroRune;
if (heroRune == null || string.IsNullOrWhiteSpace(heroRune.RuneJson))
return;
Dictionary<int, int[]>? selectRune = JsonConvert.DeserializeObject<Dictionary<int, int[]>>(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<RuneResponse>? runes)
{
if (runes == null)
return 0;
return runes.FirstOrDefault(p => p.IsChecked)?.Id ?? 0;
}
private void SetRuneCheckId(IEnumerable<RuneResponse>? runes, int id)
{
if (runes == null || id == 0)
return;
RuneResponse? setRune = runes.FirstOrDefault(p => p.Id == id);
if (setRune == null)
return;
setRune.IsChecked = true;
}
}
}

View File

@ -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<HeroRuneGroup>? _heroRunes;
private readonly IDbService _dbService;
public RuneViewModel()
{
_dbService = Ioc.Default.GetService<IDbService>()!;
RefreshHeroRune();
}
partial void OnSearchTextChanged(string value)
{
RefreshHeroRune();
}
public void RemoveHeroRune(int championId)
{
_dbService.Delete<HeroRune>(p => p.ChampionId == championId);
HeroRuneGroup? removeHero = HeroRunes?.FirstOrDefault(p => p.HeroRuneBase.ChampionId == championId);
if (removeHero == null)
return;
HeroRunes!.Remove(removeHero);
}
public void RefreshHeroRune()
{
IList<HeroRune> data = _dbService.GetAll<HeroRune>(string.IsNullOrWhiteSpace(SearchText) ? p => true : p => p.KeyWords.Contains(SearchText) || p.Name.Contains(SearchText));
HeroRunes = new ObservableCollection<HeroRuneGroup>(data.Select(p =>
{
HeroRuneGroup heroRuneGroup = new HeroRuneGroup
{
HeroRuneBase = p,
Primary = new List<RuneResponse>(),
SecondGrowing = new List<RuneResponse>(),
};
if (string.IsNullOrWhiteSpace(p.RuneJson))
return heroRuneGroup;
Dictionary<int, int[]>? selectRune = JsonConvert.DeserializeObject<Dictionary<int, int[]>>(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;
}));
}
}
}

View File

@ -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<Hero> _showHeroes = new();
[ObservableProperty] private int _showHeroRows;
[ObservableProperty] private string? _selectPosition = string.Empty;
[ObservableProperty] private Visibility _showLoading = Visibility.Visible;
/// <summary>
/// 位置信息
/// </summary>
[ObservableProperty]
private List<Position> _positions = Global.Positions;
partial void OnSearchTextChanged(string value) => LoadShowHeroes();
partial void OnSelectPositionChanged(string value) => LoadShowHeroes();
public void LoadShowHeroes()
{
ShowLoading = Visibility.Visible;
IEnumerable<Hero> 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;
}
}

View File

@ -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
{
/// <summary>
/// 自动接受
/// </summary>
[ObservableProperty]
private bool _autoAccept;
/// <summary>
/// 自动接受联动组件显隐
/// </summary>
[ObservableProperty]
private Visibility _autoAcceptVisibility = Visibility.Collapsed;
/// <summary>
/// 自动接受延迟时间
/// </summary>
[ObservableProperty]
private int _autoAcceptDelayTime;
/// <summary>
/// 自动禁用
/// </summary>
[ObservableProperty]
private bool _autoAcceptBan;
/// <summary>
/// 自动禁用联动组件显隐
/// </summary>
[ObservableProperty]
private Visibility _autoAcceptBanHeroVisibility = Visibility.Collapsed;
/// <summary>
/// 自动禁用延迟时间
/// </summary>
[ObservableProperty]
private int _autoAcceptBanDelayTime;
/// <summary>
/// 自动根据列表随机禁用
/// </summary>
[ObservableProperty]
private bool _autoAcceptBanRandom;
/// <summary>
/// 自动禁用英雄列表
/// </summary>
[ObservableProperty]
private ObservableCollection<SelectHero> _autoAcceptBanHeroes = new(Enumerable.Range(0, 5).Select(i => new SelectHero
{
ChampionId = -1,
HeadPortrait = _positions[i].Image,
Name = "无",
Priority = i,
}));
/// <summary>
/// 自动配置符文天赋
/// </summary>
[ObservableProperty]
private bool _autoPerk;
/// <summary>
/// 自动选择
/// </summary>
[ObservableProperty]
private bool _autoAcceptSelect;
/// <summary>
/// 自动选择联动组件显隐
/// </summary>
[ObservableProperty]
private Visibility _autoAcceptSelectHeroVisibility = Visibility.Hidden;
/// <summary>
/// 自动选择延迟时间
/// </summary>
[ObservableProperty] private int _autoAcceptSelectDelayTime;
/// <summary>
/// 自动根据列表随机选用
/// </summary>
[ObservableProperty]
private bool _autoAcceptSelectRandom;
/// <summary>
/// 非排位模式,自动优先选择对应位置英雄
/// </summary>
[ObservableProperty]
private int _unRankedSelectPosition;
/// <summary>
/// 位置信息
/// </summary>
[ObservableProperty]
private static List<Position> _positions = Global.Positions;
/// <summary>
/// 自动选用英雄列表
/// </summary>
[ObservableProperty]
private ObservableCollection<SelectHeroGroup> _autoAcceptSelectHeroGroups = new(_positions.Select(
p => new SelectHeroGroup
{
HeroPosition = p,
SelectHeroes = new ObservableCollection<SelectHero>(Enumerable.Range(0, 3).Select(i => new SelectHero
{
ChampionId = -1,
HeadPortrait = p.Image,
Name = "无",
Priority = i,
Portrait = p.Portrait,
Type = ConfigKeyEnum.AutoSelect,
}))
}));
/// <summary>
/// 设置头像或背景id值
/// </summary>
[ObservableProperty]
private string _profileIconId;
/// <summary>
/// 自动禁用或选用英雄状态值
/// 0、1禁用中、2选用中
/// </summary>
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<ILcuService>()!;
_dbService = Ioc.Default.GetService<IDbService>()!;
_windowService = Ioc.Default.GetService<IWindowService>()!;
_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<ReadyCheckMessage>();
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
/// <summary>
/// 自动禁用英雄
/// </summary>
/// <param name="value"></param>
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);
/// <summary>
/// 禁用英雄处理
/// </summary>
/// <param name="gameFlow"></param>
private void AutoAcceptBanHandleAsync(JToken gameFlow, string uri, string type) => AutoAcceptHeroesHandleAsync(gameFlow, ConfigKeyEnum.AutoBan);
/// <summary>
/// 自动配置天赋符文
/// </summary>
/// <param name="value"></param>
partial void OnAutoPerkChanged(bool value) => SessionActionBing(value, ConfigKeyEnum.AutoPerk);
/// <summary>
/// 自动选择英雄
/// </summary>
/// <param name="value"></param>
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);
/// <summary>
/// 非排位模式,位置设置
/// </summary>
/// <param name="value"></param>
partial void OnUnRankedSelectPositionChanged(int value)
{
_dbService.InsertOrUpdate(new Config
{
Key = ConfigKeyEnum.UnRankedSelectPosition,
Value = value.ToString(),
});
}
/// <summary>
/// 选用英雄处理
/// </summary>
/// <param name="gameFlow"></param>
private void AutoAcceptSelectHandleAsync(JToken gameFlow, string uri, string type) => AutoAcceptHeroesHandleAsync(gameFlow, ConfigKeyEnum.AutoSelect);
/// <summary>
/// 保存英雄选择信息
/// </summary>
/// <param name="sender"></param>
/// <param name="notifyCollectionChangedEventArgs"></param>
private void SaveSelectHeroesChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
SelectHero newValue = notifyCollectionChangedEventArgs.NewItems!.OfType<SelectHero>().First();
_dbService.InsertOrUpdate(newValue);
}
/// <summary>
/// 自动禁用或选择英雄回调处理
/// </summary>
/// <param name="gameFlow"></param>
/// <param name="configKey"></param>
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<ChampSelectMessage>();
if (message == null)
return;
await AutoAcceptBanPackHeroAsync(message, configKey);
}
/// <summary>
/// 自动禁用或选择英雄
/// 预选15s,禁用、选用30s
/// </summary>
/// <param name="champ"></param>
/// <param name="sessionType"></param>
/// <returns></returns>
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<SelectHero> selectHeroes = _dbService.GetAll<SelectHero>(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<string> 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
/// <summary>
/// 选用英雄配置符文信息
/// </summary>
/// <param name="gameFlow"></param>
private void AutoPerkSetHandleAsync(JToken gameFlow, string uri, string type)
{
SelectSummonersMessage? message = gameFlow.ToObject<SelectSummonersMessage>();
if (!AutoPerk || message == null || Global.LocalPlayerCellId != message.CellId)
return;
SetPerkPage(message.ChampionId);
Global.LocalPlayerCellId = null;
}
/// <summary>
/// 设置符文页
/// </summary>
/// <param name="championId"></param>
private void SetPerkPage(int championId)
{
HeroRune? heroRune = _dbService.Get<HeroRune>(p => p.ChampionId == championId);
if (heroRune == null || string.IsNullOrWhiteSpace(heroRune.RuneJson))
return;
//获取符文信息
ApiResponse<PerkCurrentPageResponse> getPerkResponse = _lcuService.GetPerkCurrentPageAsync().Result;
long? perkPageId = getPerkResponse?.Content?.Id;
//新符文页
Dictionary<int, int[]>? selectRune = JsonConvert.DeserializeObject<Dictionary<int, int[]>>(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
/// <summary>
/// 重置客户端窗口
/// </summary>
public void ResetClientWindow()
{
_windowService.SetWindowsSize(1280, 720);
}
}

View File

@ -0,0 +1,22 @@
<UserControl x:Class="LOL.Assist.App.Views.Controls.DelayTime"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<StackPanel Name="DelayTimePanel"
Height="21"
Orientation="Horizontal">
<Label Width="16"
VerticalContentAlignment="Bottom"
HorizontalContentAlignment="Right"
Content="{Binding Value}" />
<Label Content="秒"
VerticalContentAlignment="Bottom"
Margin="4,0"/>
<Slider Value="{Binding Value}"
Maximum="{Binding MaxValue}"
Minimum="{Binding MinValue}"
Width="95"/>
</StackPanel>
</UserControl>

View File

@ -0,0 +1,53 @@
using System.Windows;
namespace LOL.Assist.App.Views.Controls
{
/// <summary>
/// DelayTime.xaml 的交互逻辑
/// </summary>
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));
}
}

View File

@ -0,0 +1,27 @@
<UserControl x:Class="LOL.Assist.App.Views.Controls.HeroControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid x:Name="HeroPhotoRoot" Cursor="Hand">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Image Source="{Binding Image}"
Height="40"
Width="40">
<Image.Clip>
<RectangleGeometry
RadiusX="20"
RadiusY="20"
Rect="0,0,40,40"/>
</Image.Clip>
</Image>
<Label Grid.Row="1"
FontSize="13"
Content="{Binding Title}"
HorizontalAlignment="Center"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,85 @@
using System.Windows;
using System.Windows.Media;
namespace LOL.Assist.App.Views.Controls
{
/// <summary>
/// HeroControl.xaml 的交互逻辑
/// </summary>
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));
/// <summary>
/// 标题
/// </summary>
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));
/// <summary>
/// 英雄id
/// </summary>
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));
/// <summary>
/// 优先权限
/// </summary>
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));
/// <summary>
/// 位置信息
/// </summary>
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));
}
}

View File

@ -0,0 +1,79 @@
<ui:UiWindow
x:Class="LOL.Assist.App.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow"
MinWidth="320"
Width="320"
Height="550"
Background="{ui:ThemeResource ApplicationBackgroundBrush}"
WindowStyle="None"
ExtendsContentIntoTitleBar="True"
WindowBackdropType="Mica"
WindowCornerPreference="RoundSmall"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize" MaxWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
<RowDefinition Height="15" />
</Grid.RowDefinitions>
<GridSplitter />
<Grid Grid.Row="1" Width="290">
<ui:ProgressRing Name="Loading" IsIndeterminate="True" />
<Frame x:Name="FramePage"
Visibility="Hidden"
Source="pages/SettingPage.xaml" />
</Grid>
<Border Grid.Row="2"
BorderThickness="1">
<!--<Grid>
<Label Name="ThemeLabel"
Height="26"
Width="60"
FontSize="16"
Content="黑暗"
HorizontalAlignment="Right"
Margin="0,4,60,0" />
<ui:ToggleSwitch
HorizontalAlignment="Right"
Margin="0,0,30,0"
Click="ButtonTheme_OnClick" />
</Grid>-->
</Border>
<ui:TitleBar
Grid.Row="0"
Name="NotifyMenuBar"
Title="WPF UI - 测试信息"
ForceShutdown="False"
MinimizeToTray="False"
ShowHelp="False"
Padding="10,0,0,0"
ShowClose="True"
ShowMaximize="False"
ShowMinimize="True"
Height="30">
<ui:TitleBar.Tray>
<ui:NotifyIcon
FocusOnLeftClick="True"
MenuOnRightClick="True"
TooltipText="WPF UI">
<ui:NotifyIcon.Menu>
<ContextMenu>
<ui:MenuItem
Header="退出"
Click="NotifyBarExit_Click"
SymbolIcon="ArrowExit20"
Tag="exit" />
</ContextMenu>
</ui:NotifyIcon.Menu>
</ui:NotifyIcon>
</ui:TitleBar.Tray>
</ui:TitleBar>
</Grid>
</ui:UiWindow>

View File

@ -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
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : UiWindow
{
private readonly IThemeService _themeService;
private bool _initialized;
public MainWindow()
{
InitializeComponent();
_themeService = Ioc.Default.GetService<IThemeService>()!;
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<IDbService>()!;
IGameService gameService = Ioc.Default.GetService<IGameService>()!;
ILOLService lolService = Ioc.Default.GetService<ILOLService>()!;
//加载默认监听事件
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<int, HeroPositionRecommend>? heroPositionRecommend = JObject.Parse(heroPositionJson)["list"]?.ToObject<Dictionary<int, HeroPositionRecommend>>();
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<SettingViewModel>()!;
IList<Config> configs = dbService.GetAll<Config>();
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<SelectHero> selectSessionBanHeroes = dbService.GetAll<SelectHero>(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<SelectHero> selectSessionSelectHeroes = dbService.GetAll<SelectHero>(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;
});
}
/// <summary>
/// 设置窗口位置
/// </summary>
private void SetWindowLocation()
{
IWindowService windowService = Ioc.Default.GetService<IWindowService>()!;
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 ? "黑暗" : "明亮";
}
}
}

View File

@ -0,0 +1,167 @@
<ui:UiPage x:Class="LOL.Assist.App.Views.Pages.RunePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:model="clr-namespace:LOL.Assist.App.Model"
xmlns:models="clr-namespace:LOL.Assist.Core.Models;assembly=LOL.Assist.Core"
mc:Ignorable="d"
Width="310"
Title="RuneGenius">
<ui:UiPage.Resources>
<Style TargetType="UniformGrid">
<Setter Property="Focusable" Value="False" />
</Style>
<Style TargetType="ItemsControl">
<Setter Property="Focusable" Value="False" />
</Style>
<Style TargetType="ScrollViewer">
<Setter Property="Focusable" Value="False" />
</Style>
<Style TargetType="Border" x:Key="BorderSegStyle">
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=(ItemsControl.AlternationIndex),RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
Value="0">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</ui:UiPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Column="0"
Grid.Row="0"
Content="◀ 返回"
FontSize="16" VerticalContentAlignment="Center"
Cursor="Hand"
MouseDown="GetBack_MouseDown" />
<ui:AutoSuggestBox
Grid.Row="0" Width="160"
Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="66,0,84,0" />
<Button Grid.Row="0" Content="添加" Margin="238,0,0,0" Click="AddButton_Click" />
<ItemsControl Grid.Column="0" Grid.Row="1"
ItemsSource="{Binding HeroRunes}"
AlternationCount="{Binding HeroRunes.Count}">
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"
CanContentScroll="True">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Focusable="False" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type model:HeroRuneGroup}">
<Grid Height="80" Tag="{Binding HeroRuneBase.ChampionId}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="2" />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Border Grid.ColumnSpan="2" Height="1" Background="Black"
Style="{StaticResource BorderSegStyle}" />
<Grid Grid.Column="0"
Grid.Row="1"
Grid.RowSpan="2"
Margin="0,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="1"/>
<RowDefinition />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Image Grid.Row="1"
Source="{Binding HeroRuneBase.HeadPortrait}"
Height="50"
Width="50">
<Image.Clip>
<RectangleGeometry
RadiusX="25"
RadiusY="25"
Rect="0,0,50,50" />
</Image.Clip>
</Image>
<Label Grid.Row="2"
FontSize="13"
Content="{Binding HeroRuneBase.Name}"
HorizontalAlignment="Center" />
<Label Grid.Row="1"
Width="50"
Height="25"
Margin="0,-20,0,0"
Padding="0"
Cursor="Hand"
HorizontalContentAlignment="Center"
Tag="{Binding HeroRuneBase.ChampionId}"
MouseDown="Update_MouseDown"
Content="修改" />
<Label Grid.Row="1"
Width="50"
Height="25"
Margin="0,30,0,0"
Padding="0"
Cursor="Hand"
HorizontalContentAlignment="Center"
Tag="{Binding HeroRuneBase.ChampionId}"
MouseDown="RemoveButton_Click"
Content="移除" />
</Grid>
<StackPanel Grid.Row="1"
Grid.Column="1"
Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding Primary}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Primary.Count}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:RuneResponse}">
<Image Source="{Binding Icon}"
Height="32"
Margin="10,0,0,0" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<StackPanel Grid.Row="2"
Grid.Column="1"
Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding SecondGrowing}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding SecondGrowing.Count}" Margin="2,0,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:RuneResponse}">
<Image Source="{Binding Icon}"
Height="26"
Margin="10,0,0,0" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</ui:UiPage>

View File

@ -0,0 +1,82 @@
using LOL.Assist.App.ViewModels;
using System;
using System.Windows;
using CommunityToolkit.Mvvm.DependencyInjection;
using System.Windows.Controls;
using System.Linq;
namespace LOL.Assist.App.Views.Pages;
/// <summary>
/// RuneGenius.xaml 的交互逻辑
/// </summary>
public partial class RunePage
{
private readonly RuneViewModel _runeViewModel;
public RunePage()
{
InitializeComponent();
_runeViewModel = Ioc.Default.GetService<RuneViewModel>()!;
DataContext = _runeViewModel;
}
private void GetBack_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (Application.Current.MainWindow is not MainWindow maintain)
return;
maintain.FramePage.Source = new Uri("pages/SettingPage.xaml", UriKind.Relative);
}
private void Image_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
}
/// <summary>
/// 添加
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AddButton_Click(object sender, RoutedEventArgs e)
{
RunePopupWindow runePopupWindow = new RunePopupWindow()
{
Owner = Window.GetWindow(this)
};
runePopupWindow.ShowDialog();
_runeViewModel.RefreshHeroRune();
}
/// <summary>
/// 移除
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RemoveButton_Click(object sender, RoutedEventArgs e)
{
Label button = (Label)sender;
int championId = int.Parse(button.Tag.ToString() ?? "0");
if (championId <= 0)
return;
_runeViewModel.RemoveHeroRune(championId);
}
/// <summary>
/// 修改
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Update_MouseDown(object sender,RoutedEventArgs e)
{
Label grid = (Label)sender;
int championId = int.Parse(grid.Tag?.ToString() ?? "0");
if (championId <= 0)
return;
RunePopupWindow runePopupWindow = new RunePopupWindow()
{
Owner = Window.GetWindow(this)
};
runePopupWindow.SetSelectHeroRune(_runeViewModel.HeroRunes?.FirstOrDefault(p => p.HeroRuneBase.ChampionId == championId)?.HeroRuneBase);
runePopupWindow.ShowDialog();
_runeViewModel.RefreshHeroRune();
}
}

View File

@ -0,0 +1,224 @@
<ui:UiPage x:Class="LOL.Assist.App.Views.Pages.SettingPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dbModels="clr-namespace:LOL.Assist.Core.DbModels;assembly=LOL.Assist.Core"
xmlns:controls="clr-namespace:LOL.Assist.App.Views.Controls"
xmlns:models="clr-namespace:LOL.Assist.Core.Models;assembly=LOL.Assist.Core"
mc:Ignorable="d">
<ui:UiPage.Resources>
<Style x:Key="LabelStyle" TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="20"/>
<Setter Property="FontSize" Value="15"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="0,1,0,0"/>
</Style>
<Style TargetType="StackPanel" >
<Setter Property="Orientation" Value="Horizontal"/>
</Style>
<Style x:Key="SelectImageStyle" TargetType="Image">
<Setter Property="Height" Value="20"/>
<Setter Property="Width" Value="20"/>
<Setter Property="Margin" Value="0,-3,6,0"/>
</Style>
</ui:UiPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="Auto" MaxHeight="120"/>
<RowDefinition Height="40"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel>
<Label Content="自动接受对局:" Style="{StaticResource LabelStyle}"/>
<ui:ToggleSwitch IsChecked ="{Binding AutoAccept, Mode=TwoWay}"/>
</StackPanel>
<controls:DelayTime Grid.Column="1"
Grid.Row="0"
Margin="10,0,0,0"
Value="{Binding AutoAcceptDelayTime, Mode=TwoWay}"
MaxValue="10"
MinValue="0"
Visibility="{Binding AutoAcceptVisibility}"/>
</Grid>
<Grid Grid.Row="1" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Height="50"
VerticalAlignment="Top">
<Label Content="自动禁用英雄:" Style="{StaticResource LabelStyle}"/>
<ui:ToggleSwitch IsChecked="{Binding AutoAcceptBan}"/>
</StackPanel>
<controls:DelayTime Grid.Column="1"
Grid.Row="0"
Margin="10,0,0,0"
Value="{Binding AutoAcceptBanDelayTime, Mode=TwoWay}"
MaxValue="30"
MinValue="0"
Visibility="{Binding AutoAcceptBanHeroVisibility}"/>
<CheckBox Grid.Row="1"
Grid.Column="1"
Margin="20,0,0,0"
IsChecked="{Binding AutoAcceptBanRandom,Mode=TwoWay}"
Visibility="{Binding AutoAcceptBanHeroVisibility}">
<TextBlock Text="随机禁用"/>
</CheckBox>
<StackPanel Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Orientation="Vertical"
Visibility="{Binding AutoAcceptBanHeroVisibility}">
<ItemsControl ItemsSource="{Binding AutoAcceptBanHeroes, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="5" Rows="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type dbModels:SelectHero}">
<controls:HeroControl Priority="{Binding Priority}"
ChampionId="{Binding ChampionId}"
Title="{Binding Name}"
Image="{Binding HeadPortrait}"
Tag="Ban"
MouseDown="HeroControl_MouseDown" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
<Grid Grid.Row="2" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel>
<Label Content="自动设置符文:" Style="{StaticResource LabelStyle}"/>
<ui:ToggleSwitch IsChecked ="{Binding AutoPerk, Mode=TwoWay}"/>
</StackPanel>
<Button Grid.Column="1"
Margin="15,0,0,0"
Cursor="Hand"
Content="符文页"
Width="60"
Padding="3"
Click="RuneButton_Click"/>
</Grid>
<Grid Grid.Row="3" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Height="36"
VerticalAlignment="Top">
<Label Content="自动选择英雄:" Style="{StaticResource LabelStyle}"/>
<ui:ToggleSwitch IsChecked="{Binding AutoAcceptSelect}"/>
</StackPanel>
<controls:DelayTime Grid.Column="1"
Grid.Row="0"
Margin="10,0,0,0"
Value="{Binding AutoAcceptSelectDelayTime, Mode=TwoWay}"
MaxValue="30"
MinValue="0"
Visibility="{Binding AutoAcceptSelectHeroVisibility}"/>
<StackPanel Grid.Row="1"
Grid.Column="0"
Visibility="{Binding AutoAcceptSelectHeroVisibility}">
<Label Content="位置"
FontSize="14"
Margin="17,0,10,0"
VerticalContentAlignment="Center"/>
<ComboBox Height="35"
Margin="0"
Padding="4,3,0,0"
HorizontalAlignment="Right"
SelectedIndex="{Binding UnRankedSelectPosition}"
ItemsSource="{Binding Positions}">
<ComboBox.ItemTemplate >
<DataTemplate DataType="{x:Type models:Position}">
<StackPanel Orientation="Horizontal">
<Image Style="{StaticResource SelectImageStyle}"
Source="{Binding Image}"/>
<Label Content="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
<CheckBox Grid.Row="1"
Grid.Column="1"
Margin="20,0,0,0"
IsChecked="{Binding AutoAcceptSelectRandom,Mode=TwoWay}"
Visibility="{Binding AutoAcceptSelectHeroVisibility}">
<TextBlock Text="随机选用"/>
</CheckBox>
<ItemsControl Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Visibility="{Binding AutoAcceptSelectHeroVisibility}"
ItemsSource="{Binding AutoAcceptSelectHeroGroups}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding AutoAcceptSelectHeroGroups.Count}" Rows="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:SelectHeroGroup}">
<StackPanel Orientation="Vertical">
<Image Height="40"
Width="40"
Source="{Binding HeroPosition.Image}"/>
<ItemsControl ItemsSource="{Binding SelectHeroes, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1"
Margin="0,10,0,0"
Rows="{Binding SelectHeroes.Count}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type dbModels:SelectHero}">
<controls:HeroControl Priority="{Binding Priority}"
ChampionId="{Binding ChampionId}"
Title="{Binding Name}"
Image="{Binding HeadPortrait}"
Position="{Binding Portrait}"
Tag="Select"
MouseDown="HeroControl_MouseDown" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Grid>
</ui:UiPage>

View File

@ -0,0 +1,70 @@
using CommunityToolkit.Mvvm.DependencyInjection;
using LOL.Assist.App.ViewModels;
using System.Linq;
using System.Windows;
using LOL.Assist.App.Views.Controls;
using LOL.Assist.Core.Models;
using LOL.Assist.Core.DbModels;
using System;
namespace LOL.Assist.App.Views.Pages;
/// <summary>
/// Settings.xaml 的交互逻辑
/// </summary>
public partial class SettingPage
{
private readonly SettingViewModel _settingsViewModel;
public SettingPage()
{
InitializeComponent();
_settingsViewModel = Ioc.Default.GetService<SettingViewModel>()!;
DataContext = _settingsViewModel;
}
private void HeroControl_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
HeroControl heroControl = (HeroControl)sender;
SelectHeroWindow selectHeroWindow = new SelectHeroWindow
{
Owner = Window.GetWindow(this)
};
selectHeroWindow.InitializeComponent();
selectHeroWindow.ShowDialog();
Hero? hero = Global.Heroes.FirstOrDefault(p => p.HeroId == selectHeroWindow.SelectHeroId);
if (hero == null)
return;
SelectHero selectHero = new SelectHero()
{
Name = hero.Name,
HeadPortrait = hero.HeadPortrait,
Priority = heroControl.Priority,
ChampionId = hero.HeroId,
Portrait = heroControl.Position,
};
switch (heroControl.Tag)
{
case "Ban":
selectHero.Type = Core.Enums.ConfigKeyEnum.AutoBan;
_settingsViewModel.AutoAcceptBanHeroes[heroControl.Priority] = selectHero;
break;
case "Select":
selectHero.Type = Core.Enums.ConfigKeyEnum.AutoSelect;
_settingsViewModel.AutoAcceptSelectHeroGroups
.FirstOrDefault(p => p.HeroPosition.Portrait == selectHero.Portrait)!
.SelectHeroes[heroControl.Priority] = selectHero;
break;
}
}
/// <summary>
/// 符文页
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RuneButton_Click(object sender, RoutedEventArgs e)
{
if (Application.Current.MainWindow is not MainWindow maintain)
return;
maintain.FramePage.Source = new Uri("pages/RunePage.xaml", UriKind.Relative);
}
}

View File

@ -0,0 +1,371 @@
<ui:UiWindow
x:Class="LOL.Assist.App.Views.RunePopupWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:LOL.Assist.App.Views"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:models="clr-namespace:LOL.Assist.Core.Models;assembly=LOL.Assist.Core"
xmlns:localModel="clr-namespace:LOL.Assist.App.Model"
mc:Ignorable="d"
Title="RunePopupWindow"
Background="{ui:ThemeResource ApplicationBackgroundBrush}"
WindowStyle="None"
Width="500"
Height="480"
ExtendsContentIntoTitleBar="True"
WindowBackdropType="Mica"
WindowCornerPreference="RoundSmall"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize">
<Window.Resources>
<Style TargetType="TabControl">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="{x:Null}"/>
<Style.Resources>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<ContentPresenter ContentSource="Header"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
</Style>
<Style TargetType="RadioButton">
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="-20,0,0,0"/>
<Setter Property="MinWidth" Value="20"/>
<Setter Property="Focusable" Value="False"/>
</Style>
<Style TargetType="Grid" x:Key="RadioButtonGrid">
<Setter Property="Width" Value="50"/>
<Setter Property="ClipToBounds" Value="True"/>
<Setter Property="Margin" Value="2,0,0,0"/>
</Style>
<Style TargetType="ItemsControl">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Margin" Value="0,30,0,0"/>
</Style>
<Style TargetType="ItemsControl" x:Key="ItemsControlPrimaryStyle">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Margin" Value="0,34,0,0"/>
</Style>
<Style TargetType="ItemsControl" x:Key="ItemsControlSecondStyle">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Margin" Value="0,25,0,0"/>
</Style>
<Style TargetType="ItemsControl" x:Key="ItemsControlGrowingStyle">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Margin" Value="0,5,0,5"/>
</Style>
<Style TargetType="Image" x:Key="RuneRootStyle">
<Setter Property="Margin" Value="2,0,0,0"/>
<Setter Property="Height" Value="40"/>
<Setter Property="Width" Value="40"/>
</Style>
<Style TargetType="UniformGrid">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Focusable" Value="False"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="50"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ui:TitleBar Grid.Row="0"
Name="NotifyMenuBar"
Title="WPF UI - 测试符文页面"
ForceShutdown="False"
MinimizeToTray="False"
ShowHelp="False"
Padding="10,0,0,0"
ShowClose="True"
ShowMaximize="False"
ShowMinimize="False"
Background="{ui:ThemeResource ApplicationBackgroundBrush}"
Height="30" />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="70"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding SelectHeroRune.Name}" FontSize="18" Padding="60,12,0,0"/>
<Button Grid.Column="1" Name="SaveButton" Content="选择英雄" Click="Button_Click"/>
<Button Grid.Column="2" Content="保存" Click="SaveButton_Click"/>
</Grid>
<Grid Grid.Row="2" Width="420">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5.5*"/>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="4.5*"/>
</Grid.ColumnDefinitions>
<!-- 主系符文页 -->
<TabControl Grid.Column="0"
Name="TabControlPrimary"
ItemsSource="{Binding ShowRunes}"
SelectionChanged="TabControlPrimary_SelectionChanged">
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type localModel:RuneGroup}">
<Image Style="{DynamicResource RuneRootStyle}"
Source="{Binding Root.Icon}"
Opacity="{Binding PrimaryOpacity}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type localModel:RuneGroup}">
<StackPanel>
<!-- 主系基石符文 -->
<ItemsControl ItemsSource="{Binding Base}" Style="{StaticResource ItemsControlPrimaryStyle}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Base.Count}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:RuneResponse}">
<Grid Width="60" ClipToBounds="True">
<RadioButton Tag="{Binding Id}"
GroupName="PrimaryBase"
IsChecked="{Binding IsChecked}"
Opacity="{Binding Opacity}">
<Image Source="{Binding Icon}" Width="60"/>
</RadioButton>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--主系一级符文 -->
<ItemsControl ItemsSource="{Binding Valiant}" Style="{StaticResource ItemsControlPrimaryStyle}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Valiant.Count}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:RuneResponse}">
<Grid Style="{StaticResource RadioButtonGrid}">
<RadioButton Tag="{Binding Id}"
GroupName="PrimaryValiant"
IsChecked="{Binding IsChecked}"
Opacity="{Binding Opacity}">
<Image Source="{Binding Icon}" Width="40"/>
</RadioButton>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--主系二级符文-->
<ItemsControl ItemsSource="{Binding Legend}" Style="{StaticResource ItemsControlPrimaryStyle}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Legend.Count}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:RuneResponse}">
<Grid Style="{StaticResource RadioButtonGrid}">
<RadioButton Tag="{Binding Id}"
GroupName="PrimaryLegend"
IsChecked="{Binding IsChecked}"
Opacity="{Binding Opacity}">
<Image Source="{Binding Icon}" Width="40"/>
</RadioButton>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--主系三级符文-->
<ItemsControl ItemsSource="{Binding Combat}" Style="{StaticResource ItemsControlPrimaryStyle}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Combat.Count}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:RuneResponse}">
<Grid Style="{StaticResource RadioButtonGrid}">
<RadioButton Tag="{Binding Id}"
GroupName="PrimaryCombat"
IsChecked="{Binding IsChecked}"
Opacity="{Binding Opacity}">
<Image Source="{Binding Icon}" Width="40"/>
</RadioButton>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
<!-- 副系符文页 -->
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="6.2*"/>
<RowDefinition Height="3.8*"/>
</Grid.RowDefinitions>
<TabControl Grid.Row="0" Name="TabControlSecond"
ItemsSource="{Binding ShowRunes}"
SelectedIndex="1"
SelectionChanged="TabControlSecond_SelectionChanged">
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type localModel:RuneGroup}">
<Image Style="{DynamicResource RuneRootStyle}"
Source="{Binding Root.Icon}"
Opacity="{Binding SecondOpacity}"
Visibility="{Binding SecondVisibility}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type localModel:RuneGroup}">
<StackPanel>
<!-- 副系一级符文 -->
<ItemsControl ItemsSource="{Binding Valiant}" Style="{StaticResource ItemsControlSecondStyle}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Valiant.Count}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:RuneResponse}">
<Grid Style="{StaticResource RadioButtonGrid}"
Tag="{Binding Id}">
<RadioButton Tag="{Binding Id}"
GroupName="SecondValiant"
IsChecked="{Binding IsChecked}"
Opacity="{Binding Opacity}"
Click="RadioButton_Click">
<Image Source="{Binding Icon}" Width="40"/>
</RadioButton>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--副系二级符文-->
<ItemsControl ItemsSource="{Binding Legend}" Style="{StaticResource ItemsControlSecondStyle}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Legend.Count}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:RuneResponse}">
<Grid Style="{StaticResource RadioButtonGrid}"
Tag="{Binding Id}">
<RadioButton Tag="{Binding Id}"
GroupName="SecondLegend"
IsChecked="{Binding IsChecked}"
Opacity="{Binding Opacity}"
Click="RadioButton_Click">
<Image Source="{Binding Icon}" Width="40"/>
</RadioButton>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--副系三级符文-->
<ItemsControl ItemsSource="{Binding Combat}" Style="{StaticResource ItemsControlSecondStyle}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Combat.Count}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:RuneResponse}">
<Grid Style="{StaticResource RadioButtonGrid}"
Tag="{Binding Id}">
<RadioButton Tag="{Binding Id}"
GroupName="SecondCombat"
IsChecked="{Binding IsChecked}"
Opacity="{Binding Opacity}"
Click="RadioButton_Click">
<Image Source="{Binding Icon}" Width="40"/>
</RadioButton>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
<!-- 成长符文 -->
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0" ItemsSource="{Binding ShowGrowingRunes.Valiant}" Style="{StaticResource ItemsControlGrowingStyle}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:RuneResponse }">
<Grid Style="{StaticResource RadioButtonGrid}">
<RadioButton Tag="{Binding Id}"
GroupName="GrowingRuneValiant"
IsChecked="{Binding IsChecked}"
Opacity="{Binding Opacity}">
<Image Source="{Binding Icon}" Width="40"/>
</RadioButton>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Grid.Row="1" ItemsSource="{Binding ShowGrowingRunes.Legend}" Style="{StaticResource ItemsControlGrowingStyle}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:RuneResponse }">
<Grid Style="{StaticResource RadioButtonGrid}">
<RadioButton Tag="{Binding Id}"
GroupName="GrowingRuneLegend"
IsChecked="{Binding IsChecked}"
Opacity="{Binding Opacity}">
<Image Source="{Binding Icon}" Width="40"/>
</RadioButton>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Grid.Row="2" ItemsSource="{Binding ShowGrowingRunes.Combat}" Style="{StaticResource ItemsControlGrowingStyle}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:RuneResponse }">
<Grid Style="{StaticResource RadioButtonGrid}">
<RadioButton Tag="{Binding Id}"
GroupName="GrowingRuneCombat"
IsChecked="{Binding IsChecked}"
Opacity="{Binding Opacity}">
<Image Source="{Binding Icon}" Width="40"/>
</RadioButton>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Grid>
</Grid>
</Grid>
</ui:UiWindow>

View File

@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using CommunityToolkit.Mvvm.DependencyInjection;
using LOL.Assist.App.ViewModels;
using LOL.Assist.Core.DbModels;
using LOL.Assist.Core.Models;
using Newtonsoft.Json;
using RuneGroup = LOL.Assist.App.Model.RuneGroup;
namespace LOL.Assist.App.Views
{
/// <summary>
/// RunePopupWindow.xaml 的交互逻辑
/// </summary>
public partial class RunePopupWindow
{
private readonly RunePopupViewModel _runePopupViewModel;
public RunePopupWindow()
{
_runePopupViewModel = Ioc.Default.GetService<RunePopupViewModel>()!;
DataContext = _runePopupViewModel;
InitializeComponent();
}
/// <summary>
/// 主符文页切换
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TabControlPrimary_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
RuneGroup? addRuneGroup = e.AddedItems.Cast<RuneGroup>().FirstOrDefault();
if (addRuneGroup != null)
{
addRuneGroup.PrimaryOpacity = 1;
addRuneGroup.SecondVisibility = Visibility.Collapsed;
addRuneGroup.Base.ForEach(p => p.IsChecked = false);
addRuneGroup.Valiant.ForEach(p => p.IsChecked = false);
addRuneGroup.Legend.ForEach(p => p.IsChecked = false);
addRuneGroup.Combat.ForEach(p => p.IsChecked = false);
RuneGroup? secondSelectItem = (RuneGroup)TabControlSecond.SelectedItem;
if (secondSelectItem != null && secondSelectItem.Root.Id == addRuneGroup.Root.Id)
TabControlSecond.SelectedItem = _runePopupViewModel.ShowRunes.FirstOrDefault(p => Math.Abs(p.PrimaryOpacity - 1d) != 0);
}
RuneGroup? removeRuneGroup = e.RemovedItems.Cast<RuneGroup>().FirstOrDefault();
if (removeRuneGroup == null)
return;
removeRuneGroup.PrimaryOpacity = 0.2;
removeRuneGroup.SecondVisibility = Visibility.Visible;
removeRuneGroup.Base.ForEach(p => p.IsChecked = false);
removeRuneGroup.Valiant.ForEach(p => p.IsChecked = false);
removeRuneGroup.Legend.ForEach(p => p.IsChecked = false);
removeRuneGroup.Combat.ForEach(p => p.IsChecked = false);
}
/// <summary>
/// 副符文页切换
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TabControlSecond_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
RuneGroup? addRuneGroup = e.AddedItems.Cast<RuneGroup>().FirstOrDefault();
if (addRuneGroup != null)
{
addRuneGroup.SecondOpacity = 1;
addRuneGroup.Base.ForEach(p => p.IsChecked = false);
addRuneGroup.Valiant.ForEach(p => p.IsChecked = false);
addRuneGroup.Legend.ForEach(p => p.IsChecked = false);
addRuneGroup.Combat.ForEach(p => p.IsChecked = false);
}
RuneGroup? removeRuneGroup = e.RemovedItems.Cast<RuneGroup>().FirstOrDefault();
if (removeRuneGroup == null)
return;
removeRuneGroup.SecondOpacity = 0.2;
}
/// <summary>
/// 副符文选中
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RadioButton_Click(object sender, RoutedEventArgs e)
{
RadioButton radioButton = (RadioButton)sender;
RuneGroup? secondSelectItem = (RuneGroup)TabControlSecond.SelectedItem;
if (secondSelectItem == null)
return;
int selectId = int.Parse(radioButton.Tag?.ToString() ?? "0");
List<RuneResponse?> runeResponses = new List<RuneResponse?>
{
secondSelectItem.Valiant.FirstOrDefault(p => p.IsChecked && p.Id != selectId),
secondSelectItem.Legend.FirstOrDefault(p => p.IsChecked && p.Id != selectId),
secondSelectItem.Combat.FirstOrDefault(p => p.IsChecked && p.Id != selectId)
};
IEnumerable<RuneResponse> selectRunes = runeResponses.Where(p => p != null)!;
if (selectRunes.Count() >= 2)
selectRunes.FirstOrDefault()!.IsChecked = false;
}
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
_runePopupViewModel.SaveSelectHeroRune();
Close();
}
protected override void OnClosed(EventArgs e)
{
_runePopupViewModel.SelectHeroRune = null;
foreach (RuneGroup rootRuneGroup in _runePopupViewModel.ShowRunes)
{
rootRuneGroup.Root.IsChecked = false;
rootRuneGroup.SecondOpacity = 0.2;
rootRuneGroup.PrimaryOpacity = 0.2;
}
_runePopupViewModel.ShowGrowingRunes.Valiant.ForEach(p => p.IsChecked = false);
_runePopupViewModel.ShowGrowingRunes.Legend.ForEach(p => p.IsChecked = false);
_runePopupViewModel.ShowGrowingRunes.Combat.ForEach(p => p.IsChecked = false);
base.OnClosed(e);
}
public void SetSelectHeroRune(HeroRune? heroRune)
{
if (heroRune == null)
return;
SaveButton.Visibility = Visibility.Hidden;
_runePopupViewModel.SetSelectHeroRune(heroRune, TabControlPrimary, TabControlSecond);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SelectHeroWindow selectHeroWindow = new SelectHeroWindow
{
Owner = Window.GetWindow(this)
};
selectHeroWindow.InitializeComponent();
selectHeroWindow.ShowDialog();
Hero? hero = Global.Heroes.FirstOrDefault(p => p.HeroId == selectHeroWindow.SelectHeroId);
if (hero == null)
return;
_runePopupViewModel.SelectHeroRune = new HeroRune
{
ChampionId = hero.HeroId,
HeadPortrait = hero.HeadPortrait,
Name = hero.Name,
KeyWords = hero.KeyWords
};
}
}
}

View File

@ -0,0 +1,112 @@
<ui:UiWindow x:Class="LOL.Assist.App.Views.SelectHeroWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:models="clr-namespace:LOL.Assist.Core.Models;assembly=LOL.Assist.Core"
xmlns:controls="clr-namespace:LOL.Assist.App.Views.Controls"
Width="500"
Height="480"
Background="{ui:ThemeResource ApplicationBackgroundBrush}"
WindowStyle="None"
ExtendsContentIntoTitleBar="True"
WindowBackdropType="Mica"
WindowCornerPreference="RoundSmall"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize">
<ui:UiWindow.Resources>
<Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type RadioButton}}">
<Setter Property="Focusable" Value="False" />
<Setter Property="Opacity" Value="0.5" />
<Setter Property="MinWidth" Value="50" />
<Setter Property="Width" Value="50" />
<Setter Property="Height" Value="30" />
<Setter Property="Margin" Value="-20,0,0,0" />
<Setter Property="Padding" Value="0" />
<Setter Property="Cursor" Value="Hand" />
</Style>
</ui:UiWindow.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="60" />
<RowDefinition />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<ui:TitleBar Grid.Row="0"
Name="NotifyMenuBar"
Title="WPF UI - 测试选择页面"
ForceShutdown="False"
MinimizeToTray="False"
ShowHelp="False"
Padding="10,0,0,0"
ShowClose="True"
ShowMaximize="False"
ShowMinimize="False"
Background="{ui:ThemeResource ApplicationBackgroundBrush}"
Height="30" />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<ItemsControl VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.ScrollUnit="Pixel"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Positions}"
AlternationCount="{Binding Positions.Count}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="5" Rows="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:Position}">
<Grid Height="30" Width="30" ClipToBounds="True">
<RadioButton GroupName="HeroPosition"
Tag="{Binding Portrait}"
Click="RadioButton_Click"
Unchecked="RadioButton_Unchecked">
<Image Source="{Binding Image}" />
</RadioButton>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ui:AutoSuggestBox Grid.Column="1" Width="160"
Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
<ui:ProgressRing Grid.Row="2" Visibility="{Binding ShowLoading}" IsIndeterminate="True"/>
<ItemsControl Grid.Row="2"
Name="HeroItem"
VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.ScrollUnit="Pixel"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding ShowHeroes}">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"
CanContentScroll="True">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding ShowHeroColumns}" Rows="{Binding ShowHeroRows}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:Hero}">
<controls:HeroControl Margin="0,2"
Tag="{Binding HeroId}"
Title="{Binding Name}"
Image="{Binding HeadPortrait}"
MouseDown="Grid_MouseDown" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</ui:UiWindow>

View File

@ -0,0 +1,57 @@
using System;
using CommunityToolkit.Mvvm.DependencyInjection;
using LOL.Assist.App.ViewModels;
using System.Windows;
using LOL.Assist.App.Views.Controls;
using System.Linq;
using System.Windows.Controls;
using System.Collections.Generic;
using LOL.Assist.Core.Models;
using System.Windows.Threading;
using Wpf.Ui.Controls;
namespace LOL.Assist.App.Views;
/// <summary>
/// SelectHeroPage.xaml 的交互逻辑
/// </summary>
public partial class SelectHeroWindow
{
public int SelectHeroId { get; private set; }
private readonly SelectHeroViewModel _selectHeroViewModel;
public SelectHeroWindow()
{
_selectHeroViewModel = Ioc.Default.GetService<SelectHeroViewModel>()!;
DataContext = _selectHeroViewModel;
InitializeComponent();
Loaded += This_Loaded;
}
private void Grid_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
HeroControl heroControl = (HeroControl)sender;
SelectHeroId = (int)heroControl.Tag;
DialogResult = true;
}
private void This_Loaded(object sender, RoutedEventArgs e) => _selectHeroViewModel.LoadShowHeroes();
protected override void OnClosed(EventArgs e)
{
_selectHeroViewModel.SelectPosition = null;
base.OnClosed(e);
}
private void RadioButton_Click(object sender, RoutedEventArgs e)
{
RadioButton radioButton = (RadioButton)sender;
radioButton.IsChecked = !((radioButton.IsChecked ?? false) && Math.Abs(radioButton.Opacity - 1d) == 0);
radioButton.Opacity = radioButton.IsChecked!.Value ? 1 : 0.5;
_selectHeroViewModel.SelectPosition = radioButton.IsChecked.Value ? radioButton.Tag.ToString() : null;
}
private void RadioButton_Unchecked(object sender, RoutedEventArgs e)
{
RadioButton radioButton = (RadioButton)sender;
radioButton.Opacity = 0.5;
}
}

View File

@ -0,0 +1,6 @@
@echo off
dotnet publish -r win-x86 ^
-c Release ^
-p:PublishSingleFile=true ^
-p:IncludeNativeLibrariesForSelfExtract=true ^
-o ./bin/Publish

View File

@ -0,0 +1,26 @@
using System.ComponentModel.DataAnnotations;
using LOL.Assist.Core.Enums;
namespace LOL.Assist.Core.DbModels;
/// <summary>
/// 配置信息
/// </summary>
public class Config
{
/// <summary>
/// 配置
/// </summary>
[Key]
public ConfigKeyEnum Key { get; set; }
/// <summary>
/// 配置值
/// </summary>
[MaxLength(50)]
public string Value { get; set; } = string.Empty;
/// <summary>
/// 延迟时间
/// </summary>
public int DelayTime { get; set; }
}

View File

@ -0,0 +1,36 @@
using System.ComponentModel.DataAnnotations;
namespace LOL.Assist.Core.DbModels
{
/// <summary>
/// 符文列表
/// </summary>
public class HeroRune
{
/// <summary>
/// 角色id
/// </summary>
[Key]
public int ChampionId { get; set; }
/// <summary>
/// 头像地址
/// </summary>
[MaxLength(200)]
public string HeadPortrait { get; set; } = string.Empty;
/// <summary>
/// 名称: 黑暗之女
/// </summary>
[MaxLength(50)]
public string Name { get; set; } = string.Empty;
/// <summary>
/// 搜索词
/// </summary>
[MaxLength(200)]
public string KeyWords { get; set; } = string.Empty;
/// <summary>
/// 符文信息
/// </summary>
[MaxLength(200)]
public string RuneJson { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,40 @@
using System.ComponentModel.DataAnnotations;
using LOL.Assist.Core.Enums;
namespace LOL.Assist.Core.DbModels;
public class SelectHero
{
/// <summary>
/// 优先级
/// </summary>
[Key]
public int Priority { get; set; }
/// <summary>
/// 位置信息
/// </summary>
[Key,MaxLength(10)]
public string Portrait{get;set;}=string.Empty;
/// <summary>
/// ban、pick
/// </summary>
[Key, MaxLength(5)]
public ConfigKeyEnum Type { get; set; } = ConfigKeyEnum.AutoBan;
/// <summary>
/// 角色id
/// </summary>
public int ChampionId { get; set; }
/// <summary>
/// 头像地址
/// </summary>
[MaxLength(200)]
public string HeadPortrait { get; set; } = string.Empty;
/// <summary>
/// 名称: 黑暗之女
/// </summary>
[MaxLength(50)]
public string Name { get; set; } = string.Empty;
}

View File

@ -0,0 +1,37 @@
namespace LOL.Assist.Core.Enums;
/// <summary>
/// 配置信息枚举
/// </summary>
[Flags]
public enum ConfigKeyEnum
{
/// <summary>
/// 自动接受
/// </summary>
AutoAccept,
/// <summary>
/// 自动禁用
/// </summary>
AutoBan,
/// <summary>
/// 自动选择
/// </summary>
AutoSelect,
/// <summary>
/// 自动随机禁用
/// </summary>
AutoBanRandom,
/// <summary>
/// 自动随机选择
/// </summary>
AutoSelectRandom,
/// <summary>
/// 非排位模式,自动优先选择对应位置英雄
/// </summary>
UnRankedSelectPosition,
/// <summary>
/// 自动符文天赋
/// </summary>
AutoPerk,
}

View File

@ -0,0 +1,21 @@
namespace LOL.Assist.Core.Enums;
/// <summary>
/// 选择会话枚举
/// </summary>
[Flags]
public enum SelectSessionEnum
{
/// <summary>
///
/// </summary>
Normal,
/// <summary>
/// 禁用
/// </summary>
Ban,
/// <summary>
/// 选用
/// </summary>
Pick
}

View File

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LOL.Assist.Core.Enums
{
/// <summary>
/// 用户事件url
/// </summary>
public static class SubscribeEventUri
{
/// <summary>
/// 匹配到对局
/// </summary>
public const string GameReadyCheckTeam = "/lol-lobby-team-builder/v1/matchmaking";
public const string GameSessionTeam = "/lol-lobby-team-builder/champ-select/v1/session";
/// <summary>
/// 寻找对局,包含对局状态信息
/// </summary>
public const string SearchGame = "/lol-matchmaking/v1/search";
/// <summary>
/// 对局会话信息
/// </summary>
public const string GameSession = "/lol-gameflow/v1/session";
/// <summary>
/// 对局阶段状态信息
/// Matchmaking:排队
/// ReadyCheck:待接受对局
/// ChampSelect:对局准备
/// InProgress:对局中
/// WaitingForStats
/// PreEndOfGame
/// EndOfGame
/// </summary>
public const string GamePhase = "/lol-gameflow/v1/gameflow-phase";
/// <summary>
/// 对局校验(待确认接受或取消对局)
/// </summary>
public const string GameReadyCheck = "/lol-matchmaking/v1/ready-check";
/// <summary>
/// 对局会话信息
/// </summary>
public const string GameSessionChamp = "/lol-champ-select/v1/session";
/// <summary>
/// 玩家禁用或选择英雄通知
/// /lol-champ-select/v1/summoners/{actorCellId}
/// </summary>
public const string SummonersChamp = "/lol-champ-select/v1/summoners";
/// <summary>
/// 禁用或选择的英雄
/// /lol-champ-select/v1/grid-champions/{championId}
/// </summary>
public const string SelectChampion = "/lol-champ-select/v1/grid-champions";
/// <summary>
/// 聊天会话
/// 聊天组:/lol-chat/v1/conversations/{id}
/// 参与者:/lol-chat/v1/conversations/{id}/participants/{pId}
/// 消息:/lol-chat/v1/conversations/{id}/messages/{mId}
/// </summary>
public const string ChatConversations = "/lol-chat/v1/conversations";
/// <summary>
/// 地图位置信息(红蓝),召唤师位置信息
/// </summary>
public const string MapSideSummoners = "/lol-champ-select/v1/pin-drop-notification";
}
}

View File

@ -0,0 +1,58 @@
using System.Linq.Expressions;
using LOL.Assist.Core.DbModels;
using LOL.Assist.Core.Enums;
namespace LOL.Assist.Core.IServices;
/// <summary>
/// 数据持久化服务
/// </summary>
public interface IDbService
{
/// <summary>
/// 增改数据
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
bool InsertOrUpdate<T>(T request) where T : class, new();
/// <summary>
/// 根据key获取配置信息
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
Config GetConfig(ConfigKeyEnum key);
/// <summary>
/// 获取全部信息
/// </summary>
/// <returns></returns>
IList<T> GetAll<T>() where T : class, new();
/// <summary>
/// 删除数据
/// </summary>
/// <param name="where"></param>
/// <returns></returns>
bool Delete<T>(Expression<Func<T,bool>> where) where T : class, new();
/// <summary>
/// 获取全部配置信息
/// </summary>
/// <returns></returns>
IList<T> GetAll<T>(Expression<Func<T, bool>> where) where T : class, new();
/// <summary>
/// 根据key获取配置信息
/// </summary>
/// <param name="key"></param>
/// <param name="delayTime"></param>
/// <returns></returns>
bool UpdateConfigDelayTime(ConfigKeyEnum key, int delayTime);
/// <summary>
/// 获取全部配置信息
/// </summary>
/// <returns></returns>
T? Get<T>(Expression<Func<T, bool>> where) where T : class, new();
}

View File

@ -0,0 +1,23 @@
using LOL.Assist.Core.Models;
using Refit;
namespace LOL.Assist.Core.IServices;
/// <summary>
/// 基础数据服务
/// </summary>
public interface IGameService
{
/// <summary>
/// 获取英雄列表
/// </summary>
/// <returns></returns>
[Get("/images/lol/act/img/js/heroList/hero_list.js")]
Task<HeroListResponse> GetHeroListAsync();
/// <summary>
/// 获取符文列表
/// </summary>
/// <returns></returns>
[Get("/images/lol/act/img/js/runeList/rune_list.js")]
Task<RuneListResponse> GetRuneListAsync();
}

View File

@ -0,0 +1,17 @@
using LOL.Assist.Core.Models;
using Refit;
namespace LOL.Assist.Core.IServices;
/// <summary>
/// 基础数据服务
/// </summary>
public interface ILOLService
{
/// <summary>
/// 获取英雄列表
/// </summary>
/// <returns></returns>
[Get("/act/lbp/common/guides/guideschampion_position.js")]
Task<string> GetHeroPositionAsync();
}

View File

@ -0,0 +1,125 @@
using LOL.Assist.Core.Models;
using LOL.Assist.Core.Models.SubscribeMessage;
using Refit;
namespace LOL.Assist.Core.IServices;
public interface ILcuService
{
/// <summary>
/// 接受对局
/// </summary>
/// <returns></returns>
[Post("/lol-matchmaking/v1/ready-check/accept")]
Task<ApiResponse<string>> TeamBuilderReadyCheckAsync();
/// <summary>
/// 查询游戏会话
/// </summary>
/// <returns></returns>
[Get("/lol-gameflow/v1/session")]
Task<ApiResponse<string>> GetGameFlowSessionAsync();
//lol-lobby/v1/lobby/custom/cancel-champ-select
/// <summary>
/// 获取选人会话
/// </summary>
/// <returns></returns>
[Get("/lol-champ-select/v1/session")]
Task<ApiResponse<ChampSelectMessage>> GetChampSessionAsync();
/// <summary>
/// 选择禁用英雄
/// </summary>
/// <param name="actionId">当前用户action id</param>
/// <param name="body"></param>
/// <returns></returns>
[Patch("/lol-champ-select/v1/session/actions/{actionId}")]
Task<ApiResponse<string>> ChampSelectSessionActionsAsync(int actionId, [Body] SessionActionsRequest body);
/// <summary>
/// 获取当前符文页
/// </summary>
/// <returns></returns>
[Get("/lol-perks/v1/currentpage")]
Task<ApiResponse<PerkCurrentPageResponse>> GetPerkCurrentPageAsync();
/// <summary>
/// 删除符文页
/// </summary>
/// <returns></returns>
[Delete("/lol-perks/v1/pages/{id}")]
Task<ApiResponse<string>> DeletePerkPageAsync(long id);
/// <summary>
/// 修改符文页
/// </summary>
/// <returns></returns>
[Put("/lol-perks/v1/pages/{id}")]
Task<ApiResponse<string>> UpdatePerkPageAsync(long id,[Body] PerkPageRequest body);
/// <summary>
/// 创建符文页
/// </summary>
/// <returns></returns>
[Post("/lol-perks/v1/pages")]
Task<ApiResponse<string>> CreatePerkPageAsync([Body] PerkPageRequest body);
/// <summary>
/// 获取的是整个房间的数据,包含了观众、机器人。
/// </summary>
/// <returns></returns>
[Get("/lol-lobby/v2/lobby")]
Task<ApiResponse<string>> GetRoomAsync();
/// <summary>
/// 获取的是队伍真实玩家数据,观众、机器人不在列表中。
/// </summary>
/// <returns></returns>
[Get("/lol-lobby/v2/lobby/members")]
Task<ApiResponse<string>> GetRoomMembersAsync();
/// <summary>
/// 获取当前聊天消息组信息
/// </summary>
/// <returns></returns>
[Get("/lol-chat/v1/conversations")]
Task<ApiResponse<List<ConversationsResponse>>> GetConversationsAsync();
/// <summary>
/// 获取当前聊天组消息
/// </summary>
/// <param name="conversationId"></param>
/// <returns></returns>
[Get("/lol-chat/v1/conversations/{conversationId}/messages")]
Task<ApiResponse<string>> GetConversationsMessageAsync(string conversationId);
/// <summary>
/// 发送消息到聊天组
/// </summary>
/// <param name="conversationId"></param>
/// <param name="body"></param>
/// <returns></returns>
[Post("/lol-chat/v1/conversations/{conversationId}/messages")]
Task<ApiResponse<string>> SendConversationsMessageAsync(string conversationId, [Body] ConversationsMsgRequest body);
/// <summary>
/// 设置英雄皮肤及召唤师技能
/// </summary>
/// <param name="body"></param>
/// <returns></returns>
[Patch("/lol-champ-select/v1/session/my-selection")]
Task<ApiResponse<string>> ChampSelectSession([Body] ChampSelectSessionRequest body);
/// <summary>
/// 获取jwt
/// </summary>
/// <returns></returns>
[Get("/lol-summoner/v1/current-summoner/jwt")]
Task<ApiResponse<string>> GetCurrentSummonerJwt();
/// <summary>
/// 设置头像
/// </summary>
/// <returns></returns>
[Put("/lol-summoner/v1/current-summoner/icon")]
Task<ApiResponse<string>> CurrentSummonerIcon([Body]SummonerIconRequest body);
}

View File

@ -0,0 +1,20 @@
using LOL.Assist.Core.Services;
namespace LOL.Assist.Core.IServices;
public interface IWindowService
{
/// <summary>
/// 默认进程名称
/// </summary>
public const string LolProcessName = "League of Legends";
/// <summary>
/// 设置窗体位置
/// </summary>
WindowService.GetWindowsProcess.Rect? GetWindowsRectLocation(string name = LolProcessName);
/// <summary>
/// 设置窗体大小
/// </summary>
bool SetWindowsSize(int x, int y, string name = LolProcessName);
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="FreeSql.Provider.Sqlite" Version="3.2.700" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Refit.Newtonsoft.Json" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,66 @@
using System.Diagnostics;
using System.Net.Http;
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
namespace LOL.Assist.Core;
/// <summary>
/// lol认证信息获取
/// </summary>
public static class LolAuthProvider
{
public static (string?, int) GetCommandLineText()
{
var processes = Process.GetProcessesByName("LeagueClientUx");
if (processes.Length == 0)
return (null, 0);
Process process = new Process();
process.StartInfo = new ProcessStartInfo("wmic", "process where name='LeagueClientUx.exe' GET commandline")
{
RedirectStandardOutput = true,
CreateNoWindow = true,
};
process.Start();
string result = process.StandardOutput.ReadToEnd();
process.WaitForExit();
if (process.ExitCode != 0)
return (null, 0);
string[] args = result.Split(" ");
string? token = args.FirstOrDefault(p => p.Contains("--remoting-auth-token"))?.Split("=").Last().TrimEnd('"');
int port = int.Parse(args.FirstOrDefault(p => p.Contains("--app-port"))?.Split("=").Last().TrimEnd('"') ?? string.Empty);
return (token, port);
}
}
/// <summary>
/// lol连接认证信息
/// </summary>
public class AuthHeaderHandler : HttpClientHandler
{
private static Uri _clientUri = new("https://loclahost");
private static AuthenticationHeaderValue _authenticationHeader;
public AuthHeaderHandler()
{
ServerCertificateCustomValidationCallback = delegate { return true; };
}
public static void SetUriToken(string url, AuthenticationHeaderValue authenticationHeader)
{
_clientUri = new Uri(url);
_authenticationHeader = authenticationHeader;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.RequestUri = new Uri(_clientUri, request.RequestUri?.PathAndQuery);
request.Headers.Authorization = _authenticationHeader;
string content = request.Content != null ? await request.Content.ReadAsStringAsync(cancellationToken) : string.Empty;
request.Content = new StringContent(content, Encoding.UTF8, "application/json");
HttpResponseMessage result = await base.SendAsync(request, cancellationToken);
return result;
}
}

View File

@ -0,0 +1,165 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
using System.Net.Http.Headers;
using System.Net.WebSockets;
using System.Text;
namespace LOL.Assist.Core;
public class LolConnect
{
private static readonly HttpClient _httpClient = new(new AuthHeaderHandler())
{
BaseAddress = new Uri("https://127.0.0.1")
};
private static ClientWebSocket _webSocket;
private static bool _isConnectStatue;
private static Task? _connectTask;
private static readonly object ConnectLock = new();
public static Action ConnectNotificationAction;
public static Dictionary<string, Action<JToken?, string, string>> _eventAction = new();
public static void ManageEventAction(bool isAdd, string eventUri, Action<JToken?, string, string> action)
{
bool have = _eventAction.ContainsKey(eventUri);
if (!have)
{
if (!isAdd)
return;
_eventAction.Add(eventUri, action);
}
else
{
List<Delegate> invocations = _eventAction[eventUri].GetInvocationList().ToList();
invocations.Remove(action);
if (!invocations.Any())
{
_eventAction.Remove(eventUri);
}
else
{
invocations.ForEach(p => action += (Action<JToken?, string, string>)p);
_eventAction[eventUri] = action;
}
}
}
public HttpClient GetHttpClient() => _httpClient;
public LolConnect Connect()
{
if (_isConnectStatue || _connectTask != null)
return this;
lock (ConnectLock)
{
if (_connectTask != null)
return this;
_connectTask = Task.Factory.StartNew(() =>
{
do
{
try
{
(string? password, int port) = LolAuthProvider.GetCommandLineText();
if (password == null)
{
Thread.Sleep(4000);
continue;
}
string baseUrl = string.Concat("127.0.0.1:", port);
AuthenticationHeaderValue authenticationHeader = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(Encoding.UTF8.GetBytes($"riot:{password}")));
AuthHeaderHandler.SetUriToken($"https://{baseUrl}", authenticationHeader);
_webSocket = new ClientWebSocket();
_webSocket.Options.RemoteCertificateValidationCallback = delegate { return true; };
_webSocket.Options.SetRequestHeader("Authorization", authenticationHeader.ToString());
_webSocket.ConnectAsync(new Uri($"wss://{baseUrl}/"), CancellationToken.None).Wait();
if (_webSocket.State != WebSocketState.Open)
throw new Exception("websocket 未打开");
_isConnectStatue = true;
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
Thread.Sleep(6000);
}
} while (!_isConnectStatue);
ConnectNotificationAction?.Invoke();
WebSocketConnectMessage();
});
}
return this;
}
private LolConnect ReConnect()
{
_isConnectStatue = false;
_connectTask = null;
return Connect();
}
private void WebSocketConnectMessage()
{
if (_webSocket.State != WebSocketState.Open)
return;
//订阅所有事件
_webSocket.SendAsync("[5, \"OnJsonApiEvent\"]"u8.ToArray(), WebSocketMessageType.Text, true, CancellationToken.None);
//最终消息
List<byte> allBytes = new List<byte>();
//缓冲区
ArraySegment<byte> arraySegment = new ArraySegment<byte>(new byte[1024 * 4]);
WebSocketReceiveResult webSocketReceiveResult = _webSocket.ReceiveAsync(arraySegment, CancellationToken.None).Result;
while (!webSocketReceiveResult.CloseStatus.HasValue)
{
try
{
allBytes.AddRange(arraySegment.Take(webSocketReceiveResult.Count));
if (!webSocketReceiveResult.EndOfMessage)
continue;
string message = Encoding.UTF8.GetString(allBytes.ToArray());
if (!string.IsNullOrWhiteSpace(message))
OnMessage(message.Replace("\0", string.Empty));
allBytes = new List<byte>();
}
finally
{
webSocketReceiveResult = _webSocket.ReceiveAsync(arraySegment, CancellationToken.None).Result;
}
}
//重新连接
ReConnect();
}
private void OnMessage(string message)
{
if (string.IsNullOrWhiteSpace(message))
return;
JToken? data = JsonConvert.DeserializeObject<JToken>(message)?[2];
if (data == null) return;
string? uri = data["uri"]?.ToString();
if (string.IsNullOrWhiteSpace(uri) ||
uri.Contains("/data-store") ||
uri.Contains("/riotclient") ||
uri.Contains("/riot") ||
uri.Contains("/lol-game-client-chat") ||
uri.Contains("/lol-store") ||
uri.Contains("/lol-clash") ||
uri.Contains("/lol-login") ||
uri.Contains("/lol-loadouts") ||
uri.Contains("/lol-platform") ||
uri.Contains("/lol-settings") ||
uri.Contains("/lol-hovercard/") ||
uri.Contains("/lol-chat/v1") && !uri.Contains("conversations") ||
uri.Contains("/lol-client-config") ||
uri.Contains("/lol-inventory") ||
uri.Contains("/lol-challenges") ||
uri.Contains("/lol-league-session") ||
uri.Contains("/gcloud-voice-chat") ||
uri.Contains("/lol-content-targeting"))
return;
Debug.WriteLine($"{uri}\r\n{message}");
string? key = _eventAction.Keys.FirstOrDefault(p => p.Contains(uri) || uri.Contains(p));
if (key == null)
return;
_eventAction[key].Invoke(data["data"], uri, data["eventType"]!.ToString());
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LOL.Assist.Core.Models
{
public class ChampSelectSessionRequest
{
public int SelectedSkinId { get; set; }
public int Spell1Id { get; set; }
public int Spell2Id { get; set; }
public int WardSkinId { get; set; }
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LOL.Assist.Core.Models
{
/// <summary>
/// 会话消息发送请求
/// </summary>
public class ConversationsMsgRequest
{
/// <summary>
/// 消息类型
/// </summary>
public string Type { get; set; } = "chat";
/// <summary>
/// 消息内容
/// </summary>
public string Body { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,117 @@
namespace LOL.Assist.Core.Models;
public class ConversationsResponse
{
/// <summary>
///
/// </summary>
public string GameName { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string GameTag { get; set; } = string.Empty;
/// <summary>
/// 聊天id
/// </summary>
public string Id { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string InviterId { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string IsMuted { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public LastMessage? LastMessage { get; set; }
/// <summary>
///
/// </summary>
public MucJwtDto? MucJwtDto { get; set; }
/// <summary>
///
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string Password { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string Pid { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string TargetRegion { get; set; } = string.Empty;
/// <summary>
/// chat
/// </summary>
public string Type { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public int UnreadMessageCount { get; set; }
}
public class LastMessage
{
/// <summary>
/// 消息内容
/// </summary>
public string Body { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string FromId { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public int FromObfuscatedSummonerId { get; set; }
/// <summary>
///
/// </summary>
public string FromPid { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public int FromSummonerId { get; set; }
/// <summary>
///
/// </summary>
public string Id { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string IsHistorical { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string Timestamp { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string Type { get; set; } = string.Empty;
}
public class MucJwtDto
{
/// <summary>
///
/// </summary>
public string ChannelClaim { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string Domain { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string Jwt { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string TargetRegion { get; set; } = string.Empty;
}

View File

@ -0,0 +1,54 @@
namespace LOL.Assist.Core.Models;
public class GameTeamResponse
{
/// <summary>
///
/// </summary>
public int ChampionId { get; set; }
/// <summary>
///
/// </summary>
public int LastSelectedSkinIndex { get; set; }
/// <summary>
/// 图标id
/// </summary>
public int ProfileIconId { get; set; }
/// <summary>
///
/// </summary>
public string Puuid { get; set; } = string.Empty;
/// <summary>
/// 上单TOP
/// 打野JUNGLE
/// 中单MIDDLE
/// 下路BOTTOM
/// 辅助UTILITY
/// </summary>
public string SelectedPosition { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string SelectedRole { get; set; } = string.Empty;
/// <summary>
/// 召唤者id
/// </summary>
public long SummonerId { get; set; }
/// <summary>
/// 召唤者内部名称
/// </summary>
public string SummonerInternalName { get; set; } = string.Empty;
/// <summary>
/// 召唤者名称
/// </summary>
public string SummonerName { get; set; } = string.Empty;
/// <summary>
/// 是否组队
/// </summary>
public bool TeamOwner { get; set; }
/// <summary>
/// 组队id相同则为组队
/// </summary>
public int TeamParticipantId { get; set; }
}

View File

@ -0,0 +1,99 @@
namespace LOL.Assist.Core.Models
{
public class Hero
{
/// <summary>
/// id1
/// </summary>
public int HeroId { get; set; }
/// <summary>
/// 名称: 黑暗之女
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// 别名: Annie
/// </summary>
public string Alias { get; set; } = string.Empty;
/// <summary>
/// 头像地址
/// </summary>
public string HeadPortrait => $"https://game.gtimg.cn/images/lol/act/img/champion/{Alias}.png";
/// <summary>
/// 标题名称:安妮
/// </summary>
public string Title { get; set; } = string.Empty;
/// <summary>
/// 角色类型mage、fighter、tank
/// </summary>
public List<string> Roles { get; set; } = new();
/// <summary>
/// 是否周免
/// </summary>
public int IsWeekFree { get; set; }
/// <summary>
/// 攻击能力值
/// </summary>
public int Attack { get; set; }
/// <summary>
/// 防御能力值
/// </summary>
public int Defense { get; set; }
/// <summary>
/// 魔法能力值
/// </summary>
public int Magic { get; set; }
/// <summary>
/// 上手难度值
/// </summary>
public int Difficulty { get; set; }
/// <summary>
/// 选中声音地址
/// </summary>
public string SelectAudio { get; set; } = string.Empty;
/// <summary>
/// 禁用声音地址
/// </summary>
public string BanAudio { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public int IsARAMWeekFree { get; set; }
/// <summary>
///
/// </summary>
public int IsPermanentWeekFree { get; set; }
/// <summary>
/// 改动标签:改动英雄
/// </summary>
public string ChangeLabel { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public int GoldPrice { get; set; }
/// <summary>
///
/// </summary>
public int CouponPrice { get; set; }
/// <summary>
/// 阵营名称
/// </summary>
public string? Camp { get; set; }
/// <summary>
/// 阵营id
/// </summary>
public int? CampId { get; set; }
/// <summary>
/// 搜索词:安妮,黑暗之女,火女,Annie,anni,heianzhinv,huonv,an,hazn,hn
/// </summary>
public string KeyWords { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string Instance_Id { get; set; } = string.Empty;
/// <summary>
/// 位置推荐值
/// </summary>
public HeroPositionRecommend PositionRecommend { get; set; } = new();
}
}

View File

@ -0,0 +1,18 @@
namespace LOL.Assist.Core.Models;
public record HeroListResponse
{
public List<Hero> Hero { get; set; } = new();
/// <summary>
///
/// </summary>
public string Version { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string FileName { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string FileTime { get; set; } = string.Empty;
}

View File

@ -0,0 +1,28 @@
namespace LOL.Assist.Core.Models;
/// <summary>
/// 英雄位置推荐
/// </summary>
public class HeroPositionRecommend
{
/// <summary>
/// 上单推荐值
/// </summary>
public int Top { get; set; }
/// <summary>
/// 打野推荐值
/// </summary>
public int Jungle { get; set; }
/// <summary>
/// 中单推荐值
/// </summary>
public int Mid { get; set; }
/// <summary>
/// 下路推荐值
/// </summary>
public int Bottom { get; set; }
/// <summary>
/// 辅助推荐值
/// </summary>
public int Support { get; set; }
}

View File

@ -0,0 +1,6 @@
namespace LOL.Assist.Core.Models;
public class LcuMessageBase
{
}

View File

@ -0,0 +1,16 @@
namespace LOL.Assist.Core.Models;
/// <summary>
/// 当前符文页
/// </summary>
public class PerkCurrentPageResponse
{
/// <summary>
/// 主系id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LOL.Assist.Core.Models
{
public class PerkPageRequest
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 主系id
/// </summary>
public int PrimaryStyleId { get; set; }
/// <summary>
/// 副系id
/// </summary>
public int SubStyleId { get; set; }
/// <summary>
/// 选中的符文id 【4-主系符文id、2-副系符文id、3-成长符文id】
/// </summary>
public List<int> SelectedPerkIds { get; set; }
/// <summary>
///
/// </summary>
public bool Current { get; set; } = true;
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LOL.Assist.Core.Models
{
/// <summary>
/// 位置信息
/// </summary>
public class Position
{
public Position(string name,string image, string portrait)
{
Name = name;
Image = image;
Portrait = portrait;
}
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// Portrait
/// </summary>
public string Portrait { get; set; }
/// <summary>
/// 图标
/// </summary>
public string Image { get; set; }
}
}

View File

@ -0,0 +1,72 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace LOL.Assist.Core.Models;
public class RuneListResponse
{
/// <summary>
/// 符文列表
/// </summary>
public Dictionary<int, RuneResponse> Rune { get; set; }
}
[ObservableObject]
public partial class RuneResponse
{
/// <summary>
/// id
/// </summary>
public int Id { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// 图片地址
/// </summary>
public string Icon { get; set; } = string.Empty;
/// <summary>
/// 样式名称
/// </summary>
public string StyleName { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string Key { get; set; } = string.Empty;
/// <summary>
/// +10%攻击速度
/// </summary>
public string LongDesc { get; set; } = string.Empty;
/// <summary>
/// +10%攻击速度
/// </summary>
public string ShortDesc { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string SlotLabel { get; set; } = string.Empty;
/// <summary>
/// +10%攻击速度
/// </summary>
public string Tooltip { get; set; } = string.Empty;
/// <summary>
/// 分组信息
/// </summary>
public string GroupName { get; set; } = string.Empty;
/// <summary>
/// 是否选中
/// </summary>
[ObservableProperty]
private bool _isChecked;
[ObservableProperty]
private double _opacity= 0.2;
partial void OnIsCheckedChanged(bool value) => Opacity = value ? 1 : 0.2;
}

View File

@ -0,0 +1,18 @@
using LOL.Assist.Core.DbModels;
using System.Collections.ObjectModel;
namespace LOL.Assist.Core.Models
{
public class SelectHeroGroup
{
/// <summary>
/// 位置信息
/// </summary>
public Position HeroPosition { get; set; }
/// <summary>
/// 列表信息
/// </summary>
public ObservableCollection<SelectHero> SelectHeroes { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace LOL.Assist.Core.Models;
/// <summary>
///
/// </summary>
/// <param name="ChampionId"></param>
/// <param name="Type">ban、pick</param>
/// <param name="Completed"></param>
public record SessionActionsRequest(int ChampionId, string Type, bool Completed = true,bool IsAllyAction=true);

View File

@ -0,0 +1,119 @@
namespace LOL.Assist.Core.Models.SubscribeMessage;
/// <summary>
/// 禁用、选择响应信息
/// </summary>
public class ChampSelectMessage
{
/// <summary>
/// 0ban、1pick
/// </summary>
public List<List<ActionsItem>> Actions { get; set; }
/// <summary>
/// 位置序号
/// </summary>
public int LocalPlayerCellId { get; set; }
/// <summary>
/// 队伍信息
/// </summary>
public List<TeamInfo>? MyTeam{get; set; }
}
public class ActionsItem
{
/// <summary>
/// 位置序号
/// </summary>
public int ActorCellId { get; set; }
/// <summary>
/// 禁用或选用的英雄id
/// </summary>
public int ChampionId { get; set; }
/// <summary>
/// 是否完成操作
/// </summary>
public bool Completed { get; set; }
/// <summary>
///
/// </summary>
public int Id { get; set; }
/// <summary>
///
/// </summary>
public bool IsAllyAction { get; set; }
/// <summary>
/// 是否进行中
/// </summary>
public bool IsInProgress { get; set; }
/// <summary>
/// ban、pick
/// </summary>
public string Type { get; set; } = "ban";
}
/// <summary>
/// 队伍信息
/// </summary>
public class TeamInfo
{
/// <summary>
/// 位置信息
/// top、jungle、middle、bottom、utility
/// </summary>
public string AssignedPosition { get; set; } = string.Empty;
/// <summary>
/// 位置序号
/// </summary>
public int CellId { get; set; }
/// <summary>
///
/// </summary>
public int ChampionId { get; set; }
/// <summary>
///
/// </summary>
public int ChampionPickIntent { get; set; }
/// <summary>
///
/// </summary>
public string EntitledFeatureType { get; set; }
/// <summary>
///
/// </summary>
public string NameVisibilityType { get; set; }
/// <summary>
///
/// </summary>
public string ObfuscatedPuuid { get; set; }
/// <summary>
///
/// </summary>
public string ObfuscatedSummonerId { get; set; }
/// <summary>
///
/// </summary>
public string Puuid { get; set; }
/// <summary>
///
/// </summary>
public int SelectedSkinId { get; set; }
/// <summary>
///
/// </summary>
public int Spell1Id { get; set; }
/// <summary>
///
/// </summary>
public int Spell2Id { get; set; }
/// <summary>
///
/// </summary>
public string SummonerId { get; set; }
/// <summary>
///
/// </summary>
public int Team { get; set; }
/// <summary>
///
/// </summary>
public int WardSkinId { get; set; }
}

View File

@ -0,0 +1,30 @@
namespace LOL.Assist.Core.Models.SubscribeMessage;
public record ReadyCheckMessage
{
/// <summary>
///
/// </summary>
public List<string>? DeclinerIds { get; set; }
/// <summary>
///
/// </summary>
public string? DodgeWarning { get; set; }
/// <summary>
///
/// </summary>
public string? PlayerResponse { get; set; }
/// <summary>
/// 状态信息
/// InProgress 进行中,EveryoneReady
/// </summary>
public string? State { get; set; }
/// <summary>
///
/// </summary>
public string? SuppressUx { get; set; }
/// <summary>
/// 记时0-15s
/// </summary>
public int Timer { get; set; }
}

View File

@ -0,0 +1,32 @@
namespace LOL.Assist.Core.Models.SubscribeMessage;
/// <summary>
/// 部分字段
/// </summary>
public class SelectSummonersMessage
{
/// <summary>
/// 召集id
/// </summary>
public int CellId { get; set; }
/// <summary>
/// 英雄id
/// </summary>
public int ChampionId { get; set; }
/// <summary>
/// 英雄名称
/// </summary>
public string ChampionName { get; set; }
/// <summary>
/// "" /"pick"
/// </summary>
public string ActiveActionType { get; set; }
/// <summary>
///
/// </summary>
public bool IsActingNow { get; set; }
/// <summary>
///
/// </summary>
public bool IsDonePicking { get; set; }
}

View File

@ -0,0 +1,6 @@
namespace LOL.Assist.Core.Models;
public class SummonerIconRequest
{
public int ProfileIconId { get; set; }
}

View File

@ -0,0 +1,82 @@
using LOL.Assist.Core.Enums;
using LOL.Assist.Core.IServices;
using System.Linq.Expressions;
using LOL.Assist.Core.DbModels;
namespace LOL.Assist.Core.Services;
/// <summary>
/// 数据持久化服务
/// </summary>
public class DbService : IDbService
{
private readonly IFreeSql _freeSql;
public DbService(IFreeSql freeSql)
{
_freeSql = freeSql;
}
/// <summary>
/// 增改数据
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public bool InsertOrUpdate<T>(T request) where T : class, new()
{
int result = _freeSql.InsertOrUpdate<T>().SetSource(request).ExecuteAffrows();
return result > 0;
}
/// <summary>
/// 获取全部配置信息
/// </summary>
/// <returns></returns>
public T? Get<T>(Expression<Func<T, bool>> where) where T : class, new() => _freeSql.Select<T>().Where(where).ToOne();
/// <summary>
/// 获取全部配置信息
/// </summary>
/// <returns></returns>
public IList<T> GetAll<T>() where T : class, new() => _freeSql.Select<T>().ToList();
/// <summary>
/// 获取全部配置信息
/// </summary>
/// <returns></returns>
public IList<T> GetAll<T>(Expression<Func<T, bool>> where) where T : class, new() => _freeSql.Select<T>().Where(where).ToList();
/// <summary>
/// 删除数据
/// </summary>
/// <param name="where"></param>
/// <returns></returns>
public bool Delete<T>(Expression<Func<T, bool>> where) where T : class, new()
{
int result = _freeSql.Delete<T>().Where(where).ExecuteAffrows();
return result > 0;
}
/// <summary>
/// 根据key获取配置信息
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public Config GetConfig(ConfigKeyEnum key)
{
return _freeSql.Select<Config>().Where(p => p.Key == key).ToOne();
}
/// <summary>
/// 根据key获取配置信息
/// </summary>
/// <param name="key"></param>
/// <param name="delayTime"></param>
/// <returns></returns>
public bool UpdateConfigDelayTime(ConfigKeyEnum key, int delayTime)
{
int execute = _freeSql.Update<Config>()
.Set(p => p.DelayTime, delayTime)
.Where(p => p.Key == key)
.ExecuteAffrows();
return execute > 0;
}
}

View File

@ -0,0 +1,99 @@
using LOL.Assist.Core.IServices;
using System.Runtime.InteropServices;
namespace LOL.Assist.Core.Services;
public class WindowService : IWindowService
{
/// <summary>
/// 设置窗体位置
/// </summary>
public GetWindowsProcess.Rect? GetWindowsRectLocation(string name = IWindowService.LolProcessName)
{
IntPtr windowPtr = GetWindowsProcess.FindWindow(null, name);
GetWindowsProcess.Rect result = new GetWindowsProcess.Rect();
GetWindowsProcess.GetWindowRect(windowPtr, ref result);
if (result.Top < 0 || result.Left < 0)
return null;
return result;
}
/// <summary>
/// 设置窗体大小
/// </summary>
public bool SetWindowsSize(int x, int y, string name = IWindowService.LolProcessName)
{
IntPtr windowPtr = GetWindowsProcess.FindWindow(null, name);
bool result = GetWindowsProcess.SetWindowPos(windowPtr, -1, 0, 0, x, y, 0x0002 | 0x0200);
return result;
}
/// <summary>
/// 参考文档
/// https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-getwindow
/// https://cloud.tencent.com/developer/article/1088763
/// </summary>
public static class GetWindowsProcess
{
/// <summary>
/// 获取所有窗口
/// </summary>
/// <returns></returns>
[DllImport("User32.dll")]
public static extern IntPtr GetWindow();
/// <summary>
/// 查找窗体
/// </summary>
/// <param name="lpClassName">窗体的类名称比如Form、Window。若不知道指定为null即可</param>
/// <param name="lpWindowName">窗体的标题/文字</param>
/// <returns></returns>
[DllImport("user32.dll", EntryPoint = "FindWindowA", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
/// <summary>
/// 设置窗口位置及大小
/// https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setwindowpos
/// </summary>
/// <param name="hWnd"></param>
/// <param name="hWndInsertAfter"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="cx"></param>
/// <param name="cy"></param>
/// <param name="flags"></param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint flags);
/// <summary>
/// 获取窗口大小及位置信息
/// </summary>
/// <param name="hWnd"></param>
/// <param name="lpRect"></param>
/// <returns></returns>
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, ref Rect lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
/// <summary>
/// 最左坐标
/// </summary>
public int Left;
/// <summary>
/// 最上坐标
/// </summary>
public int Top;
/// <summary>
/// 最右坐标
/// </summary>
public int Right;
/// <summary>
/// 最下坐标
/// </summary>
public int Bottom;
}
}
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.10" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,45 @@
using Microsoft.Extensions.Options;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
//openApi.jsonÔÚÏßµØÖ· https://www.mingweisamuel.com/lcu-schema/tool/
builder.Services.AddEndpointsApiExplorer();
List<string?> swaggerJsonFiles = Directory.GetFiles($"{Environment.CurrentDirectory}/wwwroot")
.Select(Path.GetFileName)
.ToList();
builder.Services.AddSwaggerGen(genOptions =>
{
foreach (var file in swaggerJsonFiles)
{
genOptions.SwaggerDoc(file, new Microsoft.OpenApi.Models.OpenApiInfo()
{
Title = file,
Version = "v"
});
}
});
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseSwagger();
app.UseSwaggerUI(uiOptions =>
{
foreach (var file in swaggerJsonFiles)
{
uiOptions.SwaggerEndpoint($"/{file}", file);
}
uiOptions.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.None);
});
app.UseAuthorization();
app.UseStaticFiles();
app.MapControllers();
app.Run();

View File

@ -0,0 +1,31 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:36890",
"sslPort": 0
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5103",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
{"apis":[{"path":"/player-reporting"},{"path":"/product-session"},{"path":"/rso-mobile-ui"},{"path":"/play-restrictions"},{"path":"/riotclient"},{"path":"/app-command"},{"path":"/player-session-lifecycle"},{"path":"/rso-auth"},{"path":"/product-integration-deps"},{"path":"/jwt-authenticator"},{"path":"/player-account-service"},{"path":"/facebook-account"},{"path":"/client-config"},{"path":"/player-preferences"},{"path":"/chat"},{"path":"/swagger"},{"path":"/age-restriction"},{"path":"/player-affinity"},{"path":"/data-store"},{"path":"/rnet-sanitizer"},{"path":"/product-metadata"},{"path":"/product-localization"},{"path":"/reference"},{"path":"/riot-messaging-service"},{"path":"/client-feature-flags"},{"path":"/cookie-jar"},{"path":"/privacy"},{"path":"/kr-account"},{"path":"/apple-account"},{"path":"/patch"},{"path":"/mailbox"},{"path":"/platform-ui"},{"path":"/name-check"},{"path":"/platform-login"},{"path":"/voice-chat"},{"path":"/gamecenter-account"},{"path":"/plugin-manager"},{"path":"/social"},{"path":"/payments"},{"path":"/riot-client-auth"},{"path":"/nintendo-account"},{"path":"/info-radiator"},{"path":"/eula"},{"path":"/riot-status"},{"path":"/game-session"},{"path":"/player-account"},{"path":"/ga-warning"},{"path":"/loyalty"},{"path":"/tencent-launcher"},{"path":"/first-party-fulfillment"},{"path":"/playstation-account"},{"path":"/ga-restriction"},{"path":"/prime-gaming"},{"path":"/rso-authenticator"},{"path":"/private-settings"},{"path":"/product-integration"},{"path":"/entitlements-service"},{"path":"/disambiguation"},{"path":"/entitlements"},{"path":"/google-account"},{"path":"/scd"},{"path":"/xbox-account"},{"path":"/riot-login"},{"path":"/accounts-security"},{"path":"/anti-addiction"},{"path":"/friends"},{"path":"/base"}],"info":{"description":"","title":""},"swaggerVersion":"1.2"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
助手.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB