├── .config
└── dotnet-tools.json
├── .github
└── workflows
│ └── aquamai.yaml
├── .gitignore
├── AquaMai.Build
├── AquaMai.Build.csproj
├── GenerateExampleConfig.cs
└── PostBuildPatch.cs
├── AquaMai.Config.HeadlessLoader
├── AquaMai.Config.HeadlessLoader.csproj
├── ConfigAssemblyLoader.cs
├── CustomAssemblyResolver.cs
├── HeadlessConfigInterface.cs
├── HeadlessConfigLoader.cs
├── Polyfills.cs
└── ResourceLoader.cs
├── AquaMai.Config.Interfaces
├── AquaMai.Config.Interfaces.csproj
├── IConfig.cs
├── IConfigComment.cs
├── IConfigEntryAttribute.cs
├── IConfigMigrationManager.cs
├── IConfigParser.cs
├── IConfigSectionAttribute.cs
├── IConfigSerializer.cs
├── IConfigView.cs
├── IReflectionManager.cs
├── IReflectionProvider.cs
└── Polyfills.cs
├── AquaMai.Config
├── ApiVersion.cs
├── AquaMai.Config.csproj
├── Attributes
│ ├── ConfigCollapseNamespaceAttribute.cs
│ ├── ConfigComment.cs
│ ├── ConfigEntryAttribute.cs
│ ├── ConfigSectionAttribute.cs
│ └── EnableCondition.cs
├── Config.cs
├── ConfigParser.cs
├── ConfigSerializer.cs
├── ConfigView.cs
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── Migration
│ ├── ConfigMigrationManager.cs
│ ├── ConfigMigration_V1_0_V2_0.cs
│ ├── ConfigMigration_V2_0_V2_1.cs
│ ├── ConfigMigration_V2_1_V2_2.cs
│ └── IConfigMigration.cs
├── Polyfills.cs
├── Reflection
│ ├── MonoCecilAssemblyReflectionProvider.cs
│ ├── ReflectionManager.cs
│ └── SystemReflectionProvider.cs
├── Types
│ ├── AdxKeyMap.cs
│ ├── KeyCodeID.cs
│ └── KeyCodeOrName.cs
└── Utility.cs
├── AquaMai.Core
├── AquaMai.Core.csproj
├── Attributes
│ ├── EnableGameVersionAttribute.cs
│ ├── EnableIfAttribute.cs
│ └── EnableImplicitlyIfAttribute.cs
├── BuildInfo.cs
├── ConfigLoader.cs
├── Helpers
│ ├── EnableConditionHelper.cs
│ ├── FileSystem.cs
│ ├── GameInfo.cs
│ ├── GuiSizes.cs
│ ├── JsonHelper.cs
│ ├── KeyListener.cs
│ ├── MessageHelper.cs
│ ├── MusicDirHelper.cs
│ ├── NetPacketHook.cs
│ ├── SharedInstances.cs
│ └── Shim.cs
├── Resources
│ ├── I18nSingleAssemblyHook.cs
│ ├── Locale.Designer.cs
│ ├── Locale.resx
│ └── Locale.zh.resx
└── Startup.cs
├── AquaMai.ErrorReport
├── AquaMai.ErrorReport.csproj
├── ChecksumCalculator.cs
├── CrashForm.Designer.cs
├── CrashForm.cs
├── CrashForm.resx
├── FileHeaderMeta.cs
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── Polyfills.cs
└── Program.cs
├── AquaMai.Mods
├── AquaMai.Mods.csproj
├── DeprecationWarning.cs
├── Enhancement
│ ├── ServerAnnouncement.cs
│ ├── ServerNotice.cs
│ └── ServerResources.cs
├── Fancy
│ ├── CustomButton.cs
│ ├── CustomCreditsString.cs
│ ├── CustomLogo.cs
│ ├── CustomPlaceName.cs
│ ├── CustomSkins.cs
│ ├── CustomTrackStartDiff.cs
│ ├── CustomVersionString.cs
│ ├── DemoMaster.cs
│ ├── GamePlay
│ │ ├── AlignCircleSlideJudgeDisplay.cs
│ │ ├── BreakSlideJudgeBlink.cs
│ │ ├── CustomNoteTypes
│ │ │ ├── CustomNoteTypes.cs
│ │ │ └── Libs
│ │ │ │ ├── CustomSlideNoteData.cs
│ │ │ │ ├── MaiGeometry.cs
│ │ │ │ ├── ParametricSlidePath.cs
│ │ │ │ ├── SlideCodeParser.cs
│ │ │ │ ├── SlideDataBuilder.cs
│ │ │ │ └── SlidePathGenerator.cs
│ │ ├── DisableTrackStartTabs.cs
│ │ ├── ExtendNotesPool.cs
│ │ ├── FanJudgeFlip.cs
│ │ ├── HideHanabi.cs
│ │ ├── JudgeDisplay4B.cs
│ │ ├── RealisticRandomJudge.cs
│ │ ├── SlideArrowAnimation.cs
│ │ ├── SlideFadeInTweak.cs
│ │ ├── SlideLayerReverse.cs
│ │ └── TrackStartProcessTweak.cs
│ ├── HideMask.cs
│ ├── README.md
│ ├── RandomBgm.cs
│ └── Triggers.cs
├── Fix
│ ├── Common.cs
│ ├── DebugFeature.cs
│ ├── DisableReboot.cs
│ ├── FixCheckAuth.cs
│ ├── FixConnSlide.cs
│ ├── FixLevelDisplay.cs
│ ├── FixSlideAutoPlay.cs
│ ├── Legacy
│ │ └── FixQuickRetry130.cs
│ ├── README.md
│ └── Stability
│ │ ├── FixMissingCharaCrash.cs
│ │ └── SanitizeUserData.cs
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── GameSettings
│ ├── CreditConfig.cs
│ ├── ForceAsServer.cs
│ ├── JudgeAdjust.cs
│ ├── README.md
│ └── TouchSensitivity.cs
├── GameSystem
│ ├── AdxHidInput.cs
│ ├── Assets
│ │ ├── Fonts.cs
│ │ ├── LoadAssetBundleWithoutManifest.cs
│ │ ├── LoadLocalImages.cs
│ │ └── MovieLoader.cs
│ ├── CustomCameraId.cs
│ ├── DisableTimeout.cs
│ ├── KeyMap.cs
│ ├── OptionLoadFix.cs
│ ├── QuickRetry.cs
│ ├── README.md
│ ├── RemoveEncryption.cs
│ ├── SinglePlayer.ExteraMouseInput.cs
│ ├── SinglePlayer.cs
│ ├── Sound.cs
│ ├── TestProof.cs
│ ├── TouchPanelBaudRate.cs
│ ├── TouchToButtonInput.cs
│ ├── Unlock.cs
│ └── Window.cs
├── General.cs
├── Polyfills.cs
├── Tweaks
│ ├── IgnoreAimeServerError.cs
│ ├── LockFrameRate.cs
│ ├── README.md
│ ├── ResetTouch.cs
│ ├── SkipUserVersionCheck.cs
│ └── TimeSaving
│ │ ├── EntryToMusicSelection.cs
│ │ ├── ExitToSave.cs
│ │ ├── IWontTapOrSlideVigorously.cs
│ │ ├── SkipEventInfo.cs
│ │ ├── SkipGoodbyeScreen.cs
│ │ ├── SkipStartupDelays.cs
│ │ ├── SkipStartupWarning.cs
│ │ └── SkipTrackStart.cs
├── Types
│ └── ConditionalMessage.cs
├── UX
│ ├── DisableLightOutGame.cs
│ ├── HideSelfMadeCharts.cs
│ ├── ImmediateSave.cs
│ ├── JudgeAccuracyInfo.cs
│ ├── NoAmDaemonAlert.cs
│ ├── OneKeyEntryEnd.cs
│ ├── OneKeyRetrySkip.cs
│ ├── PracticeMode
│ │ ├── Libs
│ │ │ └── PractiseModeUI.cs
│ │ └── PracticeMode.cs
│ ├── QuickEndPlay.cs
│ ├── README.md
│ ├── SelectionDetail.cs
│ └── TestModeHook.cs
└── Utils
│ ├── AntiLag.cs
│ ├── DisplayFrameRate.cs
│ ├── LogNetworkErrors.cs
│ ├── LogNetworkRequests.cs
│ ├── LogUnity.cs
│ ├── LogUserId.cs
│ ├── README.md
│ ├── ShowErrorLog.cs
│ └── ShowNetErrorDetail.cs
├── AquaMai.sln
├── AquaMai
├── AquaMai.csproj
├── AssemblyLoader.cs
├── BuildInfo.cs
├── Main.cs
├── Properties
│ └── AssemblyInfo.cs
└── configSort.yaml
├── Libs
├── .gitignore
├── 0Harmony.dll
├── Assembly-CSharp-firstpass.dll
├── MelonLoader.dll
├── Mono.Cecil.dll
├── Mono.Posix.dll
├── Mono.Security.dll
├── System.Configuration.dll
├── System.Core.dll
├── System.Numerics.dll
├── System.Security.dll
├── System.Xml.dll
├── System.dll
├── Unity.Analytics.DataPrivacy.dll
├── Unity.TextMeshPro.dll
├── UnityEngine.AIModule.dll
├── UnityEngine.ARModule.dll
├── UnityEngine.AccessibilityModule.dll
├── UnityEngine.AnimationModule.dll
├── UnityEngine.AssetBundleModule.dll
├── UnityEngine.AudioModule.dll
├── UnityEngine.BaselibModule.dll
├── UnityEngine.ClothModule.dll
├── UnityEngine.ClusterInputModule.dll
├── UnityEngine.ClusterRendererModule.dll
├── UnityEngine.CoreModule.dll
├── UnityEngine.CrashReportingModule.dll
├── UnityEngine.DirectorModule.dll
├── UnityEngine.FileSystemHttpModule.dll
├── UnityEngine.GameCenterModule.dll
├── UnityEngine.GridModule.dll
├── UnityEngine.HotReloadModule.dll
├── UnityEngine.IMGUIModule.dll
├── UnityEngine.ImageConversionModule.dll
├── UnityEngine.InputModule.dll
├── UnityEngine.JSONSerializeModule.dll
├── UnityEngine.LocalizationModule.dll
├── UnityEngine.Networking.dll
├── UnityEngine.ParticleSystemModule.dll
├── UnityEngine.PerformanceReportingModule.dll
├── UnityEngine.Physics2DModule.dll
├── UnityEngine.PhysicsModule.dll
├── UnityEngine.ProfilerModule.dll
├── UnityEngine.ScreenCaptureModule.dll
├── UnityEngine.SharedInternalsModule.dll
├── UnityEngine.SpatialTracking.dll
├── UnityEngine.SpriteMaskModule.dll
├── UnityEngine.SpriteShapeModule.dll
├── UnityEngine.StreamingModule.dll
├── UnityEngine.StyleSheetsModule.dll
├── UnityEngine.SubstanceModule.dll
├── UnityEngine.TLSModule.dll
├── UnityEngine.TerrainModule.dll
├── UnityEngine.TerrainPhysicsModule.dll
├── UnityEngine.TextCoreModule.dll
├── UnityEngine.TextRenderingModule.dll
├── UnityEngine.TilemapModule.dll
├── UnityEngine.Timeline.dll
├── UnityEngine.TimelineModule.dll
├── UnityEngine.UI.dll
├── UnityEngine.UIElementsModule.dll
├── UnityEngine.UIModule.dll
├── UnityEngine.UNETModule.dll
├── UnityEngine.UmbraModule.dll
├── UnityEngine.UnityAnalyticsModule.dll
├── UnityEngine.UnityConnectModule.dll
├── UnityEngine.UnityTestProtocolModule.dll
├── UnityEngine.UnityWebRequestAssetBundleModule.dll
├── UnityEngine.UnityWebRequestAudioModule.dll
├── UnityEngine.UnityWebRequestModule.dll
├── UnityEngine.UnityWebRequestTextureModule.dll
├── UnityEngine.UnityWebRequestWWWModule.dll
├── UnityEngine.VFXModule.dll
├── UnityEngine.VRModule.dll
├── UnityEngine.VehiclesModule.dll
├── UnityEngine.VideoModule.dll
├── UnityEngine.WindModule.dll
├── UnityEngine.XRModule.dll
├── UnityEngine.dll
└── mscorlib.dll
├── README.md
├── build.cake
└── build.ps1
/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "cake.tool": {
6 | "version": "5.0.0",
7 | "commands": [
8 | "dotnet-cake"
9 | ],
10 | "rollForward": false
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/.github/workflows/aquamai.yaml:
--------------------------------------------------------------------------------
1 | name: AquaMai Build
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | pull_request_target:
7 |
8 | jobs:
9 | build:
10 | runs-on: windows-latest
11 | steps:
12 | - name: Fix Git line encoding bug
13 | run: |
14 | git config --global core.autocrlf false
15 | git config --global core.eol lf
16 |
17 | - uses: actions/checkout@v4
18 | with:
19 | fetch-depth: 0
20 |
21 | - name: Get Git Describe
22 | run: echo "GIT_DESCRIBE=$(git describe --tags --always)" >> $env:GITHUB_ENV
23 |
24 | - name: Checkout Assets
25 | uses: clansty/checkout@main
26 | with:
27 | repository: MewoLab/AquaMai-Build-Assets
28 | ssh-key: ${{ secrets.BUILD_ASSETS_KEY }}
29 | path: build-assets
30 | max-attempts: 50
31 | min-retry-interval: 1
32 | max-retry-interval: 5
33 |
34 | - name: Build AquaMai
35 | shell: cmd
36 | run: |
37 | copy /y build-assets\SDEZ\* Libs
38 | powershell ./build.ps1
39 |
40 | - name: Prepare artifact
41 | shell: cmd
42 | run: |
43 | cd Output
44 | mkdir Upload
45 | move AquaMai.dll Upload
46 | move AquaMai.*.toml Upload
47 |
48 | - uses: actions/upload-artifact@v4
49 | with:
50 | name: AquaMai
51 | path: Output\Upload
52 |
53 | - name: Upload CI release
54 | if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main'
55 | shell: cmd
56 | run: build-assets\Releaser\AquaMaiReleaser.exe "Output\Upload\AquaMai.dll" "${{ env.GIT_DESCRIBE }}"
57 |
58 | - name: Send to Telegram
59 | if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main'
60 | run: |
61 | $Uri = "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMediaGroup"
62 | $Form = @{
63 | chat_id = "-1002231087502"
64 | media = @(
65 | @{ type = "document"; media = "attach://aquamai_main"; caption = "${{ env.GIT_DESCRIBE }}`n${{ github.event.commits[0].message }}" },
66 | @{ type = "document"; media = "attach://aquamai_zh" }
67 | @{ type = "document"; media = "attach://aquamai_en" }
68 | ) | ConvertTo-Json
69 | aquamai_main = Get-Item Output\Upload\AquaMai.dll
70 | aquamai_zh = Get-Item Output\Upload\AquaMai.zh.toml
71 | aquamai_en = Get-Item Output\Upload\AquaMai.en.toml
72 | }
73 | Invoke-RestMethod -Uri $uri -Form $Form -Method Post
74 |
--------------------------------------------------------------------------------
/AquaMai.Build/AquaMai.Build.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Release
5 | AnyCPU
6 | {4C0C68C3-8B2E-4CA8-A26D-AE87CF2A38A5}
7 | Library
8 | AquaMai.Build
9 | AquaMai.Build
10 | netstandard2.0
11 | 512
12 | true
13 | 12
14 | 414;NU1702
15 | $(ProjectDir)../Libs/
16 | $(ProjectDir)../Output/
17 | false
18 | false
19 |
20 |
21 |
22 | false
23 | None
24 | true
25 | prompt
26 | 4
27 | true
28 | false
29 |
30 |
31 |
32 | DEBUG
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | $(LibsPath)Mono.Cecil.dll
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/AquaMai.Build/GenerateExampleConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using AquaMai.Config.Interfaces;
4 | using AquaMai.Config.HeadlessLoader;
5 | using Microsoft.Build.Framework;
6 | using Microsoft.Build.Utilities;
7 |
8 | public class GenerateExampleConfig : Task
9 | {
10 | [Required]
11 | public string DllPath { get; set; }
12 |
13 | [Required]
14 | public string OutputPath { get; set; }
15 |
16 | public override bool Execute()
17 | {
18 | try
19 | {
20 | var configInterface = HeadlessConfigLoader.LoadFromPacked(DllPath);
21 | var config = configInterface.CreateConfig();
22 | foreach (var lang in (string[]) ["en", "zh"])
23 | {
24 | var configSerializer = configInterface.CreateConfigSerializer(new IConfigSerializer.Options()
25 | {
26 | Lang = lang,
27 | IncludeBanner = true,
28 | OverrideLocaleValue = true
29 | });
30 | var example = configSerializer.Serialize(config);
31 | File.WriteAllText(Path.Combine(OutputPath, $"AquaMai.{lang}.toml"), example);
32 | }
33 |
34 | return true;
35 | }
36 | catch (Exception e)
37 | {
38 | Log.LogErrorFromException(e, true);
39 | return false;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/AquaMai.Build/PostBuildPatch.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.Compression;
4 | using System.Linq;
5 | using Microsoft.Build.Framework;
6 | using Microsoft.Build.Utilities;
7 | using Mono.Cecil;
8 |
9 | public class PostBuildPatch : Task
10 | {
11 | [Required]
12 | public string DllPath { get; set; }
13 |
14 | public override bool Execute()
15 | {
16 | try
17 | {
18 | var assembly = AssemblyDefinition.ReadAssembly(new MemoryStream(File.ReadAllBytes(DllPath)));
19 | CompressEmbeddedAssemblies(assembly);
20 | var outputStream = new MemoryStream();
21 | assembly.Write(outputStream);
22 | File.WriteAllBytes(DllPath, outputStream.ToArray());
23 | return true;
24 | }
25 | catch (Exception e)
26 | {
27 | Log.LogErrorFromException(e, true);
28 | return false;
29 | }
30 | }
31 |
32 | private void CompressEmbeddedAssemblies(AssemblyDefinition assembly)
33 | {
34 | foreach (var resource in assembly.MainModule.Resources.ToList())
35 | {
36 | if ((resource.Name.EndsWith(".dll") || resource.Name.EndsWith(".exe") || resource.Name.EndsWith(".yaml")) && resource is EmbeddedResource embeddedResource)
37 | {
38 | using var compressedStream = new MemoryStream();
39 | using (var deflateStream = new DeflateStream(compressedStream, CompressionLevel.Optimal))
40 | {
41 | embeddedResource.GetResourceStream().CopyTo(deflateStream);
42 | }
43 | var compressedBytes = compressedStream.ToArray();
44 |
45 | Log.LogMessage($"Compressed {resource.Name} from {embeddedResource.GetResourceStream().Length} to {compressedBytes.Length} bytes");
46 |
47 | assembly.MainModule.Resources.Remove(resource);
48 | assembly.MainModule.Resources.Add(new EmbeddedResource(resource.Name + ".compressed", resource.Attributes, compressedBytes));
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/AquaMai.Config.HeadlessLoader/AquaMai.Config.HeadlessLoader.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Release
5 | AnyCPU
6 | {6B5E1F3E-D012-4CFB-A2FA-26A6CE06BE66}
7 | Library
8 | AquaMai.Config.HeadlessLoader
9 | AquaMai.Config.HeadlessLoader
10 | netstandard2.0
11 | 512
12 | true
13 | 12
14 | 414;NU1702
15 | $(ProjectDir)../Libs/
16 | $(ProjectDir)../Output/
17 | false
18 | false
19 |
20 |
21 |
22 | false
23 | None
24 | true
25 | prompt
26 | 4
27 | true
28 | false
29 |
30 |
31 |
32 | DEBUG
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | $(LibsPath)Mono.Cecil.dll
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/AquaMai.Config.HeadlessLoader/ConfigAssemblyLoader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using Mono.Cecil;
7 |
8 | namespace AquaMai.Config.HeadlessLoader;
9 |
10 | class ConfigAssemblyLoader
11 | {
12 | public static Assembly LoadConfigAssembly(AssemblyDefinition assembly)
13 | {
14 | var references = assembly.MainModule.AssemblyReferences;
15 | foreach (var reference in references)
16 | {
17 | if (reference.Name == "mscorlib" || reference.Name == "System" || reference.Name.StartsWith("System."))
18 | {
19 | reference.Name = "netstandard";
20 | reference.Version = new Version(2, 0, 0, 0);
21 | reference.PublicKeyToken = null;
22 | }
23 | }
24 |
25 | var targetFrameworkAttribute = assembly.CustomAttributes.FirstOrDefault(attr => attr.AttributeType.Name == "TargetFrameworkAttribute");
26 | if (targetFrameworkAttribute != null)
27 | {
28 | targetFrameworkAttribute.ConstructorArguments.Clear();
29 | targetFrameworkAttribute.ConstructorArguments.Add(new CustomAttributeArgument(
30 | assembly.MainModule.TypeSystem.String, ".NETStandard,Version=v2.0"));
31 | targetFrameworkAttribute.Properties.Clear();
32 | targetFrameworkAttribute.Properties.Add(new Mono.Cecil.CustomAttributeNamedArgument(
33 | "FrameworkDisplayName", new CustomAttributeArgument(assembly.MainModule.TypeSystem.String, ".NET Standard 2.0")));
34 | }
35 |
36 | var stream = new MemoryStream();
37 | assembly.Write(stream);
38 | FixLoadedAssemblyResolution();
39 | return AppDomain.CurrentDomain.Load(stream.ToArray());
40 | }
41 |
42 | private static bool FixedLoadedAssemblyResolution = false;
43 |
44 | // XXX: Why, without this, the already loaded assemblies are not resolved?
45 | public static void FixLoadedAssemblyResolution()
46 | {
47 | if (FixedLoadedAssemblyResolution)
48 | {
49 | return;
50 | }
51 | FixedLoadedAssemblyResolution = true;
52 |
53 | var loadedAssemblies = new Dictionary();
54 | foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
55 | {
56 | loadedAssemblies[assembly.FullName] = assembly;
57 | }
58 |
59 | AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
60 | {
61 | if (loadedAssemblies.TryGetValue(args.Name, out var assembly))
62 | {
63 | return assembly;
64 | }
65 | return null;
66 | };
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/AquaMai.Config.HeadlessLoader/CustomAssemblyResolver.cs:
--------------------------------------------------------------------------------
1 | using Mono.Cecil;
2 |
3 | namespace AquaMai.Config.HeadlessLoader;
4 |
5 | public class CustomAssemblyResolver : DefaultAssemblyResolver
6 | {
7 | public new void RegisterAssembly(AssemblyDefinition assembly)
8 | {
9 | base.RegisterAssembly(assembly);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/AquaMai.Config.HeadlessLoader/HeadlessConfigInterface.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using AquaMai.Config.Interfaces;
4 | using Mono.Cecil;
5 |
6 | namespace AquaMai.Config.HeadlessLoader;
7 |
8 | public class HeadlessConfigInterface
9 | {
10 | private readonly Assembly loadedConfigAssembly;
11 |
12 | public IReflectionProvider ReflectionProvider { get; init; }
13 | public IReflectionManager ReflectionManager { get; init; }
14 |
15 | public string ApiVersion { get; init; }
16 |
17 | public HeadlessConfigInterface(Assembly loadedConfigAssembly, AssemblyDefinition modsAssembly)
18 | {
19 | this.loadedConfigAssembly = loadedConfigAssembly;
20 |
21 | ReflectionProvider = Activator.CreateInstance(
22 | loadedConfigAssembly.GetType("AquaMai.Config.Reflection.MonoCecilReflectionProvider"), [modsAssembly]) as IReflectionProvider;
23 | ReflectionManager = Activator.CreateInstance(
24 | loadedConfigAssembly.GetType("AquaMai.Config.Reflection.ReflectionManager"), [ReflectionProvider]) as IReflectionManager;
25 | ApiVersion = loadedConfigAssembly
26 | .GetType("AquaMai.Config.ApiVersion")
27 | .GetField("Version", BindingFlags.Public | BindingFlags.Static)
28 | .GetRawConstantValue() as string;
29 | }
30 |
31 | public IConfigView CreateConfigView(string tomlString = null)
32 | {
33 | return Activator.CreateInstance(
34 | loadedConfigAssembly.GetType("AquaMai.Config.ConfigView"),
35 | tomlString == null ? [] : [tomlString]) as IConfigView;
36 | }
37 |
38 | public IConfig CreateConfig()
39 | {
40 | return Activator.CreateInstance(
41 | loadedConfigAssembly.GetType("AquaMai.Config.Config"), [ReflectionManager]) as IConfig;
42 | }
43 |
44 | public IConfigParser GetConfigParser()
45 | {
46 | return loadedConfigAssembly
47 | .GetType("AquaMai.Config.ConfigParser")
48 | .GetField("Instance", BindingFlags.Public | BindingFlags.Static)
49 | .GetValue(null) as IConfigParser;
50 | }
51 |
52 | public IConfigSerializer CreateConfigSerializer(IConfigSerializer.Options options)
53 | {
54 | return Activator.CreateInstance(
55 | loadedConfigAssembly.GetType("AquaMai.Config.ConfigSerializer"), [options]) as IConfigSerializer;
56 | }
57 |
58 | public IConfigMigrationManager GetConfigMigrationManager()
59 | {
60 | return loadedConfigAssembly
61 | .GetType("AquaMai.Config.Migration.ConfigMigrationManager")
62 | .GetField("Instance", BindingFlags.Public | BindingFlags.Static)
63 | .GetValue(null) as IConfigMigrationManager;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/AquaMai.Config.HeadlessLoader/HeadlessConfigLoader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using Mono.Cecil;
6 |
7 | namespace AquaMai.Config.HeadlessLoader;
8 |
9 | public class HeadlessConfigLoader
10 | {
11 | public static HeadlessConfigInterface LoadFromPacked(string fileName)
12 | {
13 | using var file = new FileStream(fileName, FileMode.Open);
14 | return LoadFromPacked(file);
15 | }
16 |
17 | public static HeadlessConfigInterface LoadFromPacked(byte[] assemblyBinary)
18 | => LoadFromPacked(new MemoryStream(assemblyBinary));
19 |
20 | public static HeadlessConfigInterface LoadFromPacked(Stream assemblyStream)
21 | => LoadFromPacked(AssemblyDefinition.ReadAssembly(assemblyStream));
22 |
23 | public static HeadlessConfigInterface LoadFromPacked(AssemblyDefinition assembly)
24 | {
25 | return LoadFromUnpacked(
26 | ResourceLoader.LoadEmbeddedAssemblies(assembly).Values);
27 | }
28 |
29 | public static HeadlessConfigInterface LoadFromUnpacked(IEnumerable assemblyBinariess) =>
30 | LoadFromUnpacked(assemblyBinariess.Select(binary => new MemoryStream(binary)));
31 |
32 | public static HeadlessConfigInterface LoadFromUnpacked(IEnumerable assemblyStreams)
33 | {
34 | var resolver = new CustomAssemblyResolver();
35 | var assemblies = assemblyStreams
36 | .Select(
37 | assemblyStream =>
38 | AssemblyDefinition.ReadAssembly(
39 | assemblyStream,
40 | new ReaderParameters() {
41 | AssemblyResolver = resolver
42 | }))
43 | .ToArray();
44 | foreach (var assembly in assemblies)
45 | {
46 | resolver.RegisterAssembly(assembly);
47 | }
48 |
49 | var configAssembly = assemblies.First(assembly => assembly.Name.Name == "AquaMai.Config");
50 | if (configAssembly == null)
51 | {
52 | throw new InvalidOperationException("AquaMai.Config assembly not found");
53 | }
54 | var loadedConfigAssembly = ConfigAssemblyLoader.LoadConfigAssembly(configAssembly);
55 | var modsAssembly = assemblies.First(assembly => assembly.Name.Name == "AquaMai.Mods");
56 | if (modsAssembly == null)
57 | {
58 | throw new InvalidOperationException("AquaMai.Mods assembly not found");
59 | }
60 | return new(loadedConfigAssembly, modsAssembly);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/AquaMai.Config.HeadlessLoader/Polyfills.cs:
--------------------------------------------------------------------------------
1 | namespace System.Runtime.CompilerServices
2 | {
3 | internal static class IsExternalInit {}
4 | }
5 |
--------------------------------------------------------------------------------
/AquaMai.Config.HeadlessLoader/ResourceLoader.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.IO.Compression;
4 | using System.Linq;
5 | using Mono.Cecil;
6 |
7 | namespace AquaMai.Config.HeadlessLoader;
8 |
9 | public class ResourceLoader
10 | {
11 | private const string DLL_SUFFIX = ".dll";
12 | private const string COMPRESSED_SUFFIX = ".compressed";
13 | private const string DLL_COMPRESSED_SUFFIX = $"{DLL_SUFFIX}{COMPRESSED_SUFFIX}";
14 |
15 | public static Dictionary LoadEmbeddedAssemblies(AssemblyDefinition assembly)
16 | {
17 | return assembly.MainModule.Resources
18 | .Where(resource => resource.Name.ToLower().EndsWith(DLL_SUFFIX) || resource.Name.ToLower().EndsWith(DLL_COMPRESSED_SUFFIX))
19 | .Select(LoadResource)
20 | .Where(data => data.Name != null)
21 | .ToDictionary(data => data.Name, data => data.Stream);
22 | }
23 |
24 | public static (string Name, Stream Stream) LoadResource(Resource resource)
25 | {
26 | if (resource is EmbeddedResource embeddedResource)
27 | {
28 | if (resource.Name.ToLower().EndsWith(COMPRESSED_SUFFIX))
29 | {
30 | var decompressedStream = new MemoryStream();
31 | using (var deflateStream = new DeflateStream(embeddedResource.GetResourceStream(), CompressionMode.Decompress))
32 | {
33 | deflateStream.CopyTo(decompressedStream);
34 | }
35 | decompressedStream.Position = 0;
36 | return (resource.Name.Substring(0, resource.Name.Length - COMPRESSED_SUFFIX.Length), decompressedStream);
37 | }
38 | return (resource.Name, embeddedResource.GetResourceStream());
39 | }
40 | return (null, null);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/AquaMai.Config.Interfaces/AquaMai.Config.Interfaces.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Release
5 | AnyCPU
6 | {DF1536F9-3B06-4463-B654-4CC3E708B610}
7 | Library
8 | AquaMai.Config.Interfaces
9 | AquaMai.Config.Interfaces
10 | net472
11 | 512
12 | true
13 | 12
14 | 414
15 | $(ProjectDir)../Libs/;$(AssemblySearchPaths)
16 | $(ProjectDir)../Output/
17 | false
18 | false
19 |
20 |
21 |
22 | false
23 | None
24 | true
25 | prompt
26 | 4
27 | true
28 | false
29 |
30 |
31 |
32 | DEBUG
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/AquaMai.Config.Interfaces/IConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AquaMai.Config.Interfaces;
4 |
5 | public interface IConfig
6 | {
7 | public interface IEntryState
8 | {
9 | public bool IsDefault { get; }
10 | public object DefaultValue { get; }
11 | public object Value { get; set; }
12 | }
13 |
14 | public interface ISectionState
15 | {
16 | public bool IsDefault { get; set; }
17 | public bool DefaultEnabled { get; }
18 | public bool Enabled { get; set; }
19 | }
20 |
21 | public IReflectionManager ReflectionManager { get; }
22 |
23 | public ISectionState GetSectionState(IReflectionManager.ISection section);
24 | public ISectionState GetSectionState(Type type);
25 | public void SetSectionEnabled(IReflectionManager.ISection section, bool enabled);
26 | public IEntryState GetEntryState(IReflectionManager.IEntry entry);
27 | public void SetEntryValue(IReflectionManager.IEntry entry, object value);
28 | }
29 |
--------------------------------------------------------------------------------
/AquaMai.Config.Interfaces/IConfigComment.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.Config.Interfaces;
2 |
3 | public interface IConfigComment
4 | {
5 | string CommentEn { get; init; }
6 | string CommentZh { get; init; }
7 | public string GetLocalized(string lang);
8 | }
9 |
--------------------------------------------------------------------------------
/AquaMai.Config.Interfaces/IConfigEntryAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.Config.Interfaces;
2 |
3 | public interface IConfigEntryAttribute
4 | {
5 | IConfigComment Comment { get; }
6 | bool HideWhenDefault { get; }
7 | }
--------------------------------------------------------------------------------
/AquaMai.Config.Interfaces/IConfigMigrationManager.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.Config.Interfaces;
2 |
3 | public interface IConfigMigrationManager
4 | {
5 | public IConfigView Migrate(IConfigView config);
6 | public string GetVersion(IConfigView config);
7 | public string LatestVersion { get; }
8 | }
9 |
--------------------------------------------------------------------------------
/AquaMai.Config.Interfaces/IConfigParser.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.Config.Interfaces;
2 |
3 | public interface IConfigParser
4 | {
5 | public void Parse(IConfig config, string tomlString);
6 | public void Parse(IConfig config, IConfigView configView);
7 | }
8 |
--------------------------------------------------------------------------------
/AquaMai.Config.Interfaces/IConfigSectionAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.Config.Interfaces;
2 |
3 | public interface IConfigSectionAttribute
4 | {
5 | IConfigComment Comment { get; }
6 | bool ExampleHidden { get; }
7 | bool DefaultOn { get; }
8 | bool AlwaysEnabled { get; }
9 | }
--------------------------------------------------------------------------------
/AquaMai.Config.Interfaces/IConfigSerializer.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.Config.Interfaces;
2 |
3 | public interface IConfigSerializer
4 | {
5 | public record Options
6 | {
7 | public string Lang { get; init; }
8 | public bool IncludeBanner { get; init; }
9 | public bool OverrideLocaleValue { get; init; }
10 | }
11 |
12 | public string Serialize(IConfig config);
13 | }
14 |
--------------------------------------------------------------------------------
/AquaMai.Config.Interfaces/IConfigView.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.Config.Interfaces;
2 |
3 | public interface IConfigView
4 | {
5 | public void SetValue(string path, object value);
6 | public T GetValueOrDefault(string path, T defaultValue = default);
7 | public bool TryGetValue(string path, out T resultValue);
8 | public bool Remove(string path);
9 | public bool IsSectionEnabled(string path);
10 | public string ToToml();
11 | public IConfigView Clone();
12 | }
13 |
--------------------------------------------------------------------------------
/AquaMai.Config.Interfaces/IReflectionManager.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System;
3 |
4 | namespace AquaMai.Config.Interfaces;
5 |
6 | public interface IReflectionManager
7 | {
8 | public interface IEntry
9 | {
10 | public string Path { get; }
11 | public string Name { get; }
12 | public IReflectionField Field { get; }
13 | public IConfigEntryAttribute Attribute { get; init; }
14 | }
15 |
16 | public interface ISection
17 | {
18 | public string Path { get; }
19 | public IReflectionType Type { get; }
20 | public List Entries { get; }
21 | public IConfigSectionAttribute Attribute { get; init; }
22 | }
23 |
24 | public IEnumerable Sections { get; }
25 |
26 | public IEnumerable Entries { get; }
27 |
28 | public bool ContainsSection(string path);
29 |
30 | public bool TryGetSection(string path, out ISection section);
31 |
32 | public bool TryGetSection(Type type, out ISection section);
33 |
34 | public ISection GetSection(string path);
35 |
36 | public ISection GetSection(Type type);
37 |
38 | public bool ContainsEntry(string path);
39 |
40 | public bool TryGetEntry(string path, out IEntry entry);
41 |
42 | public IEntry GetEntry(string path);
43 | }
44 |
--------------------------------------------------------------------------------
/AquaMai.Config.Interfaces/IReflectionProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reflection;
4 |
5 | namespace AquaMai.Config.Interfaces;
6 |
7 | public interface IReflectionField
8 | {
9 | public string Name { get; }
10 | public Type FieldType { get; }
11 |
12 | public T GetCustomAttribute() where T : Attribute;
13 | public object GetValue(object objIsNull);
14 | public void SetValue(object objIsNull, object value);
15 | }
16 |
17 | public interface IReflectionType
18 | {
19 | public string FullName { get; }
20 | public string Namespace { get; }
21 |
22 | public T GetCustomAttribute() where T : Attribute;
23 | public IReflectionField[] GetFields(BindingFlags bindingAttr);
24 | }
25 |
26 | public interface IReflectionProvider
27 | {
28 | public IReflectionType[] GetTypes();
29 | public Dictionary GetEnum(string enumName);
30 | }
31 |
--------------------------------------------------------------------------------
/AquaMai.Config.Interfaces/Polyfills.cs:
--------------------------------------------------------------------------------
1 | namespace System.Runtime.CompilerServices
2 | {
3 | internal static class IsExternalInit {}
4 | }
5 |
--------------------------------------------------------------------------------
/AquaMai.Config/ApiVersion.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.Config;
2 |
3 | public static class ApiVersion
4 | {
5 | // Using a raw string for API version instead of a constant for maximum compatibility.
6 | // When breaking changes are made, increment the major version.
7 | // When new APIs are added in a backwards-compatible but non-forward-compatible manner, increment the minor version.
8 | public const string Version = "1.0";
9 | }
10 |
--------------------------------------------------------------------------------
/AquaMai.Config/AquaMai.Config.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Release
5 | AnyCPU
6 | {DF1536F9-3B06-4463-B654-4CC3E708B610}
7 | Library
8 | AquaMai.Config
9 | AquaMai.Config
10 | net472
11 | 512
12 | true
13 | 12
14 | 414
15 | $(ProjectDir)../Libs/;$(AssemblySearchPaths)
16 | $(ProjectDir)../Output/
17 | false
18 | false
19 |
20 |
21 |
22 | false
23 | None
24 | true
25 | prompt
26 | 4
27 | true
28 | false
29 |
30 |
31 |
32 | DEBUG
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | runtime; build; native; contentfiles; analyzers; buildtransitive
52 | all
53 |
54 |
55 | all
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/AquaMai.Config/Attributes/ConfigCollapseNamespaceAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AquaMai.Config.Attributes;
4 |
5 | // When The most inner namespace is the same name of the class, it should be collapsed.
6 | // The class must be the only class in the namespace with a [ConfigSection] attribute.
7 | [AttributeUsage(AttributeTargets.Class)]
8 | public class ConfigCollapseNamespaceAttribute : Attribute
9 | {}
10 |
--------------------------------------------------------------------------------
/AquaMai.Config/Attributes/ConfigComment.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AquaMai.Config.Interfaces;
3 |
4 | namespace AquaMai.Config.Attributes;
5 |
6 | public record ConfigComment(string CommentEn, string CommentZh) : IConfigComment
7 | {
8 | public string GetLocalized(string lang) => lang switch
9 | {
10 | "en" => CommentEn ?? "",
11 | "zh" => CommentZh ?? "",
12 | _ => throw new ArgumentException($"Unsupported language: {lang}")
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/AquaMai.Config/Attributes/ConfigEntryAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AquaMai.Config.Interfaces;
3 |
4 | namespace AquaMai.Config.Attributes;
5 |
6 | public enum SpecialConfigEntry
7 | {
8 | None,
9 | Locale
10 | }
11 |
12 | [AttributeUsage(AttributeTargets.Field)]
13 | public class ConfigEntryAttribute(
14 | string en = null,
15 | string zh = null,
16 | // NOTE: Don't use this argument to hide any useful options.
17 | // Only use it to hide options that really won't be used.
18 | bool hideWhenDefault = false,
19 | // NOTE: Use this argument to mark special config entries that need special handling.
20 | SpecialConfigEntry specialConfigEntry = SpecialConfigEntry.None) : Attribute, IConfigEntryAttribute
21 | {
22 | public IConfigComment Comment { get; } = new ConfigComment(en, zh);
23 | public bool HideWhenDefault { get; } = hideWhenDefault;
24 | public SpecialConfigEntry SpecialConfigEntry { get; } = specialConfigEntry;
25 | }
26 |
--------------------------------------------------------------------------------
/AquaMai.Config/Attributes/ConfigSectionAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AquaMai.Config.Interfaces;
3 |
4 | namespace AquaMai.Config.Attributes;
5 |
6 | [AttributeUsage(AttributeTargets.Class)]
7 | public class ConfigSectionAttribute(
8 | string en = null,
9 | string zh = null,
10 | // It will be hidden if the default value is preserved.
11 | bool exampleHidden = false,
12 | // A "Disabled = true" entry is required to disable the section.
13 | bool defaultOn = false,
14 | // NOTE: You probably shouldn't use this. Only the "General" section is using this.
15 | // Implies defaultOn = true.
16 | bool alwaysEnabled = false) : Attribute, IConfigSectionAttribute
17 | {
18 | public IConfigComment Comment { get; } = new ConfigComment(en, zh);
19 | public bool ExampleHidden { get; } = exampleHidden;
20 | public bool DefaultOn { get; } = defaultOn || alwaysEnabled;
21 | public bool AlwaysEnabled { get; } = alwaysEnabled;
22 | }
23 |
--------------------------------------------------------------------------------
/AquaMai.Config/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | tomlet
5 | $AquaMai.Config$_
6 |
7 |
8 |
--------------------------------------------------------------------------------
/AquaMai.Config/Migration/ConfigMigrationManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using AquaMai.Config.Interfaces;
5 |
6 | namespace AquaMai.Config.Migration;
7 |
8 | public class ConfigMigrationManager : IConfigMigrationManager
9 | {
10 | public static readonly ConfigMigrationManager Instance = new();
11 |
12 | private readonly Dictionary migrationMap =
13 | new List
14 | {
15 | new ConfigMigration_V1_0_V2_0(),
16 | new ConfigMigration_V2_0_V2_1(),
17 | new ConfigMigration_V2_1_V2_2(),
18 | }.ToDictionary(m => m.FromVersion);
19 |
20 | public string LatestVersion { get; }
21 |
22 | private ConfigMigrationManager()
23 | {
24 | LatestVersion = migrationMap.Values
25 | .Select(m => m.ToVersion)
26 | .OrderByDescending(version =>
27 | {
28 | var versionParts = version.Split('.').Select(int.Parse).ToArray();
29 | return versionParts[0] * 100000 + versionParts[1];
30 | })
31 | .First();
32 | }
33 |
34 | public IConfigView Migrate(IConfigView config)
35 | {
36 | var currentVersion = GetVersion(config);
37 | while (migrationMap.ContainsKey(currentVersion))
38 | {
39 | var migration = migrationMap[currentVersion];
40 | Utility.Log($"Migrating config from v{migration.FromVersion} to v{migration.ToVersion}");
41 | config = migration.Migrate((ConfigView)config);
42 | currentVersion = migration.ToVersion;
43 | }
44 |
45 | if (currentVersion != LatestVersion)
46 | {
47 | throw new ArgumentException($"Could not migrate the config from v{currentVersion} to v{LatestVersion}");
48 | }
49 |
50 | return config;
51 | }
52 |
53 | public string GetVersion(IConfigView config)
54 | {
55 | if (config.TryGetValue("Version", out var version))
56 | {
57 | return version;
58 | }
59 |
60 | // Assume v1.0 if not found
61 | return "1.0";
62 | }
63 | }
--------------------------------------------------------------------------------
/AquaMai.Config/Migration/ConfigMigration_V2_0_V2_1.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Interfaces;
2 | using Tomlet.Models;
3 |
4 | namespace AquaMai.Config.Migration;
5 |
6 | public class ConfigMigration_V2_0_V2_1 : IConfigMigration
7 | {
8 | public string FromVersion => "2.0";
9 | public string ToVersion => "2.1";
10 |
11 | public ConfigView Migrate(ConfigView src)
12 | {
13 | var dst = (ConfigView)src.Clone();
14 | dst.SetValue("Version", ToVersion);
15 |
16 | if (src.IsSectionEnabled("Tweaks.ResetTouchAfterTrack"))
17 | {
18 | dst.Remove("Tweaks.ResetTouchAfterTrack");
19 | dst.SetValue("Tweaks.ResetTouch.AfterTrack", true);
20 | }
21 |
22 | return dst;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/AquaMai.Config/Migration/ConfigMigration_V2_1_V2_2.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Interfaces;
2 | using Tomlet.Models;
3 |
4 | namespace AquaMai.Config.Migration;
5 |
6 | public class ConfigMigration_V2_1_V2_2 : IConfigMigration
7 | {
8 | public string FromVersion => "2.1";
9 | public string ToVersion => "2.2";
10 |
11 | public ConfigView Migrate(ConfigView src)
12 | {
13 | var dst = (ConfigView)src.Clone();
14 | dst.SetValue("Version", ToVersion);
15 |
16 | if (src.IsSectionEnabled("GameSystem.Assets.UseJacketAsDummyMovie"))
17 | {
18 | dst.Remove("GameSystem.Assets.UseJacketAsDummyMovie");
19 | dst.SetValue("GameSystem.Assets.MovieLoader.JacketAsMovie", true);
20 | }
21 |
22 | if (src.TryGetValue("GameSystem.Assets.LoadLocalImages.LocalAssetsDir", out var localAssetsDir))
23 | {
24 | dst.SetValue("GameSystem.Assets.LoadLocalImages.ImageAssetsDir", localAssetsDir);
25 | dst.Remove("GameSystem.Assets.LoadLocalImages.LocalAssetsDir");
26 | }
27 |
28 | return dst;
29 | }
30 | }
--------------------------------------------------------------------------------
/AquaMai.Config/Migration/IConfigMigration.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Interfaces;
2 |
3 | namespace AquaMai.Config.Migration;
4 |
5 | public interface IConfigMigration
6 | {
7 | public string FromVersion { get; }
8 | public string ToVersion { get; }
9 | public ConfigView Migrate(ConfigView config);
10 | }
11 |
--------------------------------------------------------------------------------
/AquaMai.Config/Polyfills.cs:
--------------------------------------------------------------------------------
1 | namespace System.Runtime.CompilerServices
2 | {
3 | internal static class IsExternalInit {}
4 | }
5 |
--------------------------------------------------------------------------------
/AquaMai.Config/Reflection/SystemReflectionProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reflection;
4 | using AquaMai.Config.Interfaces;
5 |
6 | namespace AquaMai.Config.Reflection;
7 |
8 | public class SystemReflectionProvider(Assembly assembly) : IReflectionProvider
9 | {
10 | public class ReflectionField(FieldInfo field) : IReflectionField
11 | {
12 | public FieldInfo UnderlyingField { get; } = field;
13 |
14 | public string Name => UnderlyingField.Name;
15 | public Type FieldType => UnderlyingField.FieldType;
16 | public T GetCustomAttribute() where T : Attribute => UnderlyingField.GetCustomAttribute();
17 | public object GetValue(object obj) => UnderlyingField.GetValue(obj);
18 | public void SetValue(object obj, object value) => UnderlyingField.SetValue(obj, value);
19 | }
20 |
21 | public class ReflectionType(Type type) : IReflectionType
22 | {
23 | public Type UnderlyingType { get; } = type;
24 |
25 | public string FullName => UnderlyingType.FullName;
26 | public string Namespace => UnderlyingType.Namespace;
27 | public T GetCustomAttribute() where T : Attribute => UnderlyingType.GetCustomAttribute();
28 | public IReflectionField[] GetFields(BindingFlags bindingAttr) => Array.ConvertAll(UnderlyingType.GetFields(bindingAttr), f => new ReflectionField(f));
29 | }
30 |
31 | public Assembly UnderlyingAssembly { get; } = assembly;
32 |
33 | public IReflectionType[] GetTypes() => Array.ConvertAll(UnderlyingAssembly.GetTypes(), t => new ReflectionType(t));
34 |
35 | public Dictionary GetEnum(string enumName)
36 | {
37 | var enumType = UnderlyingAssembly.GetType(enumName);
38 | if (enumType == null) return null;
39 | var enumValues = Enum.GetValues(enumType);
40 | var enumDict = new Dictionary();
41 | foreach (var enumValue in enumValues)
42 | {
43 | enumDict.Add(enumValue.ToString(), enumValue);
44 | }
45 | return enumDict;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/AquaMai.Config/Types/AdxKeyMap.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.Config.Types;
2 |
3 | public enum AdxKeyMap
4 | {
5 | None = 0,
6 | Select1P,
7 | Select2P,
8 | Service,
9 | Test,
10 | }
--------------------------------------------------------------------------------
/AquaMai.Config/Types/KeyCodeID.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.Config.Types;
2 |
3 | public enum KeyCodeID
4 | {
5 | None,
6 | Backspace,
7 | Tab,
8 | Clear,
9 | Return,
10 | Pause,
11 | Escape,
12 | Space,
13 | Exclaim,
14 | DoubleQuote,
15 | Hash,
16 | Dollar,
17 | Ampersand,
18 | Quote,
19 | LeftParen,
20 | RightParen,
21 | Asterisk,
22 | Plus,
23 | Comma,
24 | Minus,
25 | Period,
26 | Slash,
27 | Alpha0,
28 | Alpha1,
29 | Alpha2,
30 | Alpha3,
31 | Alpha4,
32 | Alpha5,
33 | Alpha6,
34 | Alpha7,
35 | Alpha8,
36 | Alpha9,
37 | Colon,
38 | Semicolon,
39 | Less,
40 | Equals,
41 | Greater,
42 | Question,
43 | At,
44 | LeftBracket,
45 | Backslash,
46 | RightBracket,
47 | Caret,
48 | Underscore,
49 | BackQuote,
50 | A,
51 | B,
52 | C,
53 | D,
54 | E,
55 | F,
56 | G,
57 | H,
58 | I,
59 | J,
60 | K,
61 | L,
62 | M,
63 | N,
64 | O,
65 | P,
66 | Q,
67 | R,
68 | S,
69 | T,
70 | U,
71 | V,
72 | W,
73 | X,
74 | Y,
75 | Z,
76 | Delete,
77 | Keypad0,
78 | Keypad1,
79 | Keypad2,
80 | Keypad3,
81 | Keypad4,
82 | Keypad5,
83 | Keypad6,
84 | Keypad7,
85 | Keypad8,
86 | Keypad9,
87 | KeypadPeriod,
88 | KeypadDivide,
89 | KeypadMultiply,
90 | KeypadMinus,
91 | KeypadPlus,
92 | KeypadEnter,
93 | KeypadEquals,
94 | UpArrow,
95 | DownArrow,
96 | RightArrow,
97 | LeftArrow,
98 | Insert,
99 | Home,
100 | End,
101 | PageUp,
102 | PageDown,
103 | F1,
104 | F2,
105 | F3,
106 | F4,
107 | F5,
108 | F6,
109 | F7,
110 | F8,
111 | F9,
112 | F10,
113 | F11,
114 | F12,
115 | F13,
116 | F14,
117 | F15,
118 | Numlock,
119 | CapsLock,
120 | ScrollLock,
121 | RightShift,
122 | LeftShift,
123 | RightControl,
124 | LeftControl,
125 | RightAlt,
126 | LeftAlt,
127 | RightCommand,
128 | RightApple,
129 | LeftCommand,
130 | LeftApple,
131 | LeftWindows,
132 | RightWindows,
133 | AltGr,
134 | Help,
135 | Print,
136 | SysReq,
137 | Break,
138 | Menu,
139 | Mouse0,
140 | Mouse1,
141 | Mouse2,
142 | Mouse3,
143 | Mouse4,
144 | Mouse5,
145 | Mouse6,
146 | }
147 |
--------------------------------------------------------------------------------
/AquaMai.Config/Types/KeyCodeOrName.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.Config.Types;
2 |
3 | public enum KeyCodeOrName
4 | {
5 | None,
6 | Alpha0,
7 | Alpha1,
8 | Alpha2,
9 | Alpha3,
10 | Alpha4,
11 | Alpha5,
12 | Alpha6,
13 | Alpha7,
14 | Alpha8,
15 | Alpha9,
16 | Keypad0,
17 | Keypad1,
18 | Keypad2,
19 | Keypad3,
20 | Keypad4,
21 | Keypad5,
22 | Keypad6,
23 | Keypad7,
24 | Keypad8,
25 | Keypad9,
26 | F1,
27 | F2,
28 | F3,
29 | F4,
30 | F5,
31 | F6,
32 | F7,
33 | F8,
34 | F9,
35 | F10,
36 | F11,
37 | F12,
38 | Insert,
39 | Delete,
40 | Home,
41 | End,
42 | PageUp,
43 | PageDown,
44 | UpArrow,
45 | DownArrow,
46 | LeftArrow,
47 | RightArrow,
48 |
49 | Select1P,
50 | Select2P,
51 | Service,
52 | Test,
53 | }
54 |
--------------------------------------------------------------------------------
/AquaMai.Core/Attributes/EnableGameVersionAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AquaMai.Core.Attributes;
4 |
5 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
6 | public class EnableGameVersionAttribute(uint minVersion = 0, uint maxVersion = 0, bool noWarn = false) : Attribute
7 | {
8 | public uint MinVersion { get; } = minVersion;
9 | public uint MaxVersion { get; } = maxVersion;
10 | public bool NoWarn { get; } = noWarn;
11 |
12 | public bool ShouldEnable(uint gameVersion)
13 | {
14 | if (MinVersion > 0 && MinVersion > gameVersion) return false;
15 | if (MaxVersion > 0 && MaxVersion < gameVersion) return false;
16 | return true;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/AquaMai.Core/Attributes/EnableImplicitlyIfAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AquaMai.Core.Attributes;
4 |
5 | // If the field or property with this name is true, the patch will be implicitly enabled, regardless of the config state.
6 | // This is handled outside the config module, while The config state won't be actually set to enabled by it.
7 | // Won't bypass the restriction of [EnableIf()] and [EnableGameVersion()].
8 | [AttributeUsage(AttributeTargets.Class)]
9 | public class EnableImplicitlyIf(string memberName) : Attribute
10 | {
11 | public string MemberName { get; } = memberName;
12 | }
13 |
--------------------------------------------------------------------------------
/AquaMai.Core/BuildInfo.cs:
--------------------------------------------------------------------------------
1 | using MelonLoader;
2 |
3 | namespace AquaMai.Core;
4 |
5 | public static class BuildInfo
6 | {
7 | // The fields are set in `AquaMai/Main.cs` with reflection.
8 | // The values are from `AquaMai/BuildInfo.cs`.
9 |
10 | public static string Name;
11 | public static string Description;
12 | public static string Author;
13 | public static string Company;
14 | public static string Version;
15 | public static string GitVersion;
16 | public static string BuildDate;
17 | public static string DownloadLink;
18 |
19 | public static MelonAssembly ModAssembly;
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/AquaMai.Core/ConfigLoader.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Reflection;
4 | using MelonLoader;
5 | using AquaMai.Config;
6 | using AquaMai.Config.Interfaces;
7 | using AquaMai.Config.Migration;
8 |
9 | namespace AquaMai.Core;
10 |
11 | public static class ConfigLoader
12 | {
13 | private static string ConfigFile => "AquaMai.toml";
14 | private static string ConfigExampleFile(string lang) => $"AquaMai.{lang}.toml";
15 | private static string OldConfigFile(string version) => $"AquaMai.toml.old-v{version}.";
16 |
17 | private static Config.Config config;
18 |
19 | public static Config.Config Config => config;
20 |
21 | public static bool LoadConfig(Assembly modsAssembly)
22 | {
23 | Utility.LogFunction = MelonLogger.Msg;
24 |
25 | config = new(
26 | new Config.Reflection.ReflectionManager(
27 | new Config.Reflection.SystemReflectionProvider(modsAssembly)));
28 |
29 | if (!File.Exists(ConfigFile))
30 | {
31 | var examples = GenerateExamples();
32 | foreach (var (lang, example) in examples)
33 | {
34 | var filename = ConfigExampleFile(lang);
35 | File.WriteAllText(filename, example);
36 | }
37 | MelonLogger.Error("======================================!!!");
38 | MelonLogger.Error("AquaMai.toml not found! Please create it.");
39 | MelonLogger.Error("找不到配置文件 AquaMai.toml!请创建。");
40 | MelonLogger.Error("Example copied to AquaMai.en.toml");
41 | MelonLogger.Error("示例已复制到 AquaMai.zh.toml");
42 | MelonLogger.Error("=========================================");
43 | return false;
44 | }
45 |
46 | var configText = File.ReadAllText(ConfigFile);
47 | var configView = new ConfigView(configText);
48 | var configVersion = ConfigMigrationManager.Instance.GetVersion(configView);
49 | if (configVersion != ConfigMigrationManager.Instance.LatestVersion)
50 | {
51 | File.WriteAllText(OldConfigFile(configVersion), configText);
52 | configView = (ConfigView)ConfigMigrationManager.Instance.Migrate(configView);
53 | }
54 |
55 | // Read AquaMai.toml to load settings
56 | ConfigParser.Instance.Parse(config, configView);
57 |
58 | return true;
59 | }
60 |
61 | public static void SaveConfig(string lang)
62 | {
63 | File.WriteAllText(ConfigFile, SerailizeCurrentConfig(lang));
64 | }
65 |
66 | private static string SerailizeCurrentConfig(string lang) =>
67 | new ConfigSerializer(new IConfigSerializer.Options()
68 | {
69 | Lang = lang,
70 | IncludeBanner = true,
71 | OverrideLocaleValue = true
72 | }).Serialize(config);
73 |
74 | private static IDictionary GenerateExamples()
75 | {
76 | var examples = new Dictionary();
77 | foreach (var lang in (string[]) ["en", "zh"])
78 | {
79 | examples[lang] = SerailizeCurrentConfig(lang);
80 | }
81 | return examples;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/AquaMai.Core/Helpers/EnableConditionHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Reflection;
4 | using AquaMai.Core.Attributes;
5 | using AquaMai.Core.Resources;
6 | using HarmonyLib;
7 | using MelonLoader;
8 |
9 | namespace AquaMai.Core.Helpers;
10 |
11 | public class EnableConditionHelper
12 | {
13 | [HarmonyPostfix]
14 | [HarmonyPatch("HarmonyLib.PatchTools", "GetPatchMethod")]
15 | public static void PostGetPatchMethod(ref MethodInfo __result)
16 | {
17 | if (__result != null)
18 | {
19 | if (ShouldSkipMethodOrClass(__result.GetCustomAttribute, __result.ReflectedType, __result.Name))
20 | {
21 | __result = null;
22 | }
23 | }
24 | }
25 |
26 | [HarmonyPostfix]
27 | [HarmonyPatch("HarmonyLib.PatchTools", "GetPatchMethods")]
28 | public static void PostGetPatchMethods(ref IList __result)
29 | {
30 | for (int i = 0; i < __result.Count; i++)
31 | {
32 | var harmonyMethod = Traverse.Create(__result[i]).Field("info").GetValue() as HarmonyMethod;
33 | var method = harmonyMethod.method;
34 | if (ShouldSkipMethodOrClass(method.GetCustomAttribute, method.ReflectedType, method.Name))
35 | {
36 | __result.RemoveAt(i);
37 | i--;
38 | }
39 | }
40 | }
41 |
42 | public static bool ShouldSkipClass(Type type)
43 | {
44 | return ShouldSkipMethodOrClass(type.GetCustomAttribute, type);
45 | }
46 |
47 | private static bool ShouldSkipMethodOrClass(Func getCustomAttribute, Type type, string methodName = "")
48 | {
49 | var displayName = type.FullName + (string.IsNullOrEmpty(methodName) ? "" : $".{methodName}");
50 | var enableIf = (EnableIfAttribute)getCustomAttribute(typeof(EnableIfAttribute));
51 | if (enableIf != null && !enableIf.ShouldEnable(type))
52 | {
53 | # if DEBUG
54 | MelonLogger.Msg($"Skipping {displayName} due to EnableIf condition");
55 | # endif
56 | return true;
57 | }
58 | var enableGameVersion = (EnableGameVersionAttribute)getCustomAttribute(typeof(EnableGameVersionAttribute));
59 | if (enableGameVersion != null && !enableGameVersion.ShouldEnable(GameInfo.GameVersion))
60 | {
61 | # if DEBUG
62 | MelonLogger.Msg($"Skipping {displayName} due to EnableGameVersion condition");
63 | # endif
64 | if (!enableGameVersion.NoWarn)
65 | {
66 | MelonLogger.Warning(string.Format(Locale.SkipIncompatiblePatch, type));
67 | }
68 | return true;
69 | }
70 | return false;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/AquaMai.Core/Helpers/FileSystem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace AquaMai.Core.Helpers;
5 |
6 | public static class FileSystem
7 | {
8 | public static string ResolvePath(string path)
9 | {
10 | var varExpanded = Environment.ExpandEnvironmentVariables(path);
11 | return Path.IsPathRooted(varExpanded)
12 | ? varExpanded
13 | : Path.Combine(Environment.CurrentDirectory, varExpanded);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/AquaMai.Core/Helpers/GameInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using MAI2System;
3 |
4 | namespace AquaMai.Core.Helpers;
5 |
6 | public class GameInfo
7 | {
8 | public static uint GameVersion { get; } = GetGameVersion();
9 |
10 | private static uint GetGameVersion()
11 | {
12 | return (uint)typeof(ConstParameter).GetField("NowGameVersion", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy).GetValue(null);
13 | }
14 |
15 | public static string GameId { get; } = GetGameId();
16 |
17 | private static string GetGameId()
18 | {
19 | return typeof(ConstParameter).GetField("GameIDStr", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy).GetValue(null) as string;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/AquaMai.Core/Helpers/GuiSizes.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Reflection;
4 | using HarmonyLib;
5 | using UnityEngine;
6 |
7 | namespace AquaMai.Core.Helpers;
8 |
9 | public static class GuiSizes
10 | {
11 | public static bool SinglePlayer { get; set; } = false;
12 | public static float PlayerWidth => Screen.height / 1920f * 1080;
13 | public static float PlayerCenter => SinglePlayer ? Screen.width / 2f : Screen.width / 2f - PlayerWidth / 2;
14 | public static int FontSize => (int)(PlayerWidth * .015f);
15 | public static float LabelHeight => FontSize * 1.5f;
16 | public static float Margin => PlayerWidth * .005f;
17 |
18 | private static Color backgroundColor = new(147 / 256f, 160 / 256f, 173 / 256f, .8f);
19 |
20 | public static void SetupStyles()
21 | {
22 | var buttonStyle = GUI.skin.button;
23 | buttonStyle.normal.textColor = Color.white;
24 | buttonStyle.normal.background = Texture2D.whiteTexture;
25 | buttonStyle.hover.background = Texture2D.whiteTexture;
26 | buttonStyle.active.background = Texture2D.whiteTexture;
27 | buttonStyle.border = new RectOffset(0, 0, 0, 0);
28 | buttonStyle.margin = new RectOffset(0, 0, 0, 0);
29 | buttonStyle.padding = new RectOffset(10, 10, 10, 10);
30 | buttonStyle.overflow = new RectOffset(0, 0, 0, 0);
31 |
32 | var boxStyle = GUI.skin.box;
33 | boxStyle.border = new RectOffset(0, 0, 0, 0);
34 | boxStyle.normal.background = Texture2D.whiteTexture;
35 |
36 | GUI.backgroundColor = backgroundColor;
37 | }
38 |
39 | [HarmonyPatch]
40 | public class BoxBackground
41 | {
42 | public static IEnumerable TargetMethods()
43 | {
44 | return typeof(GUI).GetMethods().Where(x => x.Name == "Box");
45 | }
46 |
47 | public static void Prefix()
48 | {
49 | GUI.backgroundColor = new Color(62 / 256f, 62 / 256f, 66 / 256f, .6f);
50 | }
51 |
52 | public static void Postfix()
53 | {
54 | GUI.backgroundColor = backgroundColor;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/AquaMai.Core/Helpers/JsonHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Globalization;
3 | using System.Linq;
4 | using MelonLoader.TinyJSON;
5 |
6 | namespace AquaMai.Core.Helpers;
7 |
8 | public static class JsonHelper
9 | {
10 | public static bool TryToInt32(Variant variant, out int result)
11 | {
12 | if (variant is ProxyNumber proxyNumber)
13 | {
14 | try
15 | {
16 | result = proxyNumber.ToInt32(CultureInfo.InvariantCulture);
17 | return true;
18 | }
19 | catch
20 | {}
21 | }
22 | else if (variant is ProxyString proxyString)
23 | {
24 | return int.TryParse(proxyString.ToString(), out result);
25 | }
26 | result = 0;
27 | return false;
28 | }
29 |
30 | public static bool TryToInt64(Variant variant, out long result)
31 | {
32 | if (variant is ProxyNumber proxyNumber)
33 | {
34 | try
35 | {
36 | result = proxyNumber.ToInt64(CultureInfo.InvariantCulture);
37 | return true;
38 | }
39 | catch
40 | {}
41 | }
42 | else if (variant is ProxyString proxyString)
43 | {
44 | return long.TryParse(proxyString.ToString(), out result);
45 | }
46 | result = 0;
47 | return false;
48 | }
49 |
50 | public class DeepEqualityComparer : IEqualityComparer
51 | {
52 | public bool Equals(Variant a, Variant b) => DeepEqual(a, b);
53 | public int GetHashCode(Variant a) => a.ToJSON().GetHashCode();
54 | }
55 |
56 |
57 | public static bool DeepEqual(Variant a, Variant b) =>
58 | (a, b) switch {
59 | (ProxyArray arrayA, ProxyArray arrayB) => Enumerable.SequenceEqual(arrayA, arrayB, new DeepEqualityComparer()),
60 | (ProxyObject objectA, ProxyObject objectB) =>
61 | objectA.Keys.Count == objectB.Keys.Count &&
62 | objectA.All(pair => objectB.TryGetValue(pair.Key, out var valueB) && DeepEqual(pair.Value, valueB)),
63 | (ProxyBoolean booleanA, ProxyBoolean booleanB) => booleanA.ToBoolean(null) == booleanB.ToBoolean(null),
64 | (ProxyNumber numberA, ProxyNumber numberB) => numberA.ToString() == numberB.ToString(),
65 | (ProxyString stringA, ProxyString stringB) => stringA.ToString() == stringB.ToString(),
66 | (null, null) => true,
67 | _ => false
68 | };
69 | }
70 |
--------------------------------------------------------------------------------
/AquaMai.Core/Helpers/MessageHelper.cs:
--------------------------------------------------------------------------------
1 | using DB;
2 | using HarmonyLib;
3 | using Manager;
4 | using MelonLoader;
5 | using Process;
6 | using UnityEngine;
7 |
8 | namespace AquaMai.Core.Helpers;
9 |
10 | public class MessageHelper
11 | {
12 | private static IGenericManager _genericManager = null;
13 |
14 | [HarmonyPostfix]
15 | [HarmonyPatch(typeof(ProcessManager), "SetMessageManager")]
16 | private static void OnSetMessageManager(IGenericManager genericManager)
17 | {
18 | _genericManager = genericManager;
19 | }
20 |
21 | public static void ShowMessage(string message, WindowSizeID size = WindowSizeID.Middle, string title = null, Sprite sprite = null, bool retain = false)
22 | {
23 | if (_genericManager is null)
24 | {
25 | MelonLogger.Error($"[MessageHelper] Unable to show message: `{message}` GenericManager is null");
26 | return;
27 | }
28 |
29 | _genericManager.Enqueue(0, retain ? WindowMessageID.CollectionCategorySelectAnnounce : WindowMessageID.CollectionAttentionEmptyFavorite, new WindowParam()
30 | {
31 | hideTitle = title is null,
32 | replaceTitle = true,
33 | title = title,
34 | replaceText = true,
35 | text = message,
36 | changeSize = true,
37 | sizeID = size,
38 | directSprite = sprite is not null,
39 | sprite = sprite,
40 | });
41 | }
42 | }
--------------------------------------------------------------------------------
/AquaMai.Core/Helpers/MusicDirHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using HarmonyLib;
3 |
4 | namespace AquaMai.Core.Helpers;
5 |
6 | public class MusicDirHelper
7 | {
8 | private static Dictionary _map = new();
9 |
10 | [HarmonyPostfix]
11 | [HarmonyPatch(typeof(Manager.MaiStudio.Serialize.MusicData), "AddPath")]
12 | private static void AddPath(Manager.MaiStudio.Serialize.MusicData __instance, string parentPath)
13 | {
14 | _map[__instance.GetID()] = parentPath;
15 | }
16 |
17 | public static string LookupPath(int id)
18 | {
19 | return _map.GetValueOrDefault(id);
20 | }
21 |
22 | public static string LookupPath(Manager.MaiStudio.Serialize.MusicData musicData)
23 | {
24 | return LookupPath(musicData.GetID());
25 | }
26 |
27 | public static string LookupPath(Manager.MaiStudio.MusicData musicData)
28 | {
29 | return LookupPath(musicData.GetID());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/AquaMai.Core/Helpers/NetPacketHook.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using Net;
4 | using Net.Packet;
5 | using MelonLoader;
6 | using MelonLoader.TinyJSON;
7 | using HarmonyLib;
8 | using System.IO;
9 |
10 | namespace AquaMai.Core.Helpers;
11 |
12 | public class NetPacketHook
13 | {
14 | // Returns true if the packet was modified
15 | public delegate Variant NetPacketCompleteHook(string api, Variant request, Variant response);
16 |
17 | public static event NetPacketCompleteHook OnNetPacketComplete;
18 |
19 | [HarmonyPrefix]
20 | [HarmonyPatch(typeof(Packet), "ProcImpl")]
21 | public static void PreProcImpl(Packet __instance)
22 | {
23 | try
24 | {
25 | if (
26 | __instance.State == PacketState.Process &&
27 | Traverse.Create(__instance).Field("Client").GetValue() is NetHttpClient client &&
28 | client.State == NetHttpClient.StateDone)
29 | {
30 | var netQuery = __instance.Query;
31 | var api = Shim.RemoveApiSuffix(netQuery.Api);
32 | var responseBytes = client.GetResponse().ToArray();
33 | var decryptedResponse = Shim.NetHttpClientDecryptsResponse ? responseBytes : Shim.DecryptNetPacketBody(responseBytes);
34 | var decodedResponse = Encoding.UTF8.GetString(decryptedResponse);
35 | var responseJson = JSON.Load(decodedResponse);
36 | var requestJson = JSON.Load(netQuery.GetRequest());
37 | var modified = false;
38 | foreach (var handler in OnNetPacketComplete?.GetInvocationList())
39 | {
40 | try
41 | {
42 | if (handler.DynamicInvoke(api, requestJson, responseJson) is Variant result)
43 | {
44 | responseJson = result;
45 | modified = true;
46 | }
47 | }
48 | catch (Exception e)
49 | {
50 | MelonLogger.Error($"[NetPacketExtension] Error in handler: {e}");
51 | }
52 | }
53 | if (
54 | modified &&
55 | Traverse.Create(client).Field("_memoryStream").GetValue() is MemoryStream memoryStream &&
56 | !JsonHelper.DeepEqual(responseJson, JSON.Load(decodedResponse)))
57 | {
58 | var modifiedResponse = Encoding.UTF8.GetBytes(responseJson.ToJSON());
59 | if (!Shim.NetHttpClientDecryptsResponse)
60 | {
61 | modifiedResponse = Shim.EncryptNetPacketBody(modifiedResponse);
62 | }
63 | memoryStream.SetLength(0);
64 | memoryStream.Write(modifiedResponse, 0, modifiedResponse.Length);
65 | memoryStream.Seek(0, SeekOrigin.Begin);
66 | MelonLogger.Msg($"[NetPacketExtension] Modified response for {api} ({decryptedResponse.Length} bytes -> {modifiedResponse.Length} bytes)");
67 | }
68 | }
69 | }
70 | catch (Exception e)
71 | {
72 | MelonLogger.Error($"[NetPacketExtension] Failed to process NetPacket: {e}");
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/AquaMai.Core/Helpers/SharedInstances.cs:
--------------------------------------------------------------------------------
1 | using HarmonyLib;
2 | using Main;
3 | using Process;
4 |
5 | namespace AquaMai.Core.Helpers;
6 |
7 | public class SharedInstances
8 | {
9 | public static ProcessDataContainer ProcessDataContainer { get; private set; }
10 | public static GameMainObject GameMainObject { get; private set; }
11 |
12 | [HarmonyPrefix]
13 | [HarmonyPatch(typeof(ProcessDataContainer), MethodType.Constructor)]
14 | public static void OnCreateProcessDataContainer(ProcessDataContainer __instance)
15 | {
16 | ProcessDataContainer = __instance;
17 | }
18 |
19 | [HarmonyPrefix]
20 | [HarmonyPatch(typeof(GameMainObject), "Awake")]
21 | public static void OnCreateGameMainObject(GameMainObject __instance)
22 | {
23 | GameMainObject = __instance;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/AquaMai.Core/Resources/I18nSingleAssemblyHook.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.Resources;
3 | using HarmonyLib;
4 |
5 | namespace AquaMai.Core.Resources;
6 |
7 | public class I18nSingleAssemblyHook
8 | {
9 | [HarmonyPatch(typeof(ResourceManager), "InternalGetResourceSet", typeof(CultureInfo), typeof(bool), typeof(bool))]
10 | [HarmonyPrefix]
11 | public static bool GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents, ref ResourceSet __result, ResourceManager __instance)
12 | {
13 | var GetResourceFileName = __instance.GetType().GetMethod("GetResourceFileName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
14 | var resourceFileName = (string)GetResourceFileName.Invoke(__instance, [culture]);
15 | var ResourcesAssembly = typeof(I18nSingleAssemblyHook).Assembly;
16 | var manifestResourceStream = ResourcesAssembly.GetManifestResourceStream(resourceFileName);
17 | if (manifestResourceStream == null)
18 | {
19 | return true;
20 | }
21 |
22 | var resourceGroveler = __instance.GetType().GetField("resourceGroveler", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(__instance);
23 | var CreateResourceSet = resourceGroveler.GetType().GetMethod("CreateResourceSet", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
24 | var resourceSet = CreateResourceSet.Invoke(resourceGroveler, [manifestResourceStream, ResourcesAssembly]);
25 | var AddResourceSet = __instance.GetType().GetMethod("AddResourceSet", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
26 | var localResourceSets = __instance.GetType().GetField("_resourceSets", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(__instance);
27 | object[] args = [localResourceSets, culture.Name, resourceSet];
28 | AddResourceSet.Invoke(null, args);
29 | __result = (ResourceSet)args[2];
30 | return false;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/AquaMai.ErrorReport/AquaMai.ErrorReport.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net472
6 | enable
7 | true
8 | enable
9 | 512
10 | true
11 | 12
12 | $(ProjectDir)../Libs/;$(AssemblySearchPaths)
13 | $(ProjectDir)../Output/
14 | false
15 | false
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | all
25 | runtime; build; native; contentfiles; analyzers; buildtransitive
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/AquaMai.ErrorReport/ChecksumCalculator.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.ErrorReport;
2 |
3 | public class ChecksumCalculator
4 | {
5 | private static readonly uint[] CrcTable = new uint[256];
6 |
7 | static ChecksumCalculator()
8 | {
9 | for (uint i = 0; i < 256; i++)
10 | {
11 | uint crc = i;
12 | for (int j = 0; j < 8; j++)
13 | {
14 | crc = (crc & 1) != 0 ? (0xEDB88320 ^ (crc >> 1)) : (crc >> 1);
15 | }
16 | CrcTable[i] = crc;
17 | }
18 | }
19 |
20 | public static uint GetChecksum(byte[] data, uint k)
21 | {
22 | uint crc = 0xFFFFFFFF; // Initial value
23 | foreach (byte b in data)
24 | {
25 | crc = (crc >> 8) ^ CrcTable[(crc ^ b) & 0xFF];
26 | }
27 | return (crc ^ k ^ 0xFFFFFFFF); // Final XOR value and ensure 32-bit unsigned
28 | }
29 | }
--------------------------------------------------------------------------------
/AquaMai.ErrorReport/FileHeaderMeta.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai.ErrorReport;
2 |
3 | public class FileHeaderMeta
4 | {
5 | public string? Filename { get; set; }
6 | public string? ContentType { get; set; }
7 | }
--------------------------------------------------------------------------------
/AquaMai.ErrorReport/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $AquaMai.ErrorReport$_
5 |
6 |
7 |
--------------------------------------------------------------------------------
/AquaMai.ErrorReport/Polyfills.cs:
--------------------------------------------------------------------------------
1 | // 一些线索:
2 | // AdjustWndProcFlagsFromMetadata
3 | // DebuggableAttribute
4 |
5 | namespace MelonLoader;
6 |
7 | [AttributeUsage(AttributeTargets.Assembly)]
8 | public class MelonInfoAttribute : Attribute
9 | {
10 | public MelonInfoAttribute(Type type, string name, string version, string author, string downloadLink = null)
11 | {
12 | }
13 |
14 | public MelonInfoAttribute(Type type, string name, int versionMajor, int versionMinor, int versionRevision, string versionIdentifier, string author, string downloadLink = null)
15 | : this(type, name, $"{versionMajor}.{versionMinor}.{versionRevision}{(string.IsNullOrEmpty(versionIdentifier) ? "" : versionIdentifier)}", author, downloadLink)
16 | {
17 | }
18 |
19 | public MelonInfoAttribute(Type type, string name, int versionMajor, int versionMinor, int versionRevision, string author, string downloadLink = null)
20 | : this(type, name, versionMajor, versionMinor, versionRevision, null, author, downloadLink)
21 | {
22 | }
23 | }
24 |
25 | [AttributeUsage(AttributeTargets.Assembly)]
26 | public class MelonColorAttribute : Attribute
27 | {
28 | public MelonColorAttribute()
29 | {
30 | }
31 |
32 | public MelonColorAttribute(ConsoleColor color)
33 | {
34 | }
35 |
36 | public MelonColorAttribute(int alpha, int red, int green, int blue)
37 | {
38 | }
39 | }
40 |
41 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
42 | public class MelonGameAttribute : Attribute
43 | {
44 | public MelonGameAttribute(string developer = null, string name = null)
45 | {
46 | }
47 | }
48 |
49 | [AttributeUsage(AttributeTargets.Assembly)]
50 | public class HarmonyDontPatchAllAttribute : Attribute
51 | {
52 | public HarmonyDontPatchAllAttribute()
53 | {
54 | }
55 | }
56 |
57 | public class MelonMod
58 | {
59 | }
--------------------------------------------------------------------------------
/AquaMai.ErrorReport/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace AquaMai.ErrorReport;
4 |
5 | public class Program
6 | {
7 | [DllImport("SHCore.dll")]
8 | [return: MarshalAs(UnmanagedType.Bool)]
9 | public static extern bool SetProcessDpiAwareness(DPI_AWARENESS awareness);
10 |
11 | // 定义 DPI_AWARENESS 枚举
12 | public enum DPI_AWARENESS
13 | {
14 | DPI_AWARENESS_INVALID = -1,
15 | DPI_UNAWARE = 0,
16 | SYSTEM_AWARE = 1,
17 | PER_MONITOR_AWARE = 2
18 | }
19 |
20 | [STAThread]
21 | public static void Main()
22 | {
23 | SetProcessDpiAwareness(DPI_AWARENESS.SYSTEM_AWARE);
24 | Application.EnableVisualStyles();
25 | Application.SetCompatibleTextRenderingDefault(false);
26 | Application.Run(new CrashForm());
27 | }
28 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/DeprecationWarning.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 |
3 | namespace AquaMai.Mods;
4 |
5 | [ConfigSection(
6 | en: """
7 | These options have been deprecated and no longer work in the current version.
8 | Remove them to get rid of the warning message at startup.
9 | """,
10 | zh: """
11 | 这些配置项已经被废弃,在当前版本不再生效
12 | 删除它们以去除启动时的警告信息
13 | """,
14 | exampleHidden: true)]
15 | public class DeprecationWarning
16 | {
17 | [ConfigEntry(hideWhenDefault: true)]
18 | public static readonly bool v1_0_ModKeyMap_TestMode;
19 |
20 | // Print friendly warning messages here.
21 | // Please keep them up-to-date while refactoring the config.
22 | public static void OnBeforeAllPatch()
23 | {
24 | if (v1_0_ModKeyMap_TestMode)
25 | {
26 | MelonLoader.MelonLogger.Warning("ModKeyMap.TestMode has been deprecated (> v1.0). Please use GameSystem.KeyMap.Test instead.");
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Enhancement/ServerNotice.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using AquaMai.Config.Attributes;
3 | using AquaMai.Core.Helpers;
4 | using AquaMai.Mods.Types;
5 | using JetBrains.Annotations;
6 | using MelonLoader;
7 | using MelonLoader.TinyJSON;
8 |
9 | namespace AquaMai.Mods.Enhancement;
10 |
11 | [ConfigSection(
12 | defaultOn: true,
13 | exampleHidden: true,
14 | en: """
15 | Show extra information from compatible server
16 | (no side effects for other servers, no extra requests made)
17 | """,
18 | zh: """
19 | 在用户登录之类的时候,显示来自兼容服务器的额外信息
20 | (对其他服务器无副作用,不会发出额外请求)
21 | """)]
22 | public class ServerNotice
23 | {
24 | private const string FieldName = "_aquaMaiServerNotice";
25 |
26 | private class ServerNoticeEntry : ConditionalMessage
27 | {
28 | [CanBeNull] public string title = null;
29 | [CanBeNull] public string content = null;
30 | }
31 |
32 | private class ServerNoticeData
33 | {
34 | public ServerNoticeEntry[] entries = [];
35 | }
36 |
37 | public static void OnBeforePatch()
38 | {
39 | NetPacketHook.OnNetPacketComplete += OnNetPacketComplete;
40 | }
41 |
42 | private static Variant OnNetPacketComplete(string api, Variant request, Variant response)
43 | {
44 | if (response is not ProxyObject obj) return null;
45 | var serverNoticeJson = obj.Keys.Contains(FieldName) ? obj[FieldName] : null;
46 | if (serverNoticeJson == null) return null;
47 |
48 | var data = serverNoticeJson.Make();
49 | var entry = data?.entries.FirstOrDefault(it => it.ShouldShow());
50 | if (entry == null) return null;
51 |
52 | MelonLogger.Msg($"[ServerNotice] {entry.title}: {entry.content}");
53 | MessageHelper.ShowMessage(entry.content, title: entry.title);
54 | return null;
55 | }
56 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/CustomCreditsString.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using AquaMai.Core.Attributes;
3 | using HarmonyLib;
4 | using Monitor;
5 |
6 | namespace AquaMai.Mods.Fancy;
7 |
8 | [ConfigSection(
9 | en: "Set the \"CREDIT(S)\" string displayed at the middle and top-right corner of the screen.",
10 | zh: "自定义中间和右上角的 \"CREDIT(S)\"(可用点数)文本")]
11 | public class CustomCreditsString
12 | {
13 | [ConfigEntry(
14 | en: "Custom string to replace the \"CREDIT(S)\". Empty for a blank string.",
15 | zh: "用于替换 \"CREDIT(S)\" 的自定义文本,留空则为空白"
16 | )]
17 | private static readonly string creditsString = "";
18 |
19 | [ConfigEntry(
20 | en: "Hide the number of credits after the \"CREDITS(S)\" string.",
21 | zh: "隐藏 \"CREDIT(S)\" 后的可用点数数量"
22 | )]
23 | private static readonly bool hideCreditsNumber = true;
24 |
25 | [HarmonyPrefix]
26 | [HarmonyPatch(typeof(CreditController), "Initialize")]
27 | public static void Initialize(ref string ____freePlayText, ref string ___CreditText)
28 | {
29 | ____freePlayText = creditsString;
30 | ___CreditText = hideCreditsNumber
31 | ? creditsString
32 | : creditsString.TrimEnd() + " "; // Original: "CREDIT(S) "
33 | }
34 |
35 | [EnableIf(nameof(hideCreditsNumber))]
36 | [HarmonyPostfix]
37 | [HarmonyPatch(typeof(CreditController), "SetCredits")]
38 | public static void PostSetCredits(TMPro.TextMeshProUGUI ____creditText, string ___CreditText)
39 | {
40 | ____creditText.text = ___CreditText.TrimEnd();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/CustomLogo.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using AquaMai.Config.Attributes;
4 | using AquaMai.Core.Helpers;
5 | using HarmonyLib;
6 | using Monitor;
7 | using Process;
8 | using UnityEngine;
9 | using UnityEngine.UI;
10 |
11 | namespace AquaMai.Mods.Fancy;
12 |
13 | [ConfigSection(
14 | en: "Replace the \"SEGA\" and \"ALL.Net\" logos with custom ones.",
15 | zh: "用自定义的图片替换「SEGA」和「ALL.Net」的标志")]
16 | public class CustomLogo
17 | {
18 | [ConfigEntry(
19 | en: "Replace the \"SEGA\" logo with a random PNG image from this directory.",
20 | zh: "从此目录中随机选择一张 PNG 图片用于「SEGA」标志")]
21 | private static readonly string segaLogoDir = "LocalAssets/SegaLogo";
22 |
23 | [ConfigEntry(
24 | en: "Replace the \"ALL.Net\" logo with a random PNG image from this directory.",
25 | zh: "从此目录中随机选择一张 PNG 图片用于「ALL.Net」标志")]
26 | private static readonly string allNetLogoDir = "LocalAssets/AllNetLogo";
27 |
28 | private readonly static List segaLogo = [];
29 | private readonly static List allNetLogo = [];
30 |
31 | public static void OnBeforePatch()
32 | {
33 | EnumSprite(segaLogo, FileSystem.ResolvePath(segaLogoDir));
34 | EnumSprite(allNetLogo, FileSystem.ResolvePath(allNetLogoDir));
35 | }
36 |
37 | private static void EnumSprite(List collection, string path)
38 | {
39 | if (!Directory.Exists(path)) return;
40 | foreach (var file in Directory.EnumerateFiles(path, "*.png"))
41 | {
42 | var data = File.ReadAllBytes(file);
43 | var texture2D = new Texture2D(1, 1, TextureFormat.RGBA32, false);
44 | if (texture2D.LoadImage(data))
45 | {
46 | collection.Add(Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f)));
47 | }
48 | }
49 | }
50 |
51 | [HarmonyPatch(typeof(AdvertiseProcess), "OnStart")]
52 | [HarmonyPostfix]
53 | private static void AdvProcessPostFix(AdvertiseMonitor[] ____monitors)
54 | {
55 | if (segaLogo.Count > 0)
56 | {
57 | var logo = segaLogo[UnityEngine.Random.Range(0, segaLogo.Count)];
58 | foreach (var monitor in ____monitors)
59 | {
60 | monitor.transform.Find("Canvas/Main/SegaAllNet_LOGO/NUL_ADT_SegaAllNet_LOGO/SegaLogo").GetComponent().sprite = logo;
61 | }
62 | }
63 |
64 | if (allNetLogo.Count > 0)
65 | {
66 | var logo = allNetLogo[UnityEngine.Random.Range(0, allNetLogo.Count)];
67 | foreach (var monitor in ____monitors)
68 | {
69 | monitor.transform.Find("Canvas/Main/SegaAllNet_LOGO/NUL_ADT_SegaAllNet_LOGO/AllNetLogo").GetComponent().sprite = logo;
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/CustomPlaceName.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Manager;
4 |
5 | namespace AquaMai.Mods.Fancy;
6 |
7 | [ConfigSection(
8 | en: """
9 | Custom shop name in photo.
10 | Also enable shop name display in SDGA.
11 | """,
12 | zh: """
13 | 自定义拍照的店铺名称
14 | 同时在 SDGA 中会启用店铺名称的显示(但是不会在游戏里有设置)
15 | """)]
16 | public class CustomPlaceName
17 | {
18 | [ConfigEntry]
19 | private static readonly string placeName = "";
20 |
21 | [HarmonyPostfix]
22 | [HarmonyPatch(typeof(OperationManager), "CheckAuth_Proc")]
23 | public static void CheckAuth_Proc(OperationManager __instance)
24 | {
25 | if (string.IsNullOrEmpty(placeName))
26 | {
27 | return;
28 | }
29 |
30 | __instance.ShopData.ShopName = placeName;
31 | __instance.ShopData.ShopNickName = placeName;
32 | }
33 |
34 | [HarmonyPostfix]
35 | [HarmonyPatch(typeof(ResultCardBaseController), "Initialize")]
36 | public static void Initialize(ResultCardBaseController __instance)
37 | {
38 | if (string.IsNullOrEmpty(placeName))
39 | {
40 | return;
41 | }
42 |
43 | __instance.SetVisibleStoreName(true);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/CustomTrackStartDiff.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 | using Monitor;
5 | using UI;
6 | using UnityEngine;
7 | using UnityEngine.UI;
8 |
9 | namespace AquaMai.Mods.Fancy;
10 |
11 | [ConfigSection(
12 | en: """
13 | Custom track start difficulty image (not really custom difficulty).
14 | Requires CustomSkins to be enabled.
15 | Will load four image resources through custom skins: musicBase, musicTab, musicLvBase, musicLvText.
16 | """,
17 | zh: """
18 | 自定义在歌曲开始界面上显示的难度贴图 (并不是真的自定义难度)
19 | 需要启用自定义皮肤功能
20 | 会通过自定义皮肤加载四个图片资源: musicBase, musicTab, musicLvBase, musicLvText
21 | """)]
22 | public class CustomTrackStartDiff
23 | {
24 | // 自定义在歌曲开始界面上显示的难度 (并不是真的自定义难度)
25 | // 需要启用自定义皮肤功能
26 | // 会加载四个图片资源: musicBase, musicTab, musicLvBase, musicLvText
27 |
28 | [HarmonyPostfix]
29 | [HarmonyPatch(typeof(TrackStartMonitor), "SetTrackStart")]
30 | private static void DisableTabs(
31 | MultipleImage ____musicBaseImage,
32 | MultipleImage ____musicTabImage,
33 | SpriteCounter ____difficultySingle,
34 | SpriteCounter ____difficultyDouble,
35 | Image ____levelTextImage,
36 | List ____musicLevelSpriteSheets,
37 | TimelineRoot ____musicDetail
38 | )
39 | {
40 | var texture = CustomSkins.CustomTrackStart[0];
41 | if (texture != null)
42 | {
43 | ____musicBaseImage.MultiSprites[6] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f);
44 | ____musicBaseImage.ChangeSprite(6);
45 | }
46 |
47 | texture = CustomSkins.CustomTrackStart[1];
48 | if (texture != null)
49 | {
50 | ____musicTabImage.MultiSprites[6] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f);
51 | ____musicTabImage.ChangeSprite(6);
52 | }
53 |
54 | texture = CustomSkins.CustomTrackStart[2];
55 | if (texture != null)
56 | {
57 | var lvBase = Traverse.Create(____musicDetail).Field("_lv_Base").Value;
58 | lvBase.MultiSprites[6] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f);
59 | lvBase.ChangeSprite(6);
60 | }
61 |
62 | texture = CustomSkins.CustomTrackStart[3];
63 | if (texture != null)
64 | {
65 | var original = ____musicLevelSpriteSheets[0].Sheet;
66 | var sheet = new Sprite[original.Length];
67 | for (var i = 0; i < original.Length; i++)
68 | {
69 | var sprite = original[i];
70 | sheet[i] = Sprite.Create(texture, sprite.textureRect, new Vector2(0.5f, 0.5f), 100f);
71 | }
72 |
73 | ____difficultySingle.SetSpriteSheet(sheet);
74 | ____difficultyDouble.SetSpriteSheet(sheet);
75 | ____levelTextImage.sprite = sheet[14];
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/CustomVersionString.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 |
4 | namespace AquaMai.Mods.Fancy;
5 |
6 | [ConfigSection(
7 | en: "Set the version string displayed at the top-right corner of the screen.",
8 | zh: "把右上角的版本更改为自定义文本")]
9 | public class CustomVersionString
10 | {
11 | [ConfigEntry]
12 | private static readonly string versionString = "";
13 |
14 | /*
15 | * Patch displayVersionString Property Getter
16 | */
17 | [HarmonyPrefix]
18 | [HarmonyPatch(typeof(MAI2System.Config), "displayVersionString", MethodType.Getter)]
19 | public static bool GetDisplayVersionString(ref string __result)
20 | {
21 | if (string.IsNullOrEmpty(versionString))
22 | {
23 | return true;
24 | }
25 |
26 | __result = versionString;
27 | // Return false to block the original method
28 | return false;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/DemoMaster.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using DB;
3 | using HarmonyLib;
4 | using MAI2.Util;
5 | using Manager;
6 | using Process;
7 |
8 | namespace AquaMai.Mods.Fancy;
9 |
10 | [ConfigSection(
11 | en: "Play \"Master\" difficulty on Demo screen.",
12 | zh: "在闲置时的演示画面上播放紫谱而不是绿谱")]
13 | public class DemoMaster
14 | {
15 | [HarmonyPostfix]
16 | [HarmonyPatch(typeof(AdvDemoProcess), "OnStart")]
17 | public static void AdvDemoProcessPostStart()
18 | {
19 | for (int i = 0; i < 2; i++)
20 | {
21 | var userOption = Singleton.Instance.GetGameScore(i).UserOption;
22 | userOption.NoteSpeed = OptionNotespeedID.Speed6_5;
23 | userOption.TouchSpeed = OptionTouchspeedID.Speed7_0;
24 | }
25 | }
26 |
27 | [HarmonyPrefix]
28 | [HarmonyPatch(typeof(GamePlayManager), "InitializeAdvertise")]
29 | public static void PreInitializeAdvertise()
30 | {
31 | GameManager.SelectDifficultyID[0] = 3;
32 | GameManager.SelectDifficultyID[1] = 3;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/GamePlay/AlignCircleSlideJudgeDisplay.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 | using Manager;
5 | using Monitor;
6 | using UnityEngine;
7 |
8 | namespace AquaMai.Mods.Fancy.GamePlay;
9 |
10 | [ConfigSection(
11 | en: """
12 | Make the judgment display of circular Slides align precisely with the judgment line (originally a bit off).
13 | Just like in majdata.
14 | """,
15 | zh: """
16 | 让圆弧形的 Slide 的判定显示与判定线精确对齐 (原本会有一点歪)
17 | 就像 majdata 里那样
18 | """)]
19 | public class AlignCircleSlideJudgeDisplay
20 | {
21 | /*
22 | * 这个 Patch 让圆弧形的 Slide 的判定显示与判定线精确对齐 (原本会有一点歪), 就像 majdata 里那样
23 | */
24 | [HarmonyPostfix]
25 | [HarmonyPatch(typeof(SlideRoot), "Initialize")]
26 | private static void FixJudgePosition(
27 | SlideRoot __instance, SlideType ___EndSlideType, SlideJudge ___JudgeObj
28 | )
29 | {
30 | if (null != ___JudgeObj)
31 | {
32 | float z = ___JudgeObj.transform.localPosition.z;
33 | if (___EndSlideType == SlideType.Slide_Circle_L)
34 | {
35 | float angle = -45.0f - 45.0f * __instance.EndButtonId;
36 | double angleRad = Math.PI / 180.0 * (angle + 90 + 22.5 + 2.6415);
37 | ___JudgeObj.transform.localPosition = new Vector3(480f * (float)Math.Cos(angleRad), 480f * (float)Math.Sin(angleRad), z);
38 | ___JudgeObj.transform.localRotation = Quaternion.Euler(0.0f, 0.0f, angle);
39 | }
40 | else if (___EndSlideType == SlideType.Slide_Circle_R)
41 | {
42 | float angle = -45.0f * __instance.EndButtonId;
43 | double angleRad = Math.PI / 180.0 * (angle + 90 - 22.5 - 2.6415);
44 | ___JudgeObj.transform.localPosition = new Vector3(480f * (float)Math.Cos(angleRad), 480f * (float)Math.Sin(angleRad), z);
45 | ___JudgeObj.transform.localRotation = Quaternion.Euler(0.0f, 0.0f, angle);
46 | }
47 | }
48 | }
49 |
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/GamePlay/BreakSlideJudgeBlink.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Monitor;
4 | using UnityEngine;
5 |
6 | namespace AquaMai.Mods.Fancy.GamePlay;
7 |
8 | [ConfigSection(
9 | en: """
10 | This Patch makes the Critical judgment of BreakSlide also flicker like BreakTap.
11 | Recommended to use with custom skins (otherwise the visual effect may not be good).
12 | """,
13 | zh: """
14 | 这个 Patch 让 BreakSlide 的 Critical 判定也可以像 BreakTap 一样闪烁
15 | 推荐与自定义皮肤一起使用 (否则视觉效果可能并不好)
16 | """)]
17 | public class BreakSlideJudgeBlink
18 | {
19 | /*
20 | * 这个 Patch 让 BreakSlide 的 Critical 判定也可以像 BreakTap 一样闪烁
21 | * 推荐与自定义皮肤一起使用 (否则视觉效果可能并不好)
22 | */
23 | [HarmonyPostfix]
24 | [HarmonyPatch(typeof(SlideJudge), "UpdateBreakEffectAdd")]
25 | private static void FixBreakSlideJudgeBlink(
26 | SpriteRenderer ___SpriteRenderAdd, int ____addEffectCount
27 | )
28 | {
29 | if (!___SpriteRenderAdd.gameObject.activeSelf) return;
30 | float num = (____addEffectCount & 0b10) >> 1;
31 | ___SpriteRenderAdd.color = new Color(num, num, num, 1f);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/CustomSlideNoteData.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using DB;
4 | using Manager;
5 | using MelonLoader;
6 | using UnityEngine;
7 |
8 | namespace AquaMai.Mods.Fancy.GamePlay.CustomNoteTypes.Libs;
9 |
10 | public class CustomSlideNoteData: NoteData
11 | {
12 | public string SlideCode;
13 | public List> SlidePathList = new List>();
14 | public List> SlideHitAreasList = new List>();
15 | public float SlidePathLength;
16 |
17 | public bool ParseSlideCode(string slideCode, OptionMirrorID mirrorMode)
18 | {
19 | if (string.IsNullOrEmpty(slideCode))
20 | {
21 | return false;
22 | }
23 |
24 | SlidePathList.Clear();
25 | SlideHitAreasList.Clear();
26 |
27 | this.SlideCode = slideCode;
28 | var path = SlideCodeParser.Parse(slideCode);
29 | if (path == null)
30 | {
31 | return false;
32 | }
33 |
34 | var arrowData = SlideDataBuilder.BuildArrowData(path);
35 | SlidePathLength = (float)path.GetPathLength();
36 | var hitAreaData = SlideDataBuilder.BuildHitAreas(path);
37 | for (var i = 0; i < 8; i++)
38 | {
39 | SlidePathList.Add(SlideDataBuilder.ConvertAndRotateArrowData(arrowData, i, mirrorMode));
40 | SlideHitAreasList.Add(SlideDataBuilder.ConvertAndRotateHitAreas(hitAreaData, i, mirrorMode));
41 | }
42 |
43 | var msg = string.Join(", ",
44 | hitAreaData.Select(x => x.PanelAreas).Select(x => string.Join("/", x.Cast())));
45 | MelonLogger.Msg(msg);
46 |
47 | this.slideData.type = path.GetEndType(mirrorMode);
48 |
49 | return true;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/GamePlay/DisableTrackStartTabs.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Monitor;
4 | using TMPro;
5 | using UI;
6 | using UnityEngine;
7 |
8 | namespace AquaMai.Mods.Fancy.GamePlay;
9 |
10 | [ConfigSection(
11 | en: """
12 | Disable the TRACK X text, DX/Standard display box, and the derakkuma at the bottom of the screen in the song start screen.
13 | For recording chart confirmation.
14 | """,
15 | zh: """
16 | 在歌曲开始界面, 把 TRACK X 字样, DX/标准谱面的显示框, 以及画面下方的滴蜡熊隐藏掉
17 | 录制谱面确认用
18 | """)]
19 | public class DisableTrackStartTabs
20 | {
21 | // 在歌曲开始界面, 把 TRACK X 字样, DX/标准谱面的显示框, 以及画面下方的滴蜡熊隐藏掉, 让他看起来不那么 sinmai, 更像是 majdata
22 |
23 | [HarmonyPostfix]
24 | [HarmonyPatch(typeof(TrackStartMonitor), "SetTrackStart")]
25 | private static void DisableTabs(
26 | SpriteCounter ____trackNumber, SpriteCounter ____bossTrackNumber, SpriteCounter ____utageTrackNumber,
27 | MultipleImage ____musicTabImage, GameObject[] ____musicTabObj, GameObject ____derakkumaRoot,
28 | TimelineRoot ____musicDetail
29 | )
30 | {
31 | ____trackNumber.transform.parent.gameObject.SetActive(false);
32 | ____bossTrackNumber.transform.parent.gameObject.SetActive(false);
33 | ____utageTrackNumber.transform.parent.gameObject.SetActive(false);
34 | ____musicTabImage.gameObject.SetActive(false);
35 | ____musicTabObj[0].gameObject.SetActive(false);
36 | ____musicTabObj[1].gameObject.SetActive(false);
37 | ____musicTabObj[2].gameObject.SetActive(false);
38 | ____derakkumaRoot.SetActive(false);
39 | var traverse = Traverse.Create(____musicDetail);
40 | traverse.Field("_achivement_Base").Value.ChangeSprite(1);
41 | traverse.Field("_clearRank_Base").Value.ChangeSprite(1);
42 | traverse.Field("_achivement_Text").Value.gameObject.SetActive(false);
43 | traverse.Field("_achivement_decimal_Text").Value.gameObject.SetActive(false);
44 | traverse.Field("_achivement_percent_Text").Value.gameObject.SetActive(false);
45 | traverse.Field("_clearRank_Image").Value.gameObject.SetActive(false);
46 | traverse.Field("_deluxScore_Obj").Value.SetActive(false);
47 | traverse.Field("_comboRank_Image").Value.ChangeSprite(0);
48 | traverse.Field("_syncRank_Image").Value.ChangeSprite(0);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/GamePlay/FanJudgeFlip.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Monitor;
4 |
5 | namespace AquaMai.Mods.Fancy.GamePlay;
6 |
7 | [ConfigSection(
8 | en: """
9 | Make the judgment display of WiFi Slide different in up and down (originally all WiFi judgment displays are towards the center), just like in majdata.
10 | The reason for this bug is that SEGA forgot to assign EndButtonId to WiFi.
11 | """,
12 | zh: """
13 | 这个 Patch 让 WiFi Slide 的判定显示有上下的区别 (原本所有 WiFi 的判定显示都是朝向圆心的), 就像 majdata 里那样
14 | 这个 bug 产生的原因是 SBGA 忘记给 WiFi 的 EndButtonId 赋值了
15 | """)]
16 | public class FanJudgeFlip
17 | {
18 | /*
19 | * 这个 Patch 让 WiFi Slide 的判定显示有上下的区别 (原本所有 WiFi 的判定显示都是朝向圆心的), 就像 majdata 里那样
20 | * 这个 bug 产生的原因是 SBGA 忘记给 WiFi 的 EndButtonId 赋值了
21 | * 不过需要注意的是, 考虑到圆弧形 Slide 的判定显示就是永远朝向圆心的, 我个人会觉得这个 Patch 关掉更好看一点
22 | */
23 | [HarmonyPostfix]
24 | [HarmonyPatch(typeof(SlideFan), "Initialize")]
25 | private static void FixFanJudgeFilp(
26 | int[] ___GoalButtonId, SlideJudge ___JudgeObj
27 | )
28 | {
29 | if (null != ___JudgeObj)
30 | {
31 | if (2 <= ___GoalButtonId[1] && ___GoalButtonId[1] <= 5)
32 | {
33 | ___JudgeObj.Flip(false);
34 | ___JudgeObj.transform.Rotate(0.0f, 0.0f, 180f);
35 | }
36 | else
37 | {
38 | ___JudgeObj.Flip(true);
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/GamePlay/HideHanabi.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using Fx;
3 | using HarmonyLib;
4 | using Monitor;
5 | using UnityEngine;
6 |
7 | namespace AquaMai.Mods.Fancy.GamePlay;
8 |
9 | [ConfigSection(
10 | en: "Hide hanabi completely.",
11 | zh: "完全隐藏烟花")]
12 | public class HideHanabi
13 | {
14 | [HarmonyPatch(typeof(TapCEffect), "SetUpParticle")]
15 | [HarmonyPostfix]
16 | public static void FixZeroSize(TapCEffect __instance, FX_Mai2_Note_Color ____particleControler)
17 | {
18 | var entities = ____particleControler.GetComponentsInChildren(true);
19 | foreach (var entity in entities)
20 | {
21 | entity.maxParticleSize = 0f;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/GamePlay/JudgeDisplay4B.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Monitor;
4 | using UnityEngine;
5 |
6 | namespace AquaMai.Mods.Fancy.GamePlay;
7 |
8 | [ConfigSection(
9 | en: """
10 | More detailed judgment display.
11 | Requires CustomSkins to be enabled and the resource file to be downloaded.
12 | https://github.com/hykilpikonna/AquaDX/releases/download/nightly/JudgeDisplay4B.7z
13 | """,
14 | zh: """
15 | 更精细的判定表示
16 | 需开启 CustomSkins 并下载资源文件
17 | https://github.com/hykilpikonna/AquaDX/releases/download/nightly/JudgeDisplay4B.7z
18 | """)]
19 | public class JudgeDisplay4B
20 | {
21 | // 精确到子判定的自定义判定显示, 需要启用自定义皮肤功能 (理论上不启用自定义皮肤不会崩游戏, 只不过此时这个功能显然不会生效)
22 |
23 | [HarmonyPostfix]
24 | [HarmonyPatch(typeof(SlideJudge), "Initialize")]
25 | private static void SlideJudgeDisplay4B(
26 | SpriteRenderer ___SpriteRenderAdd, SpriteRenderer ___SpriteRender,
27 | SlideJudge.SlideJudgeType ____judgeType, SlideJudge.SlideAngle ____angle,
28 | NoteJudge.ETiming judge, float msec, bool isBreak
29 | )
30 | {
31 | var i = isBreak ? 1 : 0;
32 | Sprite sprite = CustomSkins.CustomJudgeSlide[i, (int)____judgeType, (int)____angle, (int)judge];
33 | if (sprite != null) {
34 | ___SpriteRender.sprite = sprite;
35 | }
36 |
37 | if (isBreak && judge == NoteJudge.ETiming.Critical)
38 | {
39 | sprite = CustomSkins.CustomJudgeSlide[i, (int)____judgeType, (int)____angle, (int) NoteJudge.ETiming.End];
40 | if (sprite != null)
41 | {
42 | ___SpriteRenderAdd.sprite = sprite;
43 | }
44 | }
45 | }
46 |
47 |
48 | [HarmonyPostfix]
49 | [HarmonyPatch(typeof(JudgeGrade), "Initialize")]
50 | private static void JudgeGradeDisplay4B(
51 | SpriteRenderer ___SpriteRender,
52 | NoteJudge.ETiming judge, float msec, NoteJudge.EJudgeType type
53 | )
54 | {
55 | var i = (type == NoteJudge.EJudgeType.Break) ? 1 : 0;
56 | Sprite sprite = CustomSkins.CustomJudge[i, (int)judge];
57 | if (sprite != null) {
58 | ___SpriteRender.sprite = sprite;
59 | }
60 | }
61 |
62 | [HarmonyPostfix]
63 | [HarmonyPatch(typeof(JudgeGrade), "InitializeBreak")]
64 | private static void JudgeGradeBreakDisplay4B(
65 | SpriteRenderer ___SpriteRenderAdd,
66 | NoteJudge.ETiming judge, float msec, NoteJudge.EJudgeType type
67 | )
68 | {
69 | if (judge == NoteJudge.ETiming.Critical)
70 | {
71 | var sprite = CustomSkins.CustomJudge[1, (int) NoteJudge.ETiming.End];
72 | if (sprite != null)
73 | {
74 | ___SpriteRenderAdd.sprite = sprite;
75 | }
76 | }
77 | }
78 |
79 | [HarmonyPrefix]
80 | [HarmonyPatch(typeof(JudgeGrade), "InitializeBreak")]
81 | private static void InitializeBreakFix(ref NoteJudge.EJudgeType type)
82 | {
83 | type = NoteJudge.EJudgeType.Break;
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/GamePlay/RealisticRandomJudge.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Manager;
4 |
5 | namespace AquaMai.Mods.Fancy.GamePlay;
6 |
7 | [ConfigSection(
8 | en: """
9 | Make the AutoPlay random judgment mode really randomize all judgments (down to sub-judgments).
10 | The original random judgment will only produce all 15 judgment results from Miss(TooFast) ~ Critical ~ Miss(TooLate).
11 | Here, it is changed to a triangular distribution to produce all 15 judgment results from Miss(TooFast) ~ Critical ~ Miss(TooLate).
12 | Of course, it will not consider whether the original Note really has a corresponding judgment (such as Slide should not have non-Critical Prefect).
13 | """,
14 | zh: """
15 | 让 AutoPlay 的随机判定模式真的会随机产生所有的判定 (精确到子判定)
16 | 原本的随机判定只会等概率产生 Critical, LateGreat1st, LateGood, Miss(TooLate)
17 | 这里改成三角分布产生从 Miss(TooFast) ~ Critical ~ Miss(TooLate) 的所有 15 种判定结果
18 | 当然, 此处并不会考虑原本那个 Note 是不是真的有对应的判定 (比如 Slide 实际上不应该有小 p 之类的)
19 | """)]
20 | public class RealisticRandomJudge
21 | {
22 | // 让 AutoPlay 的随机判定模式真的会随机产生所有的判定 (精确到子判定)
23 | // 原本的随机判定只会等概率产生 Critical, LateGreat1st, LateGood, Miss(TooLate)
24 | // 这里改成三角分布产生从 Miss(TooFast) ~ Critical ~ Miss(TooLate) 的所有 15 种判定结果
25 | // 当然, 此处并不会考虑原本那个 Note 是不是真的有对应的判定 (比如 Slide 实际上不应该有小 p 之类的)
26 |
27 | [HarmonyPostfix]
28 | [HarmonyPatch(typeof(GameManager), "AutoJudge")]
29 | private static NoteJudge.ETiming RealAutoJudgeRandom(NoteJudge.ETiming retval)
30 | {
31 | if (GameManager.AutoPlay == GameManager.AutoPlayMode.Random)
32 | {
33 | var x = UnityEngine.Random.Range(0, 8);
34 | x += UnityEngine.Random.Range(0, 8);
35 | return (NoteJudge.ETiming) x;
36 | }
37 | return retval;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/GamePlay/SlideLayerReverse.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 | using Monitor;
5 | using UnityEngine;
6 |
7 | namespace AquaMai.Mods.Fancy.GamePlay;
8 |
9 | [ConfigSection(
10 | en: """
11 | Invert the Slide hierarchy, so that the new Slide appears on top like Maimai classic.
12 | Enable to support color changing effects achieved by overlaying multiple stars.
13 | """,
14 | zh: """
15 | 反转 Slide 层级, 使新出现的 Slide 像旧框一样显示在上层
16 | 启用以支持通过叠加多个星星达成的变色效果
17 | """)]
18 | public class SlideLayerReverse
19 | {
20 | [HarmonyPostfix]
21 | [HarmonyPatch(typeof(SlideRoot), "Initialize")]
22 | private static void CalcArrowLayer(
23 | bool ___BreakFlag, List ____spriteRenders, List ____breakSpriteRenders,
24 | int ___SlideIndex, int ____baseArrowSortingOrder
25 | )
26 | {
27 | // 原本的 sortingOrder 是 -(SlideIndex + _baseArrowSortingOrder + index)
28 | // 令 orderBase = SlideIndex + _baseArrowSortingOrder
29 | // 分配给这条 slide 的 sortingOrder 范围是 -(orderBase + count - 1) ~ -(orderBase)
30 | // 现在要保留 slide 内部箭头顺序, 但使得 slide 间次序反转
31 | // 范围会变成 orderBase ~ orderBase + count - 1
32 | // 其中原本是 -(orderBase) 的箭头应该调整为 orderBase + count - 1
33 |
34 | var orderBase = ___SlideIndex + ____baseArrowSortingOrder; // SlideIndex + _baseArrowSortingOrder
35 | if (!___BreakFlag)
36 | {
37 | var lastIdx = ____spriteRenders.Count - 1;
38 | for (var index = 0; index < ____spriteRenders.Count; index++)
39 | {
40 | var renderer = ____spriteRenders[index];
41 | renderer.sortingOrder = -32700 + orderBase + lastIdx - index;
42 | }
43 | }
44 | else
45 | {
46 | var lastIdx = ____breakSpriteRenders.Count - 1;
47 | for (var index = 0; index < ____breakSpriteRenders.Count; index++)
48 | {
49 | var breakSlide = ____breakSpriteRenders[index];
50 | breakSlide.SetSortingOrder(-32700 + orderBase + lastIdx - index);
51 | }
52 | }
53 | }
54 |
55 | [HarmonyPostfix]
56 | [HarmonyPatch(typeof(SlideFan), "Initialize")]
57 | private static void CalcFanArrowLayer(
58 | SpriteRenderer[] ____spriteLines, SpriteRenderer[] ____effectSprites,
59 | int ___SlideIndex, int ____baseArrowSortingOrder
60 | )
61 | {
62 | var orderBase = ___SlideIndex + ____baseArrowSortingOrder; // SlideIndex + _baseArrowSortingOrder
63 | var lastIdx = ____spriteLines.Length - 1;
64 | for (var index = 0; index < ____spriteLines.Length; index++)
65 | {
66 | var renderer = ____spriteLines[index];
67 | renderer.sortingOrder = -32700 + orderBase + lastIdx - index;
68 | }
69 | lastIdx = ____effectSprites.Length - 1;
70 | for (var index = 0; index < ____effectSprites.Length; index++)
71 | {
72 | var renderer = ____effectSprites[index];
73 | renderer.sortingOrder = 1000 + orderBase + lastIdx - index;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/GamePlay/TrackStartProcessTweak.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Process;
4 | using UnityEngine;
5 |
6 | namespace AquaMai.Mods.Fancy.GamePlay;
7 |
8 | [ConfigSection(
9 | en: """
10 | Delayed the animation of the song start screen.
11 | For recording chart confirmation.
12 | """,
13 | zh: """
14 | 推迟了歌曲开始界面的动画
15 | 录制谱面确认用
16 | """)]
17 | public class TrackStartProcessTweak
18 | {
19 | // 总之这个 Patch 没啥用, 是我个人用 sinmai 录谱面确认时用得到, 顺手也写进来了
20 | // 具体而言就是推迟了歌曲开始界面的动画便于后期剪辑
21 |
22 | [HarmonyPrefix]
23 | [HarmonyPatch(typeof(TrackStartProcess), "OnUpdate")]
24 | private static bool DelayAnimation(
25 | TrackStartProcess.TrackStartSequence ____state,
26 | ref float ____timeCounter,
27 | ProcessDataContainer ___container
28 | )
29 | {
30 | if (____state == TrackStartProcess.TrackStartSequence.Wait)
31 | {
32 | // 将开始动画(就是“噔噔, 噔 噔噔”)推迟
33 | float temp = ____timeCounter + Time.deltaTime;
34 | if (____timeCounter < 1.0f && temp >= 1.0f)
35 | {
36 | // 这是用来让转场动画继续播放的, 原本就是这个时候 notify 的同时开始播放开始动画
37 | // 现在把开始动画往后延
38 | ___container.processManager.NotificationFadeIn();
39 | }
40 | ____timeCounter = temp;
41 | if (____timeCounter >= 3.0f)
42 | {
43 | return true;
44 | // 原 method 的逻辑是这样
45 | // case TrackStartProcess.TrackStartSequence.Wait:
46 | // this._timeCounter += Time.deltaTime;
47 | // if ((double) this._timeCounter >= 1.0)
48 | // {
49 | // this._timeCounter = 0.0f;
50 | // this._state = TrackStartProcess.TrackStartSequence.Disp;
51 | // /* 一些开始播放开始动画的代码 */
52 | // this.container.processManager.NotificationFadeIn();
53 | // break;
54 | // }
55 | // break;
56 | // 所以只要在 prefix 里面等到 timeCounter 达到我们想要的值以后再执行原 method 就好
57 | // 这里有个细节: NotificationFadeIn() 会被执行两遍, 这其实不好, 是个潜在 bug
58 | // 不过由于此处把开始动画往后推了 2s, 转场动画已经结束把 Process 释放掉了, 所以第二遍会找不到 Process 就没效果
59 | }
60 | return false;
61 | }
62 | else if (____state == TrackStartProcess.TrackStartSequence.DispEnd)
63 | {
64 | // 将开始动画结束以后的转场动画推迟
65 | ____timeCounter += Time.deltaTime; // timeCounter 会在先前由原本的 method 归零
66 | if (____timeCounter >= 1.0f)
67 | {
68 | return true;
69 | }
70 | return false;
71 | }
72 | return true;
73 | }
74 |
75 |
76 | }
77 |
78 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/HideMask.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Monitor;
4 | using UnityEngine;
5 |
6 | namespace AquaMai.Mods.Fancy;
7 |
8 | [ConfigSection(
9 | en: "Remove the circle mask of the game screen.",
10 | zh: "移除游戏画面的圆形遮罩")]
11 | public class HideMask
12 | {
13 | [HarmonyPrefix]
14 | [HarmonyPatch(typeof(Main.GameMain), "LateInitialize", typeof(MonoBehaviour), typeof(Transform), typeof(Transform))]
15 | public static void LateInitialize(MonoBehaviour gameMainObject)
16 | {
17 | GameObject.Find("Mask").SetActive(false);
18 | }
19 |
20 | [HarmonyPostfix]
21 | [HarmonyPatch(typeof(MonitorBackgroundTownController), "Awake")]
22 | public static void Awake(GameObject ____bgObject)
23 | {
24 | try
25 | {
26 | ____bgObject.transform.Find("BG").Find("Monitor_Mask").gameObject.SetActive(false);
27 | }
28 | catch
29 | {
30 | // ignored
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/README.md:
--------------------------------------------------------------------------------
1 | # Fancy
2 |
3 | All the fancy features, even if not required by most players, are welcomed to this category, whether for personalization, for beautify, for self-made charts or for other uncommon purposes.
4 |
5 | These patches may not well-tested by the project maintainers and could be enabled only if you know what you're doing.
6 |
7 | Patches affect the gameplay should go to the GamePlay subcategory.
8 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/RandomBgm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using AquaMai.Config.Attributes;
5 | using AquaMai.Core.Helpers;
6 | using HarmonyLib;
7 | using Mai2.Mai2Cue;
8 | using MAI2.Util;
9 | using Manager;
10 | using MelonLoader;
11 |
12 | namespace AquaMai.Mods.Fancy;
13 |
14 | [ConfigSection(
15 | en: """
16 | Random BGM.
17 | Put Mai2Cue.{acb,awb} of old version of the game in the configured directory and rename them.
18 | Won't work with 2P mode.
19 | """,
20 | zh: """
21 | 在配置的目录下放置了旧版游戏的 Mai2Cue.{acb,awb} 并重命名的话,可以在播放游戏 BGM 的时候随机播放这里面的旧版游戏 BGM
22 | 无法在 2P 模式下工作
23 | """)]
24 | public class RandomBgm
25 | {
26 | [ConfigEntry]
27 | private static readonly string mai2CueDir = "LocalAssets/Mai2Cue";
28 |
29 | private static List _acbs = new List();
30 |
31 | [HarmonyPostfix]
32 | [HarmonyPatch(typeof(SoundManager), "Initialize")]
33 | public static void Init()
34 | {
35 | var resolvedDir = FileSystem.ResolvePath(mai2CueDir);
36 | if (!Directory.Exists(resolvedDir)) return;
37 | var files = Directory.EnumerateFiles(resolvedDir);
38 | foreach (var file in files)
39 | {
40 | if (!file.EndsWith(".acb")) continue;
41 | // Seems there's limit for max opened ACB files
42 | _acbs.Add(Path.ChangeExtension(file, null));
43 | }
44 |
45 | MelonLogger.Msg($"Random BGM loaded {_acbs.Count} files");
46 | }
47 |
48 | [HarmonyPrefix]
49 | [HarmonyPatch(typeof(SoundManager), "Play")]
50 | public static void PrePlay(ref SoundManager.AcbID acbID, int cueID)
51 | {
52 | if (acbID != SoundManager.AcbID.Default) return;
53 | if (_acbs.Count == 0) return;
54 | var cueIndex = (Cue)cueID;
55 | switch (cueIndex)
56 | {
57 | case Cue.BGM_ENTRY:
58 | case Cue.BGM_COLLECTION:
59 | case Cue.BGM_RESULT_CLEAR:
60 | case Cue.BGM_RESULT:
61 | var acb = _acbs[UnityEngine.Random.Range(0, _acbs.Count)];
62 | acbID = SoundManager.AcbID.Max;
63 | var result = Singleton.Instance.LoadCueSheet((int)acbID, acb);
64 | MelonLogger.Msg($"Picked {acb} for {cueIndex}, result: {result}");
65 | return;
66 | default:
67 | return;
68 | }
69 | }
70 |
71 | [HarmonyPrefix]
72 | [HarmonyPatch(typeof(SoundManager), "PlayBGM")]
73 | public static bool PrePlayBGM(ref int target)
74 | {
75 | switch (target)
76 | {
77 | case 0:
78 | return true;
79 | case 1:
80 | return false;
81 | case 2:
82 | target = 0;
83 | return true;
84 | default:
85 | return false;
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fancy/Triggers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using AquaMai.Config.Attributes;
4 | using HarmonyLib;
5 | using Process;
6 |
7 | namespace AquaMai.Mods.Fancy;
8 |
9 | [ConfigSection(
10 | en: "Triggers for executing commands at certain events.",
11 | zh: "在一定时机执行命令的触发器")]
12 | public class Triggers
13 | {
14 | [ConfigEntry(
15 | en: "Execute some command on game idle.",
16 | zh: """
17 | 在游戏闲置的时候执行指定的命令脚本
18 | 比如说可以在游戏闲置时降低显示器的亮度
19 | """)]
20 | private static readonly string execOnIdle = "";
21 |
22 | [ConfigEntry(
23 | en: "Execute some command on game start.",
24 | zh: "在玩家登录的时候执行指定的命令脚本")]
25 | private static readonly string execOnEntry = "";
26 |
27 | [HarmonyPrefix]
28 | [HarmonyPatch(typeof(AdvertiseProcess), "OnStart")]
29 | public static void AdvertiseProcessPreStart()
30 | {
31 | if (!string.IsNullOrWhiteSpace(execOnIdle))
32 | {
33 | Exec(execOnIdle);
34 | }
35 | }
36 |
37 | [HarmonyPrefix]
38 | [HarmonyPatch(typeof(EntryProcess), "OnStart")]
39 | public static void EntryProcessPreStart()
40 | {
41 | if (!string.IsNullOrWhiteSpace(execOnEntry))
42 | {
43 | Exec(execOnEntry);
44 | }
45 | }
46 |
47 | [HarmonyPrefix]
48 | [HarmonyPatch(typeof(MusicSelectProcess), "OnStart")]
49 | public static void MusicSelectProcessPreStart()
50 | {
51 | if (!string.IsNullOrWhiteSpace(execOnEntry))
52 | {
53 | Exec(execOnEntry);
54 | }
55 | }
56 |
57 | private static void Exec(string command)
58 | {
59 | var process = new System.Diagnostics.Process();
60 | process.StartInfo.FileName = "cmd.exe";
61 | process.StartInfo.Arguments = "/c " + command;
62 | process.StartInfo.UseShellExecute = true;
63 | process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
64 | process.StartInfo.WorkingDirectory = Environment.CurrentDirectory;
65 |
66 | process.Start();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fix/DisableReboot.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Reflection;
3 | using AquaMai.Config.Attributes;
4 | using AquaMai.Core.Attributes;
5 | using HarmonyLib;
6 | using Manager.Operation;
7 | using MelonLoader;
8 |
9 | namespace AquaMai.Mods.Fix;
10 |
11 | [ConfigSection(exampleHidden: true, defaultOn: true)]
12 | public class DisableReboot
13 | {
14 | private static bool forceOfflineTimerExists = false;
15 |
16 | public static void OnBeforePatch()
17 | {
18 | forceOfflineTimerExists = typeof(MaintenanceTimer).GetProperty(
19 | "ForceOfflineRemainingMinutes",
20 | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) != null;
21 | }
22 |
23 | // IsAutoRebootNeeded
24 | [HarmonyPrefix]
25 | [HarmonyPatch(typeof(MaintenanceTimer), "IsAutoRebootNeeded")]
26 | public static bool IsAutoRebootNeeded(ref bool __result)
27 | {
28 | __result = false;
29 | return false;
30 | }
31 |
32 | // IsUnderServerMaintenance
33 | [HarmonyPrefix]
34 | [HarmonyPatch(typeof(MaintenanceTimer), "IsUnderServerMaintenance")]
35 | public static bool IsUnderServerMaintenance(ref bool __result)
36 | {
37 | __result = false;
38 | return false;
39 | }
40 |
41 | // RemainingMinutes
42 | // Original: private int RemainingMinutes => (this._secServerMaintenance + 59) / 60;
43 | [HarmonyPrefix]
44 | [HarmonyPatch(typeof(MaintenanceTimer), "RemainingMinutes", MethodType.Getter)]
45 | public static bool RemainingMinutes(ref int __result)
46 | {
47 | __result = 600;
48 | return false;
49 | }
50 |
51 | // GetAutoRebootSec
52 | [HarmonyPrefix]
53 | [HarmonyPatch(typeof(MaintenanceTimer), "GetAutoRebootSec")]
54 | public static bool GetAutoRebootSec(ref int __result)
55 | {
56 | __result = 60 * 60 * 10;
57 | return false;
58 | }
59 |
60 | // GetServerMaintenanceSec
61 | [HarmonyPrefix]
62 | [HarmonyPatch(typeof(MaintenanceTimer), "GetServerMaintenanceSec")]
63 | public static bool GetServerMaintenanceSec(ref int __result)
64 | {
65 | __result = 60 * 60 * 10;
66 | return false;
67 | }
68 |
69 | // Execute
70 | [HarmonyPrefix]
71 | [HarmonyPatch(typeof(MaintenanceTimer), "Execute")]
72 | public static bool Execute(MaintenanceTimer __instance)
73 | {
74 | return false;
75 | }
76 |
77 | // UpdateTimes
78 | [HarmonyPrefix]
79 | [HarmonyPatch(typeof(MaintenanceTimer), "UpdateTimes")]
80 | public static bool UpdateTimes(MaintenanceTimer __instance)
81 | {
82 | return false;
83 | }
84 |
85 | [HarmonyPrefix]
86 | [HarmonyPatch(typeof(ClosingTimer), "IsShowRemainingMinutes")]
87 | public static bool IsShowRemainingMinutes(ref bool __result)
88 | {
89 | __result = false;
90 | return false;
91 | }
92 |
93 | [HarmonyPrefix]
94 | [HarmonyPatch(typeof(ClosingTimer), "IsClosed")]
95 | public static bool IsClosed(ref bool __result)
96 | {
97 | __result = false;
98 | return false;
99 | }
100 |
101 | [EnableIf(nameof(forceOfflineTimerExists))]
102 | [HarmonyPrefix]
103 | [HarmonyPatch(typeof(MaintenanceTimer), "ForceOfflineRemainingMinutes", MethodType.Getter)]
104 | public static bool ForceOfflineRemainingMinutes(ref int __result)
105 | {
106 | __result = 600;
107 | return false;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fix/FixCheckAuth.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 | using AMDaemon.Allnet;
4 | using AquaMai.Config.Attributes;
5 | using HarmonyLib;
6 | using Manager;
7 | using Manager.Operation;
8 |
9 | namespace AquaMai.Mods.Fix;
10 |
11 | [ConfigSection(exampleHidden: true, defaultOn: true)]
12 | public class FixCheckAuth
13 | {
14 | [ConfigEntry]
15 | private static readonly bool allowHttpsUpgrade = true;
16 |
17 | [HarmonyPostfix]
18 | [HarmonyPatch(typeof(OperationManager), "CheckAuth_Proc")]
19 | private static void PostCheckAuthProc(ref OperationData ____operationData)
20 | {
21 | if (Auth.GameServerUri.StartsWith("http://") || Auth.GameServerUri.StartsWith("https://"))
22 | {
23 | ____operationData.ServerUri = Auth.GameServerUri;
24 |
25 | // Host is used only for "CheckServerHash" and it is disabled now
26 | // it originally contains server's tls cert hash
27 | // So this can be used to transfer ambiguous data
28 | // we use this to notify that we can upgrade the link to https
29 | // as if we originally pass a https link to game, games without CheckServerHash will reject the link because ssl pinning
30 | if (Auth.GameServerHost == "_AquaMai_Upgrade_")
31 | {
32 | ____operationData.ServerUri = Auth.GameServerUri.Replace("http://", "https://");
33 | }
34 | else if (upgradePort.IsMatch(Auth.GameServerHost))
35 | {
36 | var match = upgradePort.Match(Auth.GameServerHost);
37 | var builder = new UriBuilder(Auth.GameServerUri)
38 | {
39 | Port = int.Parse(match.Groups[1].Value),
40 | Scheme = Uri.UriSchemeHttps,
41 | };
42 | ____operationData.ServerUri = builder.ToString();
43 | }
44 | }
45 | }
46 |
47 | private static readonly Regex upgradePort = new Regex(@"_AquaMai_UpgradPort_(\d+)_", RegexOptions.Compiled);
48 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/Fix/FixConnSlide.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Reflection;
3 | using AquaMai.Core.Attributes;
4 | using AquaMai.Config.Attributes;
5 | using HarmonyLib;
6 | using Manager;
7 |
8 | namespace AquaMai.Mods.Fix.Legacy;
9 |
10 | [ConfigSection(exampleHidden: true, defaultOn: true)]
11 | [EnableGameVersion(23000)]
12 | public class FixConnSlide
13 | {
14 | /* 这个 Patch 用于修复以下 bug:
15 | * 非 ConnSlide 被错误解析为 ConnSlide (Fes 首日刹那旅程爆机 bug)
16 | * 原 method 逻辑如下:
17 | *
18 | * if (this.IsSlideAll(noteData1.type) && (index1 + 1 < this._note._noteData.Count ? 1 : 0) != 0)
19 | * {
20 | * int targetNote = noteData1.slideData.targetNote;
21 | * if (noteData1.slideData != null)
22 | * targetNote = noteData1.slideData.targetNote;
23 | * for (int index3 = index1; index3 < this._note._noteData.Count; ++index3)
24 | * {
25 | * NoteData noteData3 = this._note._noteData[index3];
26 | * if (this.IsSlideAll(noteData3.type) && noteData3.time == noteData1.end && noteData3.startButtonPos == targetNote && noteData3.parent == null)
27 | * {
28 | * noteData3.parent = noteData1.parent;
29 | * noteData1.child.Add(noteData3);
30 | * noteData3.isUsed = true;
31 | * noteData3.isJudged = true;
32 | * break;
33 | * }
34 | * }
35 | * }
36 | *
37 | * 修复 bug 需要把第二次调用 this.IsSlideAll() 更改为 this.IsConnectNote(), 这里使用 Transpiler 解决
38 | */
39 | [HarmonyTranspiler]
40 | [HarmonyPatch(typeof(NotesReader), "calcSlide")]
41 | private static IEnumerable Fix(IEnumerable instructions)
42 | {
43 | List instList = new List(instructions);
44 | bool found = false;
45 | MethodInfo methodIsSlideAll = AccessTools.Method(typeof(NotesReader), "IsSlideAll");
46 | MethodInfo methodIsConnectNote = AccessTools.Method(typeof(NotesReader), "IsConnectNote");
47 |
48 | for (int i = 0; i < instList.Count; i++)
49 | {
50 | CodeInstruction inst = instList[i];
51 | if (!found && inst.Calls(methodIsSlideAll))
52 | {
53 | found = true;
54 | continue;
55 | }
56 |
57 | if (found && inst.Calls(methodIsSlideAll))
58 | {
59 | inst.operand = methodIsConnectNote;
60 | // MelonLogger.Msg($"[FixConnSlide] Successfully patched NotesReader::calcSlide");
61 | break;
62 | }
63 | }
64 | return instList;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fix/Legacy/FixQuickRetry130.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Core.Attributes;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 | using Manager;
5 |
6 | namespace AquaMai.Mods.Fix.Legacy;
7 |
8 | [ConfigSection(exampleHidden: true, defaultOn: true)]
9 | [EnableGameVersion(23000, 23499, noWarn: true)]
10 | public class FixQuickRetry130
11 | {
12 | // Fix for the game not resetting Fast and Late counts when quick retrying
13 | // For game version < 1.35.0
14 | [HarmonyPostfix]
15 | [HarmonyPatch(typeof(GamePlayManager), "SetQuickRetryFrag")]
16 | public static void PostGamePlayManagerSetQuickRetryFrag(GamePlayManager __instance, bool flag)
17 | {
18 | // Since 1.35.0, `GameScoreList.Initialize()` resets the Fast and Late counts
19 | if (flag && !Traverse.Create(typeof(GameScoreList)).Methods().Contains("Initialize"))
20 | {
21 | for (int i = 0; i < 4; i++)
22 | {
23 | var gameScoreList = __instance.GetGameScore(i);
24 | var traverse = Traverse.Create(gameScoreList);
25 | traverse.Property("Fast").SetValue((uint)0);
26 | traverse.Property("Late").SetValue((uint)0);
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Fix/README.md:
--------------------------------------------------------------------------------
1 | # Fix
2 |
3 | Fix of the game's bugs or removal of the game's annoying unuseful "features".
4 |
5 | Non-removal "Fix" patches should have no (negative, or any visual changing) side-effects on the original game.
6 |
7 | All patches under "Fix" should enabled by default and hide in example. They could be still turned off manually in the config.
8 |
--------------------------------------------------------------------------------
/AquaMai.Mods/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | hidlibrary|Numerics
5 | $AquaMai.Mods$_
6 |
7 |
8 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSettings/CreditConfig.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using AquaMai.Core.Attributes;
3 | using HarmonyLib;
4 |
5 | namespace AquaMai.Mods.GameSettings;
6 |
7 | [ConfigSection(
8 | en: "Set the game to Paid Play (lock credits) or Free Play.",
9 | zh: "设置游戏为付费游玩(锁定可用点数)或免费游玩")]
10 | public class CreditConfig
11 | {
12 | [ConfigEntry(
13 | en: "Set to Free Play (set to false for Paid Play).",
14 | zh: "是否免费游玩(设为 false 时为付费游玩)")]
15 | private static readonly bool isFreePlay = true;
16 |
17 | [HarmonyPrefix]
18 | [HarmonyPatch(typeof(Manager.Credit), "IsFreePlay")]
19 | public static bool PreIsFreePlay(ref bool __result)
20 | {
21 | __result = isFreePlay;
22 | return false;
23 | }
24 |
25 | [ConfigEntry(
26 | en: "Lock credits amount (only valid in Paid Play). Set to 0 to disable.",
27 | zh: "锁定可用点数数量(仅在付费游玩时有效),设为 0 以禁用")]
28 | private static readonly uint lockCredits = 24u;
29 |
30 | private static bool ShouldLockCredits => !isFreePlay && lockCredits > 0;
31 |
32 | [EnableIf(nameof(ShouldLockCredits))]
33 | [HarmonyPrefix]
34 | [HarmonyPatch(typeof(Manager.Credit), "IsGameCostEnough")]
35 | public static bool PreIsGameCostEnough(ref bool __result)
36 | {
37 | __result = true;
38 | return false;
39 | }
40 |
41 | [EnableIf(nameof(ShouldLockCredits))]
42 | [HarmonyPrefix]
43 | [HarmonyPatch(typeof(AMDaemon.CreditUnit), "Credit", MethodType.Getter)]
44 | public static bool PreCredit(ref uint __result)
45 | {
46 | __result = lockCredits;
47 | return false;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSettings/ForceAsServer.cs:
--------------------------------------------------------------------------------
1 | using AMDaemon;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 |
5 | namespace AquaMai.Mods.GameSettings;
6 |
7 | [ConfigSection(
8 | en: "If you want to configure in-shop party-link, you should turn this off.",
9 | zh: "如果要配置店内招募的话,应该要把这个关闭",
10 | defaultOn: true)]
11 | public class ForceAsServer
12 | {
13 | [HarmonyPrefix]
14 | [HarmonyPatch(typeof(LanInstall), "IsServer", MethodType.Getter)]
15 | private static bool PreIsServer(ref bool __result)
16 | {
17 | __result = true;
18 | return false;
19 | }
20 |
21 | [HarmonyPrefix]
22 | [HarmonyPatch(typeof(Network), "IsLanAvailable", MethodType.Getter)]
23 | private static bool PreIsLanAvailable(ref bool __result)
24 | {
25 | __result = false;
26 | return false;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSettings/JudgeAdjust.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 | using IO;
5 | using Manager.UserDatas;
6 |
7 | namespace AquaMai.Mods.GameSettings;
8 |
9 | [ConfigSection(
10 | en: "Globally adjust A/B judgment (unit same as in-game options) or increase touch delay.",
11 | zh: "全局调整 A/B 判(单位和游戏里一样)或增加触摸延迟")]
12 | public class JudgeAdjust
13 | {
14 | [ConfigEntry(
15 | en: "Adjust A judgment.",
16 | zh: "调整 A 判")]
17 | private static readonly double a = 0;
18 |
19 | [ConfigEntry(
20 | en: "Adjust B judgment.",
21 | zh: "调整 B 判")]
22 | private static readonly double b = 0;
23 |
24 | [ConfigEntry(
25 | en: "Increase touch delay.",
26 | zh: "增加触摸延迟")]
27 | private static readonly uint touchDelay = 0;
28 |
29 | [HarmonyPostfix]
30 | [HarmonyPatch(typeof(UserOption), "GetAdjustMSec")]
31 | public static void GetAdjustMSec(ref float __result)
32 | {
33 | __result += (float)(a * 16.666666d);
34 | }
35 |
36 | [HarmonyPostfix]
37 | [HarmonyPatch(typeof(UserOption), "GetJudgeTimingFrame")]
38 | public static void GetJudgeTimingFrame(ref float __result)
39 | {
40 | __result += (float)b;
41 | }
42 |
43 | [HarmonyPrefix]
44 | [HarmonyPatch(typeof(NewTouchPanel), "Recv")]
45 | public static void NewTouchPanelRecv()
46 | {
47 | if (touchDelay <= 0) return;
48 | Thread.Sleep((int)touchDelay);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSettings/README.md:
--------------------------------------------------------------------------------
1 | # GameSettings
2 |
3 | Overriding or adjusting the game settings that're originally configurable / modifiable, but made into patches for unification, flexibility or convenience.
4 |
5 | Patches changing the way the game running / behaving which are not possible in the stock game may need to go to the GameSystem category.
6 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSystem/Assets/LoadAssetBundleWithoutManifest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using HarmonyLib;
4 | using UnityEngine;
5 | using Manager;
6 | using Util;
7 | using AquaMai.Config.Attributes;
8 |
9 | namespace AquaMai.Mods.GameSystem.Assets;
10 |
11 | [ConfigSection(
12 | en: "Load all existing \".ab\" image resources regardless of the AssetBundleImages manifest.",
13 | zh: """
14 | 加载所有存在的 .ab 图片资源(无视 AssetBundleImages.manifest)
15 | 导入了删除曲包之类的话,应该需要开启这个
16 | """)]
17 | public class LoadAssetBundleWithoutManifest
18 | {
19 | private static HashSet abFiles = new HashSet();
20 |
21 | [HarmonyPostfix]
22 | [HarmonyPatch(typeof(OptionDataManager), "CheckAssetBundle")]
23 | public static void PostCheckAssetBundle(ref Safe.ReadonlySortedDictionary abs)
24 | {
25 | foreach (var ab in abs)
26 | {
27 | abFiles.Add(ab.Key);
28 | }
29 | }
30 |
31 | [HarmonyPrefix]
32 | [HarmonyPatch(typeof(AssetBundleManifest), "GetAllAssetBundles")]
33 | public static bool PreGetAllAssetBundles(AssetBundleManifest __instance, ref string[] __result)
34 | {
35 | __result = abFiles.ToArray();
36 | return false;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSystem/OptionLoadFix.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using AMDaemon;
4 | using AquaMai.Config.Attributes;
5 | using HarmonyLib;
6 | using Manager;
7 | using UnityEngine;
8 |
9 | namespace AquaMai.Mods.GameSystem;
10 |
11 | [ConfigSection(
12 | en: "When loading Opt in StreamingAssets, not only load those starting with A, but also those starting with the letter corresponding to the version",
13 | zh: "在 StreamingAssets 加载 Opt 时,不仅加载 A 开头的,也加载版本对应字母开头的",
14 | defaultOn: true)]
15 | public static class OptionLoadFix
16 | {
17 | [HarmonyPrefix]
18 | [HarmonyPatch(typeof(AppImage), "OptionMountRootPath", MethodType.Getter)]
19 | public static bool AppImageOptionMountRootPath(ref string __result)
20 | {
21 | __result = Application.streamingAssetsPath;
22 |
23 | return false;
24 | }
25 |
26 | // 如果 optDir 也是 A 开头的话,结果会重复,需要去重
27 | [HarmonyPostfix]
28 | [HarmonyPatch(typeof(DataManager), "GetDirs")]
29 | public static void DataManagerGetDirs(ref List dirs)
30 | {
31 | dirs = dirs.Distinct().ToList();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSystem/QuickRetry.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Core.Attributes;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 | using Manager;
5 |
6 | namespace AquaMai.Mods.GameSystem;
7 |
8 | [ConfigSection(
9 | en: "Hold the bottom four buttons (3456) for quick retry (like in Freedom Mode, default non-utage only).",
10 | zh: "按住下方四个按钮(3456)快速重开本局游戏(像在 Freedom Mode 中一样,默认仅对非宴谱有效)")]
11 | [EnableGameVersion(23000)]
12 | public class QuickRetry
13 | {
14 | [ConfigEntry(
15 | en: "Force enable in Utage.",
16 | zh: "在宴谱中强制启用")]
17 | private static readonly bool enableInUtage = false;
18 |
19 | [HarmonyPrefix]
20 | [HarmonyPatch(typeof(Monitor.QuickRetry), "IsQuickRetryEnable")]
21 | public static bool OnQuickRetryIsQuickRetryEnable(ref bool __result)
22 | {
23 | if (enableInUtage)
24 | {
25 | __result = true;
26 | }
27 | else
28 | {
29 | var isUtageProperty = Traverse.Create(typeof(GameManager)).Property("IsUtage");
30 | __result = !isUtageProperty.PropertyExists() || !isUtageProperty.GetValue();
31 | }
32 | return false;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSystem/README.md:
--------------------------------------------------------------------------------
1 | # GameSystem
2 |
3 | Patches changing the way the game running / behaving which are not possible in the stock game. See also the [GameSettings README](../GameSettings/README.md) for differences.
4 |
5 | Game asset related patches should go to the Assets subcategory (or the Fancy category if they're too fancy).
6 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSystem/RemoveEncryption.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Reflection;
4 | using AquaMai.Config.Attributes;
5 | using AquaMai.Core.Helpers;
6 | using HarmonyLib;
7 | using Net.Packet;
8 |
9 | namespace AquaMai.Mods.GameSystem;
10 |
11 | [ConfigSection(
12 | en: """
13 | If you are using an unmodified client, requests to the server will be encrypted by default, but requests to the private server should not be encrypted.
14 | With this option enabled, the connection will not be encrypted, and the suffix added by different versions of the client to the API names are also removed.
15 | Please keep this option enabled normally.
16 | """,
17 | zh: """
18 | 如果你在用未经修改的客户端,会默认加密到服务器的连接,而连接私服的时候不应该加密
19 | 开了这个选项之后就不会加密连接了,同时也会移除不同版本的客户端可能会对 API 接口加的后缀
20 | 正常情况下,请保持这个选项开启
21 | """,
22 | defaultOn: true)]
23 | public class RemoveEncryption
24 | {
25 | [HarmonyPrefix]
26 | [HarmonyPatch(typeof(Packet), "Obfuscator", typeof(string))]
27 | public static bool PreObfuscator(string srcStr, ref string __result)
28 | {
29 | __result = Shim.RemoveApiSuffix(srcStr);
30 | return false;
31 | }
32 |
33 | [HarmonyPatch]
34 | public class EncryptDecrypt
35 | {
36 | public static IEnumerable TargetMethods()
37 | {
38 | var methods = AccessTools.TypeByName("Net.CipherAES").GetMethods();
39 | return
40 | [
41 | methods.FirstOrDefault(it => it.Name == "Encrypt" && it.IsPublic),
42 | methods.FirstOrDefault(it => it.Name == "Decrypt" && it.IsPublic)
43 | ];
44 | }
45 |
46 | public static bool Prefix(object[] __args, ref object __result)
47 | {
48 | if (__args.Length == 1)
49 | {
50 | // public static byte[] Encrypt(byte[] data)
51 | // public static byte[] Decrypt(byte[] encryptData)
52 | __result = __args[0];
53 | }
54 | else if (__args.Length == 2)
55 | {
56 | // public static bool Encrypt(byte[] data, out byte[] encryptData)
57 | // public static bool Decrypt(byte[] encryptData, out byte[] plainData)
58 | __args[1] = __args[0];
59 | __result = true;
60 | }
61 | return false;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSystem/Sound.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 | using Manager;
5 |
6 | namespace AquaMai.Mods.GameSystem;
7 |
8 | [ConfigSection(
9 | zh: "音频独占与八声道设置",
10 | en: "Audio Exclusive and 8-Channel Settings")]
11 | public static class Sound
12 | {
13 | [ConfigEntry(
14 | zh: "是否启用音频独占",
15 | en: "Enable Audio Exclusive")]
16 | private readonly static bool enableExclusive = false;
17 |
18 | [ConfigEntry(
19 | zh: "是否启用八声道",
20 | en: "Enable 8-Channel")]
21 | private readonly static bool enable8Channel = false;
22 |
23 | private static CriAtomUserExtension.AudioClientShareMode AudioShareMode => enableExclusive ? CriAtomUserExtension.AudioClientShareMode.Exclusive : CriAtomUserExtension.AudioClientShareMode.Shared;
24 |
25 | private const ushort wBitsPerSample = 32;
26 | private const uint nSamplesPerSec = 48000u;
27 | private static ushort nChannels => enable8Channel ? (ushort)8 : (ushort)2;
28 | private static ushort nBlockAlign => (ushort)(wBitsPerSample / 8 * nChannels);
29 | private static uint nAvgBytesPerSec => nSamplesPerSec * nBlockAlign;
30 |
31 | private static CriAtomUserExtension.WaveFormatExtensible CreateFormat() =>
32 | new()
33 | {
34 | Format = new CriAtomUserExtension.WaveFormatEx
35 | {
36 | wFormatTag = 65534,
37 | nSamplesPerSec = nSamplesPerSec,
38 | wBitsPerSample = wBitsPerSample,
39 | cbSize = 22,
40 | nChannels = nChannels,
41 | nBlockAlign = nBlockAlign,
42 | nAvgBytesPerSec = nAvgBytesPerSec
43 | },
44 | Samples = new CriAtomUserExtension.Samples
45 | {
46 | wValidBitsPerSample = 24,
47 | },
48 | dwChannelMask = enable8Channel ? 1599u : 3u,
49 | SubFormat = new Guid("00000001-0000-0010-8000-00aa00389b71")
50 | };
51 |
52 | [HarmonyPrefix]
53 | // Original typo
54 | [HarmonyPatch(typeof(WasapiExclusive), "Intialize")]
55 | public static bool InitializePrefix()
56 | {
57 | CriAtomUserExtension.SetAudioClientShareMode(AudioShareMode);
58 | CriAtomUserExtension.SetAudioBufferTime(160000uL);
59 | var format = CreateFormat();
60 | CriAtomUserExtension.SetAudioClientFormat(ref format);
61 | return false;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSystem/TestProof.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Linq;
3 | using AquaMai.Config.Attributes;
4 | using AquaMai.Config.Types;
5 | using AquaMai.Core;
6 | using AquaMai.Core.Attributes;
7 | using AquaMai.Core.Helpers;
8 | using AquaMai.Mods.Tweaks;
9 | using AquaMai.Mods.UX;
10 | using AquaMai.Mods.UX.PracticeMode;
11 | using HarmonyLib;
12 | using Manager;
13 |
14 | namespace AquaMai.Mods.GameSystem;
15 |
16 | [ConfigSection(
17 | en: """
18 | When enabled, test button must be long pressed to enter game test mode.
19 | When test button is bound to other features, this option is enabled automatically.
20 | """,
21 | zh: """
22 | 启用后,测试键必须长按才能进入游戏测试模式
23 | 当测试键被绑定到其它功能时,此选项自动开启
24 | """)]
25 | [EnableImplicitlyIf(nameof(ShouldEnableImplicitly))]
26 | public class TestProof
27 | {
28 | public static bool ShouldEnableImplicitly
29 | {
30 | get
31 | {
32 | (System.Type section, KeyCodeOrName key)[] featureKeys =
33 | [
34 | (typeof(OneKeyEntryEnd), OneKeyEntryEnd.key),
35 | (typeof(OneKeyRetrySkip), OneKeyRetrySkip.retryKey),
36 | (typeof(OneKeyRetrySkip), OneKeyRetrySkip.skipKey),
37 | (typeof(HideSelfMadeCharts), HideSelfMadeCharts.key),
38 | (typeof(PracticeMode), PracticeMode.key),
39 | (typeof(ResetTouch), ResetTouch.key),
40 | ];
41 | var keyMapEnabled = ConfigLoader.Config.GetSectionState(typeof(KeyMap)).Enabled;
42 | return featureKeys.Any(it =>
43 | // The feature is enabled and...
44 | ConfigLoader.Config.GetSectionState(it.section).Enabled &&
45 | (
46 | // and the key is test, or...
47 | it.key == KeyCodeOrName.Test ||
48 | // or the key have been mapped to the same key as test.
49 | (keyMapEnabled && it.key.ToString() == KeyMap.Test.ToString())));
50 | }
51 | }
52 |
53 | [ConfigEntry(
54 | en: "Change it to a value other than Test to enable long pressing of a specific key to enter the game test mode, so that the Test key can be fully used to implement custom functions",
55 | zh: "修改为 Test 以外的值来实现长按特定的键进入游戏测试模式,这样 Test 键就可以完全用来实现自定义功能了")]
56 | private static readonly KeyCodeOrName testKey = KeyCodeOrName.Test;
57 |
58 | [HarmonyPrefix]
59 | [HarmonyPatch(typeof(InputManager), "GetSystemInputDown")]
60 | public static bool GetSystemInputDown(ref bool __result, InputManager.SystemButtonSetting button, bool[] ___SystemButtonDown)
61 | {
62 | __result = ___SystemButtonDown[(int)button];
63 | if (button != InputManager.SystemButtonSetting.ButtonTest)
64 | return false;
65 |
66 | var stackTrace = new StackTrace(); // get call stack
67 | var stackFrames = stackTrace.GetFrames(); // get method calls (frames)
68 |
69 | if (stackFrames.Any(it => it.GetMethod().Name == "DMD"))
70 | {
71 | __result = KeyListener.GetKeyDownOrLongPress(testKey, true);
72 | }
73 |
74 | return false;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSystem/TouchPanelBaudRate.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using IO;
4 |
5 | namespace AquaMai.Mods.GameSystem;
6 |
7 | [ConfigSection(
8 | en: """
9 | Adjust the baud rate of the touch screen serial port, default value is 9600.
10 | Requires hardware support. If you are unsure, don't use it.
11 | """,
12 | zh: """
13 | 调整触摸屏串口波特率,默认值 9600
14 | 需要硬件配合,如果你不清楚你是否可以使用,请不要使用
15 | """)]
16 | public class TouchPanelBaudRate
17 | {
18 | [ConfigEntry(
19 | en: "Baud rate.",
20 | zh: "波特率")]
21 | private static readonly int baudRate = 9600;
22 |
23 | [HarmonyPatch(typeof(NewTouchPanel), "Open")]
24 | [HarmonyPrefix]
25 | private static void OpenPrefix(ref int ___BaudRate)
26 | {
27 | if (baudRate <= 0) return;
28 | ___BaudRate = baudRate;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/AquaMai.Mods/GameSystem/TouchToButtonInput.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Reflection;
3 | using AquaMai.Config.Attributes;
4 | using HarmonyLib;
5 | using Process;
6 | using static Manager.InputManager;
7 |
8 | namespace AquaMai.Mods.GameSystem;
9 |
10 | [ConfigSection(
11 | en: "Map touch actions to buttons.",
12 | zh: "映射触摸操作至实体按键")]
13 | public class TouchToButtonInput
14 | {
15 | private static bool _isPlaying = false;
16 |
17 | [HarmonyPostfix]
18 | [HarmonyPatch(typeof(GameProcess), "OnStart")]
19 | public static void OnGameProcessStart(GameProcess __instance)
20 | {
21 | _isPlaying = true;
22 | }
23 |
24 | [HarmonyPostfix]
25 | [HarmonyPatch(typeof(GameProcess), "OnRelease")]
26 | public static void OnGameProcessRelease(GameProcess __instance)
27 | {
28 | _isPlaying = false;
29 | }
30 |
31 | [HarmonyPostfix]
32 | [HarmonyPatch(typeof(Manager.InputManager), "GetButtonDown")]
33 | public static void GetButtonDown(ref bool __result, int monitorId, ButtonSetting button)
34 | {
35 | if (_isPlaying || __result) return;
36 | if (button.ToString().StartsWith("Button"))
37 | {
38 | __result = GetTouchPanelAreaDown(monitorId, (TouchPanelArea)button);
39 | }
40 | else if (button.ToString().Equals("Select"))
41 | {
42 | __result = GetTouchPanelAreaLongPush(monitorId, TouchPanelArea.C1, 500L) || GetTouchPanelAreaLongPush(monitorId, TouchPanelArea.C2, 500L);
43 | }
44 | }
45 |
46 | [HarmonyPatch]
47 | public static class GetMonitorButtonDown
48 | {
49 | public static IEnumerable TargetMethods()
50 | {
51 | yield return AccessTools.Method(typeof(Manager.InputManager), "GetMonitorButtonDown", [typeof(int).MakeByRefType(), typeof(ButtonSetting)]);
52 | }
53 |
54 | public static void Postfix(ref bool __result, ref int monitorId, ButtonSetting button)
55 | {
56 | if (_isPlaying || __result) return;
57 | for (int i = 0; i < 2; i++)
58 | {
59 | if (button.ToString().StartsWith("Button"))
60 | {
61 | __result = GetTouchPanelAreaDown(i, (TouchPanelArea)button);
62 | }
63 | else if (button.ToString().Equals("Select"))
64 | {
65 | __result = GetTouchPanelAreaLongPush(i, TouchPanelArea.C1, 500L) || GetTouchPanelAreaLongPush(i, TouchPanelArea.C2, 500L);
66 | }
67 |
68 | if (__result)
69 | {
70 | monitorId = i;
71 | break;
72 | }
73 | }
74 | }
75 | }
76 |
77 | [HarmonyPostfix]
78 | [HarmonyPatch(typeof(Manager.InputManager), "GetButtonPush")]
79 | public static void GetButtonPush(ref bool __result, int monitorId, ButtonSetting button)
80 | {
81 | if (_isPlaying || __result) return;
82 | if (button.ToString().StartsWith("Button")) __result = GetTouchPanelAreaPush(monitorId, (TouchPanelArea)button);
83 | }
84 |
85 | [HarmonyPostfix]
86 | [HarmonyPatch(typeof(Manager.InputManager), "GetButtonLongPush")]
87 | public static void GetButtonLongPush(ref bool __result, int monitorId, ButtonSetting button, long msec)
88 | {
89 | if (_isPlaying || __result) return;
90 | if (button.ToString().StartsWith("Button")) __result = GetTouchPanelAreaLongPush(monitorId, (TouchPanelArea)button, msec);
91 | }
92 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/General.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 |
3 | namespace AquaMai.Mods;
4 |
5 | // This class is for settings only. Don't patch anything here.
6 |
7 | [ConfigSection(
8 | en: "AquaMai's general settings.",
9 | zh: "AquaMai 的通用设置",
10 | alwaysEnabled: true)]
11 | public class General
12 | {
13 | [ConfigEntry(
14 | en: """
15 | Language for mod UI (en and zh supported).
16 | If empty, the system language will be used.
17 | The config file will also be saved in this language.
18 | """,
19 | zh: """
20 | Mod 界面的语言,支持 en 和 zh
21 | 如果为空,将使用系统语言
22 | 配置文件也将以此语言保存
23 | """,
24 | specialConfigEntry: SpecialConfigEntry.Locale)]
25 | public static readonly string locale = "";
26 | }
27 |
28 | // Please add/remove corresponding entries in SectionNameOrder enum when adding/removing sections.
29 | public enum SectionNameOrder
30 | {
31 | DeprecationWarning,
32 | General,
33 | Fix,
34 | GameSystem_Assets,
35 | GameSystem,
36 | GameSettings,
37 | Tweaks,
38 | Tweaks_TimeSaving,
39 | UX,
40 | Utils,
41 | Fancy,
42 | Enhancement,
43 | }
44 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Polyfills.cs:
--------------------------------------------------------------------------------
1 | namespace System.Runtime.CompilerServices
2 | {
3 | internal static class IsExternalInit {}
4 | }
5 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/IgnoreAimeServerError.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Manager;
4 |
5 | namespace AquaMai.Mods.Tweaks;
6 |
7 | [ConfigSection(
8 | en: "Prevent gray network caused by mistakenly thinking it's an AimeDB server issue.",
9 | zh: "防止因错误认为 AimeDB 服务器问题引起的灰网,建议开启")]
10 | public class IgnoreAimeServerError
11 | {
12 | [HarmonyPatch(typeof(OperationManager), "IsAliveAimeServer", MethodType.Getter)]
13 | [HarmonyPrefix]
14 | public static bool Prefix(ref bool __result)
15 | {
16 | __result = true;
17 | return false;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/LockFrameRate.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using UnityEngine;
3 |
4 | namespace AquaMai.Mods.Tweaks;
5 |
6 | [ConfigSection(
7 | en: """
8 | Force the frame rate limit to 60 FPS and disable vSync.
9 | Do not use if your game has no issues.
10 | """,
11 | zh: """
12 | 强制设置帧率上限为 60 帧并关闭垂直同步
13 | 如果你的游戏没有问题,请不要使用
14 | """)]
15 | public class LockFrameRate
16 | {
17 | [ConfigEntry(
18 | zh: "目标帧率,不建议修改。除非你知道你在做什么")]
19 | public static readonly int targetFrameRate = 60;
20 |
21 | public static void OnBeforePatch()
22 | {
23 | Application.targetFrameRate = targetFrameRate;
24 | QualitySettings.vSyncCount = 0;
25 | }
26 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/README.md:
--------------------------------------------------------------------------------
1 | # Tweaks
2 |
3 | Patches to make the game more stable, more robust and less annoying. The game is playable at all without them, but sometimes they help a lot.
4 |
5 | These patches don't change the way the game behaving, otherwise they may go to the GameSystem category.
6 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/ResetTouch.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using AquaMai.Config.Types;
3 | using AquaMai.Core.Helpers;
4 | using AquaMai.Core.Resources;
5 | using HarmonyLib;
6 | using MAI2.Util;
7 | using Main;
8 | using Manager;
9 | using Process;
10 |
11 | namespace AquaMai.Mods.Tweaks;
12 |
13 | [ConfigSection(
14 | en: "Reset touch panel manually or after playing track.",
15 | zh: "重置触摸面板")]
16 | public class ResetTouch
17 | {
18 | [ConfigEntry(en: "Reset touch panel after playing track.", zh: "玩完一首歌自动重置")]
19 | private static bool afterTrack = false;
20 |
21 | [ConfigEntry(en: "Reset manually.", zh: "按键重置")]
22 | public static readonly KeyCodeOrName key = KeyCodeOrName.None;
23 |
24 | [ConfigEntry] private static readonly bool longPress = false;
25 |
26 | [HarmonyPostfix]
27 | [HarmonyPatch(typeof(ResultProcess), "OnStart")]
28 | public static void ResultProcessOnStart()
29 | {
30 | if (!afterTrack) return;
31 | SingletonStateMachine.Instance.StartTouchPanel();
32 | MelonLoader.MelonLogger.Msg("[TouchResetAfterTrack] Touch panel reset");
33 | }
34 |
35 | [HarmonyPostfix]
36 | [HarmonyPatch(typeof(GameMainObject), "Update")]
37 | public static void OnGameMainObjectUpdate()
38 | {
39 | if (!KeyListener.GetKeyDownOrLongPress(key, longPress)) return;
40 | SingletonStateMachine.Instance.StartTouchPanel();
41 | MessageHelper.ShowMessage(Locale.TouchPanelReset);
42 | }
43 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/SkipUserVersionCheck.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Process.Entry.State;
4 |
5 | namespace AquaMai.Mods.Tweaks;
6 |
7 | [ConfigSection(
8 | en: "Allow login with higher data version.",
9 | zh: """
10 | 原先如果你的账号版本比当前游戏设定的版本高的话,就会不能登录
11 | 开了这个选项之后就可以登录了,不过你的账号版本还是会被设定为当前游戏的版本
12 | """)]
13 | public class SkipUserVersionCheck
14 | {
15 | [HarmonyPrefix]
16 | [HarmonyPatch(typeof(ConfirmPlay), "IsValidVersion")]
17 | public static bool IsValidVersion(ref bool __result)
18 | {
19 | __result = true;
20 | return false;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/TimeSaving/EntryToMusicSelection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using AquaMai.Config.Attributes;
3 | using AquaMai.Core.Helpers;
4 | using DB;
5 | using HarmonyLib;
6 | using MAI2.Util;
7 | using Manager;
8 | using Monitor;
9 | using Monitor.ModeSelect;
10 | using Process;
11 | using Process.Information;
12 |
13 | namespace AquaMai.Mods.Tweaks.TimeSaving;
14 |
15 | [ConfigSection(
16 | en: "Directly enter the song selection screen after login.",
17 | zh: "登录完成后直接进入选歌界面")]
18 | public class EntryToMusicSelection
19 | {
20 | /*
21 | * Highly experimental, may well break some stuff
22 | * Works by overriding the info screen (where it shows new events and stuff)
23 | * to directly exit to the music selection screen, skipping character and
24 | * event selection, among others
25 | */
26 | [HarmonyPrefix]
27 | [HarmonyPatch(typeof(InformationProcess), "OnUpdate")]
28 | public static bool OnUpdate(InformationProcess __instance, ProcessDataContainer ___container)
29 | {
30 | GameManager.SetMaxTrack();
31 | SharedInstances.GameMainObject.StartCoroutine(GraduallyIncreaseHeadphoneVolumeCoroutine());
32 | ___container.processManager.AddProcess(new MusicSelectProcess(___container));
33 | ___container.processManager.ReleaseProcess(__instance);
34 | return false;
35 | }
36 |
37 | [HarmonyPrefix]
38 | [HarmonyPatch(typeof(MapResultMonitor), "Initialize")]
39 | public static void MapResultMonitorPreInitialize(int monIndex)
40 | {
41 | var userData = Singleton.Instance.GetUserData(monIndex);
42 | var index = userData.MapList.FindIndex((m) => m.ID == userData.Detail.SelectMapID);
43 | if (index >= 0) return;
44 | userData.MapList.Clear();
45 | }
46 |
47 | // Gradually increase headphone volume
48 | private static IEnumerator GraduallyIncreaseHeadphoneVolumeCoroutine()
49 | {
50 | CommonValue[] _volumeFadeIns = [null, null];
51 |
52 | for (var i = 0; i < 2; i++)
53 | {
54 | var userData = UserDataManager.Instance.GetUserData(i);
55 | if (!userData.IsEntry) continue;
56 | _volumeFadeIns[i] = new CommonValue();
57 | var value = userData.Option.HeadPhoneVolume.GetValue();
58 | if (GameManager.IsSelectContinue[i])
59 | {
60 | _volumeFadeIns[i].start = value;
61 | _volumeFadeIns[i].current = value;
62 | }
63 | else
64 | {
65 | _volumeFadeIns[i].start = 0.05f;
66 | _volumeFadeIns[i].current = 0.05f;
67 | }
68 |
69 | _volumeFadeIns[i].end = value;
70 | _volumeFadeIns[i].diff = (_volumeFadeIns[i].end - _volumeFadeIns[i].start) / 90f;
71 | }
72 |
73 | yield return null;
74 |
75 |
76 | for (var timer = 90; timer >= 0; timer--)
77 | {
78 | for (var i = 0; i < 2; i++)
79 | {
80 | if (_volumeFadeIns[i] == null) continue;
81 | if (timer == 0)
82 | {
83 | SoundManager.SetHeadPhoneVolume(i, _volumeFadeIns[i].end);
84 | }
85 | else if (!_volumeFadeIns[i].UpdateValue())
86 | {
87 | SoundManager.SetHeadPhoneVolume(i, _volumeFadeIns[i].current);
88 | }
89 | }
90 |
91 | yield return null;
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/TimeSaving/ExitToSave.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Process;
4 |
5 | namespace AquaMai.Mods.Tweaks.TimeSaving;
6 |
7 | [ConfigSection(
8 | en: "Skip uploading photos and collectibles after the game ends and log out directly.",
9 | zh: "游戏结束后跳过上传照片和收藏品直接登出")]
10 | public class ExitToSave
11 | {
12 | [HarmonyPrefix]
13 | [HarmonyPatch(typeof(PhotoEditProcess), nameof(PhotoEditProcess.OnUpdate))]
14 | public static bool SkipPhotoEditProcess(ProcessDataContainer ___container, PhotoEditProcess __instance)
15 | {
16 | ___container.processManager.AddProcess(new DataSaveProcess(___container));
17 | ___container.processManager.ReleaseProcess(__instance);
18 | return false;
19 | }
20 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/TimeSaving/IWontTapOrSlideVigorously.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Monitor;
4 |
5 | namespace AquaMai.Mods.Tweaks.TimeSaving;
6 |
7 | [ConfigSection(
8 | en: "Skip the \"Do not tap or slide vigorously\" screen, immediately proceed to the next screen once data is loaded.",
9 | zh: "跳过“不要大力拍打或滑动哦”这个界面,数据一旦加载完就立马进入下一个界面")]
10 | public class IWontTapOrSlideVigorously
11 | {
12 | [HarmonyPrefix]
13 | [HarmonyPatch(typeof(PlInformationMonitor), "IsPlayPlInfoEnd")]
14 | public static bool Patch(ref bool __result)
15 | {
16 | __result = true;
17 | return false;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/TimeSaving/SkipEventInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 | using Process;
5 | using Process.Information;
6 |
7 | namespace AquaMai.Mods.Tweaks.TimeSaving;
8 |
9 | [ConfigSection(
10 | en: "Skip possible prompts like \"New area discovered\", \"New songs added\", \"There are events\" during game login/registration.",
11 | zh: "跳过登录 / 注册游戏时候可能的 “发现了新的区域哟” “乐曲增加” “有活动哟” 之类的提示")]
12 | public class SkipEventInfo
13 | {
14 | [HarmonyPostfix]
15 | [HarmonyPatch(typeof(InformationProcess), "OnStart")]
16 | public static void InformationProcessPostStart(ref uint ____state)
17 | {
18 | ____state = 3;
19 | }
20 |
21 | [HarmonyPostfix]
22 | [HarmonyPatch(typeof(RegionalSelectProcess), "OnStart")]
23 | public static void RegionalSelectProcessPreStart(ref Queue[] ____discoverList)
24 | {
25 | ____discoverList = new Queue[] { new Queue(), new Queue() };
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/TimeSaving/SkipGoodbyeScreen.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Monitor;
4 | using Process;
5 |
6 | namespace AquaMai.Mods.Tweaks.TimeSaving;
7 |
8 | [ConfigSection(
9 | en: "Skip the \"Goodbye\" screen at the end of the game.",
10 | zh: "跳过游戏结束的「再见」界面")]
11 | public class SkipGoodbyeScreen
12 | {
13 | [HarmonyPrefix]
14 | [HarmonyPatch(typeof(GameOverMonitor), "IsPlayEnd")]
15 | public static bool GameOverMonitorPlayEnd(ref bool __result)
16 | {
17 | __result = true;
18 | return false;
19 | }
20 |
21 | [HarmonyPrefix]
22 | [HarmonyPatch(typeof(GameOverProcess), "OnUpdate")]
23 | public static void GameOverProcessOnUpdate(ref GameOverProcess.GameOverSequence ____state)
24 | {
25 | if (____state == GameOverProcess.GameOverSequence.SkyChange)
26 | {
27 | ____state = GameOverProcess.GameOverSequence.Disp;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/TimeSaving/SkipStartupDelays.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 | using Process;
5 |
6 | namespace AquaMai.Mods.Tweaks.TimeSaving;
7 |
8 | [ConfigSection(
9 | en: "Skip useless 2s delays to speed up the game boot process.",
10 | zh: """
11 | 在自检界面,每个屏幕结束的时候都会等两秒才进入下一个屏幕,很浪费时间
12 | 开了这个选项之后就不会等了
13 | """)]
14 | public class SkipStartupDelays
15 | {
16 | [HarmonyPrefix]
17 | [HarmonyPatch(typeof(PowerOnProcess), "OnStart")]
18 | public static void PrePowerOnStart(ref float ____waitTime)
19 | {
20 | ____waitTime = 0f;
21 | }
22 |
23 | [HarmonyPrefix]
24 | [HarmonyPatch(typeof(StartupProcess), "OnUpdate")]
25 | public static void PreStartupUpdate(byte ____state, ref Stopwatch ___timer)
26 | {
27 | if (____state == 8)
28 | {
29 | Traverse.Create(___timer).Field("elapsed").SetValue(2 * 10000000L);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/TimeSaving/SkipStartupWarning.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Monitor;
4 |
5 | namespace AquaMai.Mods.Tweaks.TimeSaving;
6 |
7 | [ConfigSection(
8 | en: "Skip SDEZ's warning screen and logo shown after the POST sequence.",
9 | zh: "跳过 SDEZ 启动时的 WARNING 界面")]
10 | public class SkipStartupWarning
11 | {
12 | /*
13 | * Patch PlayLogo to disable the warning screen
14 | */
15 | [HarmonyPrefix]
16 | [HarmonyPatch(typeof (WarningMonitor), "PlayLogo")]
17 | public static bool PlayLogo()
18 | {
19 | // Return false to block the original method
20 | return false;
21 | }
22 |
23 | [HarmonyPrefix]
24 | [HarmonyPatch(typeof (WarningMonitor), "IsLogoAnimationEnd")]
25 | public static bool IsLogoAnimationEnd(ref bool __result)
26 | {
27 | // Always return true to indicate the animation has ended
28 | __result = true;
29 | return false;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Tweaks/TimeSaving/SkipTrackStart.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using Manager;
4 | using Process;
5 |
6 | namespace AquaMai.Mods.Tweaks.TimeSaving;
7 |
8 | [ConfigSection(
9 | en: "Skip TrackStart screen.",
10 | zh: "跳过乐曲开始界面")]
11 | public class SkipTrackStart
12 | {
13 | [HarmonyPostfix]
14 | [HarmonyPatch(typeof (TrackStartProcess), "OnStart")]
15 | public static void OnStart(ref TrackStartProcess.TrackStartSequence ____state)
16 | {
17 | ____state = TrackStartProcess.TrackStartSequence.DispEnd;
18 | }
19 |
20 | [HarmonyPrefix]
21 | [HarmonyPatch(typeof (MusicSelectProcess), "GameStart")]
22 | public static bool GameStart(MusicSelectProcess __instance, ProcessDataContainer ___container)
23 | {
24 | ___container.processManager.AddProcess(new TrackStartProcess(___container), 50);
25 | ___container.processManager.ReleaseProcess(__instance);
26 | SoundManager.PreviewEnd();
27 | return false;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Types/ConditionalMessage.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using AquaMai.Core.Helpers;
3 | using JetBrains.Annotations;
4 |
5 | namespace AquaMai.Mods.Types;
6 |
7 | public abstract class ConditionalMessage
8 | {
9 | public string[] locales = [];
10 | [CanBeNull] public string minimumAquaMaiVersion = null;
11 | [CanBeNull] public string maximumAquaMaiVersion = null;
12 | public int minimumGameVersion = 0;
13 | public int maximumGameVersion = 0;
14 |
15 | public bool ShouldShow()
16 | {
17 | if (locales != null && locales.Length != 0 && !locales.Contains(General.locale))
18 | {
19 | return false;
20 | }
21 |
22 | var aquaMaiVersion = new System.Version(Core.BuildInfo.Version);
23 | if (minimumAquaMaiVersion != null && aquaMaiVersion < new System.Version(minimumAquaMaiVersion))
24 | {
25 | return false;
26 | }
27 |
28 | if (maximumAquaMaiVersion != null && aquaMaiVersion > new System.Version(maximumAquaMaiVersion))
29 | {
30 | return false;
31 | }
32 |
33 | var gameVersion = GameInfo.GameVersion;
34 | if (minimumGameVersion != 0 && gameVersion < minimumGameVersion)
35 | {
36 | return false;
37 | }
38 |
39 | if (maximumGameVersion != 0 && gameVersion > maximumGameVersion)
40 | {
41 | return false;
42 | }
43 |
44 | return true;
45 | }
46 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/UX/DisableLightOutGame.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using HarmonyLib;
3 | using IO;
4 | using Mecha;
5 | using MelonLoader;
6 | using Monitor;
7 | using Process;
8 |
9 | namespace AquaMai.Mods.UX;
10 |
11 | [ConfigSection(
12 | en: "Disable button LED when not playing",
13 | zh: """
14 | 在游戏闲置时关闭外键和框体的灯光
15 | “一闪一闪的 闪的我心发慌”
16 | """)]
17 | public static class DisableLightOutGame
18 | {
19 | [HarmonyPrefix]
20 | [HarmonyPatch(typeof(Bd15070_4IF), nameof(Bd15070_4IF.SetColorFetAutoFade))]
21 | public static bool SetColorFetAutoFadePrefix()
22 | {
23 | #if DEBUG
24 | MelonLogger.Msg("[DisableLightOutGame] 拦截 Bd15070_4IF.SetColorFetAutoFade");
25 | #endif
26 | return false;
27 | }
28 |
29 | [HarmonyPrefix]
30 | [HarmonyPatch(typeof(AdvDemoMonitor), "BeatUpdate")]
31 | public static bool BeatUpdate()
32 | {
33 | #if DEBUG
34 | MelonLogger.Msg("[DisableLightOutGame] 拦截 AdvDemoMonitor.BeatUpdate");
35 | #endif
36 | return false;
37 | }
38 |
39 | [HarmonyPostfix]
40 | [HarmonyPatch(typeof(AdvDemoProcess), "OnStart")]
41 | public static void AdvDemoProcessStart()
42 | {
43 | MechaManager.SetAllCuOff();
44 | }
45 |
46 | [HarmonyPostfix]
47 | [HarmonyPatch(typeof(AdvertiseProcess), "OnStart")]
48 | public static void AdvertiseProcessStart()
49 | {
50 | MechaManager.SetAllCuOff();
51 | }
52 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/UX/NoAmDaemonAlert.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using AquaMai.Config.Attributes;
3 | using AquaMai.Core.Resources;
4 | using HarmonyLib;
5 | using Main;
6 | using UnityEngine;
7 |
8 | namespace AquaMai.Mods.UX;
9 |
10 | [ConfigSection(
11 | defaultOn: true,
12 | zh: "因 AmDaemon 未运行而黑屏时显示警告")]
13 | public class NoAmDaemonAlert : MonoBehaviour
14 | {
15 | private static NoAmDaemonAlert display;
16 | private Stopwatch stopwatch = new Stopwatch();
17 |
18 | [HarmonyPostfix]
19 | [HarmonyPatch(typeof(GameMainObject), "Awake")]
20 | public static void OnGameMainObjectAwake()
21 | {
22 | var go = new GameObject("黑屏提示组件");
23 | display = go.AddComponent();
24 | }
25 |
26 | [HarmonyPrefix]
27 | [HarmonyPatch(typeof(Main.GameMain), "LateInitialize", typeof(MonoBehaviour), typeof(Transform), typeof(Transform))]
28 | public static void LateInitialize(MonoBehaviour gameMainObject)
29 | {
30 | if (display == null) return;
31 | Destroy(display);
32 | }
33 |
34 | private void Start()
35 | {
36 | stopwatch.Start();
37 | }
38 |
39 | private void OnGUI()
40 | {
41 | if (stopwatch.ElapsedMilliseconds < 2000)
42 | {
43 | return;
44 | }
45 | GUIStyle styleTitle = new GUIStyle(GUI.skin.label)
46 | {
47 | fontSize = 35,
48 | alignment = TextAnchor.MiddleCenter,
49 | wordWrap = true
50 | };
51 | GUIStyle style = new GUIStyle(GUI.skin.label)
52 | {
53 | fontSize = 25,
54 | alignment = TextAnchor.MiddleLeft,
55 | wordWrap = true
56 | };
57 |
58 | GUI.Label(new Rect(50, 50, Screen.width - 50, Screen.height - 500), Locale.NoAmDaemonAlertTitle, styleTitle);
59 | GUI.Label(new Rect(50, 50, Screen.width - 50, Screen.height - 50), Locale.NoAmDaemonAlertMessage, style);
60 | }
61 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/UX/OneKeyRetrySkip.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using AquaMai.Config.Types;
3 | using AquaMai.Core.Helpers;
4 | using HarmonyLib;
5 | using MAI2.Util;
6 | using Manager;
7 | using MelonLoader;
8 | using Process;
9 |
10 | namespace AquaMai.Mods.UX;
11 |
12 | [ConfigSection(
13 | en: "One key to retry (1.30+) or skip current chart in gameplay.",
14 | zh: "在游戏中途一键重试(1.30+)或跳过当前谱面")]
15 | public class OneKeyRetrySkip
16 | {
17 | [ConfigEntry]
18 | public static readonly KeyCodeOrName retryKey = KeyCodeOrName.Service;
19 |
20 | [ConfigEntry]
21 | public static readonly bool retryLongPress = false;
22 |
23 | [ConfigEntry]
24 | public static readonly KeyCodeOrName skipKey = KeyCodeOrName.Service;
25 |
26 | [ConfigEntry]
27 | public static readonly bool skipLongPress = true;
28 |
29 | private static bool dirty = false;
30 |
31 | [HarmonyPostfix]
32 | [HarmonyPatch(typeof(GameProcess), "OnStart")]
33 | public static void PostGameProcessStart()
34 | {
35 | #if DEBUG
36 | MelonLogger.Msg("[OneKeyRetrySkip] Dirty flag reset");
37 | #endif
38 | dirty = false;
39 | }
40 |
41 | [HarmonyPostfix]
42 | [HarmonyPatch(typeof(GameProcess), "OnUpdate")]
43 | public static void PostGameProcessUpdate(GameProcess __instance, Message[] ____message, ProcessDataContainer ___container)
44 | {
45 | if (dirty) return;
46 |
47 | if (KeyListener.GetKeyDownOrLongPress(skipKey, skipLongPress))
48 | {
49 | #if DEBUG
50 | MelonLogger.Msg("[OneKeyRetrySkip] Skip key pressed.");
51 | #endif
52 | dirty = true;
53 | var traverse = Traverse.Create(__instance);
54 | ___container.processManager.SendMessage(____message[0]);
55 | Singleton.Instance.SetSyncResult(0);
56 | traverse.Method("SetRelease").GetValue();
57 | }
58 |
59 | else if (KeyListener.GetKeyDownOrLongPress(retryKey, retryLongPress) && GameInfo.GameVersion >= 23000)
60 | {
61 | #if DEBUG
62 | MelonLogger.Msg("[OneKeyRetrySkip] Retry key pressed.");
63 | #endif
64 | dirty = true;
65 | // This is original typo in Assembly-CSharp
66 | Singleton.Instance.SetQuickRetryFrag(flag: true);
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/UX/README.md:
--------------------------------------------------------------------------------
1 | # UX
2 |
3 | Features aiming at improving the user experience and can't fit in other categories go here. Generally, UX features are triggered by some user action or let the user perceive.
4 |
--------------------------------------------------------------------------------
/AquaMai.Mods/UX/TestModeHook.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using AquaMai.Core;
3 | using DB;
4 | using HarmonyLib;
5 |
6 | namespace AquaMai.Mods.UX;
7 |
8 | [ConfigSection(defaultOn: true, exampleHidden: true)]
9 | public static class TestModeHook
10 | {
11 | [HarmonyPostfix]
12 | [HarmonyPatch(typeof(TestmodeRootTableRecord), MethodType.Constructor, typeof(int), typeof(string), typeof(string), typeof(string))]
13 | public static void ShowAquaMaiVersion(int EnumValue, ref string ___Name)
14 | {
15 | if (EnumValue != 0) return;
16 | ___Name += $"\nAquaMai {BuildInfo.GitVersion}";
17 | }
18 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/Utils/AntiLag.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 | using Main;
5 | using MelonLoader;
6 | using UnityEngine;
7 |
8 | namespace AquaMai.Mods.Utils;
9 |
10 | [ConfigSection(
11 | en: "Some tricks to prevent the system from lagging",
12 | zh: "奇妙的防掉帧,如果你有莫名其妙的掉帧,可以试试这个")]
13 | public class AntiLag : MonoBehaviour
14 | {
15 | [ConfigEntry(zh: "游戏未取得焦点时也运行")]
16 | private static readonly bool activateWhileBackground = false;
17 |
18 | [HarmonyPostfix]
19 | [HarmonyPatch(typeof(GameMainObject), "Awake")]
20 | public static void OnGameMainObjectAwake()
21 | {
22 | var go = new GameObject("妙妙防掉帧");
23 | go.AddComponent();
24 | }
25 |
26 | private void Awake()
27 | {
28 | InvokeRepeating(nameof(OnTimer), 10f, 10f);
29 | }
30 |
31 | [DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)]
32 | private static extern void keybd_event(uint bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
33 |
34 | const int KEYEVENTF_KEYDOWN = 0x0000;
35 | const int KEYEVENTF_KEYUP = 0x0002;
36 | const int CTRL = 17;
37 |
38 | private void OnTimer()
39 | {
40 | if (!Application.isFocused && !activateWhileBackground) return;
41 | #if DEBUG
42 | MelonLogger.Msg("[AntiLag] Trigger");
43 | #endif
44 | keybd_event(CTRL, 0, KEYEVENTF_KEYDOWN, 0);
45 | keybd_event(CTRL, 0, KEYEVENTF_KEYUP, 0);
46 | }
47 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/Utils/DisplayFrameRate.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using AquaMai.Core.Helpers;
3 | using HarmonyLib;
4 | using Main;
5 | using UnityEngine;
6 |
7 | namespace AquaMai.Mods.Utils;
8 |
9 | [ConfigSection(
10 | en: "Display framerate.",
11 | zh: "显示帧率")]
12 | public class DisplayFrameRate
13 | {
14 | [HarmonyPatch(typeof(GameMainObject), "Awake")]
15 | [HarmonyPostfix]
16 | public static void ShowUi(GameMainObject __instance)
17 | {
18 | __instance.gameObject.AddComponent();
19 | }
20 |
21 | private class Ui : MonoBehaviour
22 | {
23 | private static float sampleTime = 1f;
24 | private static int frame;
25 | private static float time;
26 | private static float fps;
27 |
28 | public void OnGUI()
29 | {
30 | var labelStyle = GUI.skin.GetStyle("label");
31 | labelStyle.fontSize = GuiSizes.FontSize;
32 | labelStyle.alignment = TextAnchor.MiddleCenter;
33 |
34 | const float x = 10f;
35 | const float y = 10f;
36 | var width = GuiSizes.FontSize * 7f;
37 | var height = GuiSizes.LabelHeight * 1.5f;
38 |
39 | frame += 1;
40 | time += Time.deltaTime;
41 |
42 | if (time >= sampleTime)
43 | {
44 | fps = frame / time;
45 | frame = 0;
46 | time = 0;
47 | }
48 |
49 | GUI.Box(new Rect(x, y, width, height), "");
50 | GUI.Label(new Rect(x, y, width, height), $"{fps:0.0} FPS");
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Utils/LogNetworkErrors.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 | using Manager;
5 | using Manager.Operation;
6 | using MelonLoader;
7 | using Net.Packet;
8 |
9 | namespace AquaMai.Mods.Utils;
10 |
11 | [ConfigSection(exampleHidden: true, defaultOn: true)]
12 | public class LogNetworkErrors
13 | {
14 | [HarmonyPostfix]
15 | [HarmonyPatch(typeof(Packet), "ProcImpl")]
16 | public static void Postfix(PacketState __result, Packet __instance)
17 | {
18 | if (__result == PacketState.Error)
19 | {
20 | MelonLogger.Msg($"[LogNetworkErrors] {__instance.Query.Api}: {__instance.Status}");
21 | }
22 | }
23 |
24 | [HarmonyPostfix]
25 | [HarmonyPatch(typeof(DataDownloader), "NotifyOffline")]
26 | public static void DataDownloader()
27 | {
28 | MelonLogger.Msg("[LogNetworkErrors] DataDownloader NotifyOffline");
29 | var stackTrace = new StackTrace();
30 | MelonLogger.Msg(stackTrace.ToString());
31 | }
32 |
33 | [HarmonyPostfix]
34 | [HarmonyPatch(typeof(OnlineCheckInterval), "NotifyOffline")]
35 | public static void OnlineCheckInterval()
36 | {
37 | MelonLogger.Msg("[LogNetworkErrors] OnlineCheckInterval NotifyOffline");
38 | var stackTrace = new StackTrace();
39 | MelonLogger.Msg(stackTrace.ToString());
40 | }
41 |
42 | [HarmonyPostfix]
43 | [HarmonyPatch(typeof(OperationManager), "IsAliveAimeServer", MethodType.Getter)]
44 | public static void IsAliveAimeServer(bool __result)
45 | {
46 | if (__result == false)
47 | MelonLogger.Msg($"[LogNetworkErrors] IsAliveAimeServer Is {__result}");
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Utils/LogUnity.cs:
--------------------------------------------------------------------------------
1 | using AquaMai.Config.Attributes;
2 | using MelonLoader;
3 | using UnityEngine;
4 |
5 | namespace AquaMai.Mods.Utils;
6 |
7 | [ConfigSection(
8 | en: "Output Unity logs (for debugging purposes)",
9 | zh: "输出 Unity 日志(调试用)",
10 | exampleHidden: true)]
11 | public static class LogUnity
12 | {
13 | public static void OnBeforePatch()
14 | {
15 | Application.logMessageReceived += Log;
16 | }
17 |
18 | private static void Log(string msg, string stackTrace, LogType type)
19 | {
20 | MelonLogger.Msg("[Unity] " + msg);
21 | MelonLogger.Msg("[Unity] " + stackTrace);
22 | }
23 | }
--------------------------------------------------------------------------------
/AquaMai.Mods/Utils/LogUserId.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AquaMai.Config.Attributes;
3 | using HarmonyLib;
4 | using MelonLoader;
5 | using Net.Packet;
6 | using Net.Packet.Mai2;
7 | using Net.VO.Mai2;
8 |
9 | namespace AquaMai.Mods.Utils;
10 |
11 | [ConfigSection(
12 | en: "Log user ID on login.",
13 | zh: "登录时将 UserID 输出到日志")]
14 | public class LogUserId
15 | {
16 | [HarmonyPostfix]
17 | [HarmonyPatch(typeof(PacketGetUserPreview), MethodType.Constructor, typeof(ulong), typeof(string), typeof(Action), typeof(Action))]
18 | public static void Postfix(ulong userId)
19 | {
20 | MelonLogger.Msg($"[LogUserId] UserLogin: {userId}");
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Utils/README.md:
--------------------------------------------------------------------------------
1 | # Utils
2 |
3 | Debugging or diagnostic purpose utilities goes here. They don't affect the game play at all, but may show UIs if needed.
4 |
--------------------------------------------------------------------------------
/AquaMai.Mods/Utils/ShowNetErrorDetail.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using AquaMai.Config.Attributes;
3 | using AquaMai.Core.Helpers;
4 | using AquaMai.Core.Resources;
5 | using HarmonyLib;
6 | using MAI2.Util;
7 | using Manager;
8 | using Monitor;
9 | using Process;
10 | using UnityEngine;
11 |
12 | namespace AquaMai.Mods.Utils;
13 |
14 | [ConfigSection(
15 | en: "Show Network error detail in the game when gray network icon appears.",
16 | zh: "出现灰网时显示原因")]
17 | public class ShowNetErrorDetail
18 | {
19 | [HarmonyPatch(typeof(CommonProcess), "OnStart")]
20 | [HarmonyPostfix]
21 | public static void SetIconStatus(CommonMonitor[] ____monitors)
22 | {
23 | ____monitors[0].gameObject.AddComponent();
24 | }
25 |
26 | private class DetailUi : MonoBehaviour
27 | {
28 | public void OnGUI()
29 | {
30 | var errors = new List();
31 | if (!Singleton.Instance.IsAliveServer)
32 | {
33 | errors.Add(Locale.NetErrIsAliveServer);
34 | }
35 |
36 | if (!Singleton.Instance.IsAliveAimeReader)
37 | {
38 | errors.Add(Locale.NetErrIsAliveAimeReader);
39 | }
40 |
41 | if (!Singleton.Instance.IsAliveAimeServer)
42 | {
43 | errors.Add(Locale.NetErrIsAliveAimeServer);
44 | }
45 |
46 | if (!Singleton.Instance.WasDownloadSuccessOnce)
47 | {
48 | errors.Add(Locale.NetErrWasDownloadSuccessOnce);
49 | }
50 |
51 | if (errors.Count == 0)
52 | {
53 | return;
54 | }
55 |
56 | var labelStyle = GUI.skin.GetStyle("label");
57 | labelStyle.fontSize = GuiSizes.FontSize;
58 | labelStyle.alignment = TextAnchor.MiddleCenter;
59 |
60 | var x = GuiSizes.PlayerCenter + GuiSizes.PlayerWidth * .2f;
61 | var y = Screen.height * .01f;
62 | var width = GuiSizes.FontSize * 15f;
63 | var height = GuiSizes.LabelHeight * errors.Count + GuiSizes.Margin * 2;
64 |
65 | GUI.Box(new Rect(x, y, width, height), "");
66 | for (var i = 0; i < errors.Count; i++)
67 | {
68 | GUI.Label(new Rect(x, y + GuiSizes.Margin + GuiSizes.LabelHeight * i, width, GuiSizes.LabelHeight), errors[i]);
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/AquaMai/AssemblyLoader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.IO.Compression;
5 | using System.Reflection;
6 |
7 | namespace AquaMai;
8 |
9 | public static class AssemblyLoader
10 | {
11 | public enum AssemblyName
12 | {
13 | ConfigInterfaces,
14 | Config,
15 | Core,
16 | Mods,
17 | }
18 |
19 | private static readonly Dictionary Assemblies = new()
20 | {
21 | [AssemblyName.ConfigInterfaces] = "AquaMai.Config.Interfaces.dll",
22 | [AssemblyName.Config] = "AquaMai.Config.dll",
23 | [AssemblyName.Core] = "AquaMai.Core.dll",
24 | [AssemblyName.Mods] = "AquaMai.Mods.dll",
25 | };
26 |
27 | private static readonly Dictionary LoadedAssemblies = [];
28 |
29 | public static Assembly GetAssembly(AssemblyName assemblyName) => LoadedAssemblies[assemblyName];
30 |
31 | public static void LoadAssemblies()
32 | {
33 | foreach (var (assemblyName, assemblyFileName) in Assemblies)
34 | {
35 | # if DEBUG
36 | MelonLoader.MelonLogger.Msg($"Loading assembly \"{assemblyFileName}\"...");
37 | # endif
38 | LoadedAssemblies[assemblyName] = LoadAssemblyFromResource(assemblyFileName);
39 | }
40 | }
41 |
42 | public static Assembly LoadAssemblyFromResource(string assemblyName)
43 | {
44 | var executingAssembly = Assembly.GetExecutingAssembly();
45 | using var decompressedStream = executingAssembly.GetManifestResourceStream(assemblyName);
46 | if (decompressedStream != null)
47 | {
48 | return AppDomain.CurrentDomain.Load(StreamToBytes(decompressedStream));
49 | }
50 | using var compressedStream = executingAssembly.GetManifestResourceStream($"{assemblyName}.compressed");
51 | if (compressedStream != null)
52 | {
53 | return AppDomain.CurrentDomain.Load(DecompressToBytes(compressedStream));
54 | }
55 | throw new Exception($"Embedded assembly \"{assemblyName}\" not found.");
56 | }
57 |
58 | private static byte[] StreamToBytes(Stream stream)
59 | {
60 | if (stream == null)
61 | {
62 | return [];
63 | }
64 | using var memoryStream = new MemoryStream();
65 | stream.CopyTo(memoryStream);
66 | return memoryStream.ToArray();
67 | }
68 |
69 | private static byte[] DecompressToBytes(Stream stream) => StreamToBytes(new DeflateStream(stream, CompressionMode.Decompress));
70 | }
71 |
--------------------------------------------------------------------------------
/AquaMai/BuildInfo.cs:
--------------------------------------------------------------------------------
1 | namespace AquaMai;
2 |
3 | public static partial class BuildInfo
4 | {
5 | public const string Name = "AquaMai";
6 | public const string Description = "Mod for Sinmai";
7 | public const string Author = "Aza ft. Clansty ft. Menci";
8 | public const string Company = null;
9 | public const string DownloadLink = null;
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/AquaMai/Main.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 | using MelonLoader;
4 |
5 | namespace AquaMai;
6 |
7 | public class AquaMai : MelonMod
8 | {
9 | public const string AQUAMAI_SAY = """
10 | 如果你在 dnSpy / ILSpy 里看到了这行字,请从 resources 中解包 DLLs。
11 | If you see this line in dnSpy / ILSpy, please unpack the DLLs from resources.
12 | """;
13 |
14 | [DllImport("kernel32.dll", SetLastError = true)]
15 | private static extern bool SetConsoleOutputCP(uint wCodePageID);
16 |
17 | private void SetCoreBuildInfo(Assembly coreAssembly)
18 | {
19 | var coreBuildInfo = coreAssembly.GetType("AquaMai.Core.BuildInfo");
20 | var buildInfo = typeof(BuildInfo);
21 | foreach (var field in buildInfo.GetFields())
22 | {
23 | coreBuildInfo.GetField(field.Name)?.SetValue(null, field.GetValue(null));
24 | }
25 | coreBuildInfo.GetField("ModAssembly")?.SetValue(null, MelonAssembly);
26 | }
27 |
28 | private static MethodInfo onGUIMethod;
29 |
30 | public override void OnInitializeMelon()
31 | {
32 | // Prevent Chinese characters from being garbled
33 | SetConsoleOutputCP(65001);
34 |
35 | AssemblyLoader.LoadAssemblies();
36 |
37 | var modsAssembly = AssemblyLoader.GetAssembly(AssemblyLoader.AssemblyName.Mods);
38 | var coreAssembly = AssemblyLoader.GetAssembly(AssemblyLoader.AssemblyName.Core);
39 | SetCoreBuildInfo(coreAssembly);
40 | coreAssembly.GetType("AquaMai.Core.Startup")
41 | .GetMethod("Initialize", BindingFlags.Public | BindingFlags.Static)
42 | .Invoke(null, [modsAssembly, HarmonyInstance]);
43 | onGUIMethod = coreAssembly.GetType("AquaMai.Core.Startup")
44 | .GetMethod("OnGUI", BindingFlags.Public | BindingFlags.Static);
45 | }
46 |
47 | public override void OnGUI()
48 | {
49 | base.OnGUI();
50 | onGUIMethod?.Invoke(null, []);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/AquaMai/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using MelonLoader;
3 |
4 | [assembly: AssemblyTitle(AquaMai.BuildInfo.Description)]
5 | [assembly: AssemblyDescription(AquaMai.BuildInfo.Description)]
6 | [assembly: AssemblyCompany(AquaMai.BuildInfo.Company)]
7 | [assembly: AssemblyProduct(AquaMai.BuildInfo.Name)]
8 | [assembly: AssemblyCopyright("Created by " + AquaMai.BuildInfo.Author)]
9 | [assembly: AssemblyTrademark(AquaMai.BuildInfo.Company)]
10 | [assembly: AssemblyVersion(AquaMai.BuildInfo.Version)]
11 | [assembly: AssemblyFileVersion(AquaMai.BuildInfo.GitVersion)]
12 | [assembly: MelonInfo(typeof(AquaMai.AquaMai), AquaMai.BuildInfo.Name, AquaMai.BuildInfo.GitVersion, AquaMai.BuildInfo.Author, AquaMai.BuildInfo.DownloadLink)]
13 | [assembly: MelonColor()]
14 | [assembly: HarmonyDontPatchAll]
15 |
16 | // Create and Setup a MelonGame Attribute to mark a Melon as Universal or Compatible with specific Games.
17 | // If no MelonGame Attribute is found or any of the Values for any MelonGame Attribute on the Melon is null or empty it will be assumed the Melon is Universal.
18 | // Values for MelonGame Attribute can be found in the Game's app.info file or printed at the top of every log directly beneath the Unity version.
19 | [assembly: MelonGame(null, null)]
20 |
--------------------------------------------------------------------------------
/AquaMai/configSort.yaml:
--------------------------------------------------------------------------------
1 | 常用:
2 | - General
3 | - GameSystem.SinglePlayer
4 | - GameSystem.DisableTimeout
5 | - UX.ImmediateSave
6 | - GameSystem.QuickRetry
7 | - UX.OneKeyEntryEnd
8 | - UX.OneKeyRetrySkip
9 | - GameSystem.TouchToButtonInput
10 | - GameSystem.TestProof
11 | - GameSettings.CreditConfig
12 | - Fancy.GamePlay.ExtendNotesPool
13 |
14 | 直读:
15 | - GameSystem.Assets.LoadAssetBundleWithoutManifest
16 | - GameSystem.Assets.LoadLocalImages
17 | - GameSystem.Assets.MovieLoader
18 |
19 | 全解和跳过:
20 | - GameSystem.Unlock
21 | - Tweaks.TimeSaving.SkipStartupWarning
22 | - Tweaks.TimeSaving.EntryToMusicSelection
23 | - Tweaks.TimeSaving.ExitToSave
24 | - Tweaks.TimeSaving.SkipStartupDelays
25 | - Tweaks.TimeSaving.SkipEventInfo
26 | - Tweaks.TimeSaving.SkipTrackStart
27 | - Tweaks.TimeSaving.SkipGoodbyeScreen
28 | - Tweaks.TimeSaving.IWontTapOrSlideVigorously
29 | - UX.QuickEndPlay
30 |
31 | 美化:
32 | - GameSystem.Assets.Fonts
33 | - Fancy.CustomLogo
34 | - Fancy.CustomPlaceName
35 | - Fancy.CustomVersionString
36 | - Fancy.CustomCreditsString
37 | - Fancy.RandomBgm
38 | - Fancy.CustomSkins
39 | - Fancy.CustomButton
40 | - Fancy.DemoMaster
41 | - Fancy.GamePlay.AlignCircleSlideJudgeDisplay
42 | - Fancy.GamePlay.SlideArrowAnimation
43 | - Fancy.GamePlay.SlideFadeInTweak
44 | - Fancy.GamePlay.SlideLayerReverse
45 |
46 | 实用工具:
47 | - UX.HideSelfMadeCharts
48 | - UX.JudgeAccuracyInfo
49 | - UX.PracticeMode
50 | - UX.SelectionDetail
51 | - UX.DisableLightOutGame
52 | - Fancy.GamePlay.HideHanabi
53 | - Fancy.GamePlay.JudgeDisplay4B
54 | - Fancy.Triggers
55 |
56 | 键位和灵敏度:
57 | - GameSystem.HidInput
58 | - GameSystem.AdxHidInput
59 | - GameSystem.KeyMap
60 | - GameSettings.TouchSensitivity
61 |
62 | 调试:
63 | - GameSystem.Window
64 | - Utils.AntiLag
65 | - GameSettings.JudgeAdjust
66 | - Tweaks.IgnoreAimeServerError
67 | - Utils.ShowNetErrorDetail
68 | - Tweaks.LockFrameRate
69 | - Tweaks.ResetTouch
70 | - Tweaks.SkipUserVersionCheck
71 | - Utils.DisplayFrameRate
72 | - Utils.LogUserId
73 | - Utils.LogNetworkRequests
74 | - Utils.ShowErrorLog
75 | - UX.NoAmDaemonAlert
76 |
77 | 高级设置:
78 | - Fancy.HideMask
79 | - GameSettings.ForceAsServer
80 | - GameSystem.RemoveEncryption
81 | - GameSystem.Sound
82 | - GameSystem.TouchPanelBaudRate
83 | - GameSystem.TouchPanelBaudRate
84 | - GameSystem.CustomCameraId
85 | - GameSystem.OptionLoadFix
86 | - UX.ServerAnnouncement
87 |
88 | 过新过热:
89 | - Fancy.CustomTrackStartDiff
90 | - Fancy.GamePlay.BreakSlideJudgeBlink
91 | - Fancy.GamePlay.CustomNoteTypes
92 | - Fancy.GamePlay.DisableTrackStartTabs
93 | - Fancy.GamePlay.FanJudgeFlip
94 | - Fancy.GamePlay.RealisticRandomJudge
95 | - Fancy.GamePlay.TrackStartProcessTweak
96 |
--------------------------------------------------------------------------------
/Libs/.gitignore:
--------------------------------------------------------------------------------
1 | /Assembly-CSharp.dll
2 | /AMDaemon.NET.dll
3 |
--------------------------------------------------------------------------------
/Libs/0Harmony.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/0Harmony.dll
--------------------------------------------------------------------------------
/Libs/Assembly-CSharp-firstpass.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/Assembly-CSharp-firstpass.dll
--------------------------------------------------------------------------------
/Libs/MelonLoader.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/MelonLoader.dll
--------------------------------------------------------------------------------
/Libs/Mono.Cecil.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/Mono.Cecil.dll
--------------------------------------------------------------------------------
/Libs/Mono.Posix.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/Mono.Posix.dll
--------------------------------------------------------------------------------
/Libs/Mono.Security.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/Mono.Security.dll
--------------------------------------------------------------------------------
/Libs/System.Configuration.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/System.Configuration.dll
--------------------------------------------------------------------------------
/Libs/System.Core.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/System.Core.dll
--------------------------------------------------------------------------------
/Libs/System.Numerics.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/System.Numerics.dll
--------------------------------------------------------------------------------
/Libs/System.Security.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/System.Security.dll
--------------------------------------------------------------------------------
/Libs/System.Xml.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/System.Xml.dll
--------------------------------------------------------------------------------
/Libs/System.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/System.dll
--------------------------------------------------------------------------------
/Libs/Unity.Analytics.DataPrivacy.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/Unity.Analytics.DataPrivacy.dll
--------------------------------------------------------------------------------
/Libs/Unity.TextMeshPro.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/Unity.TextMeshPro.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.AIModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.AIModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.ARModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.ARModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.AccessibilityModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.AccessibilityModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.AnimationModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.AnimationModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.AssetBundleModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.AssetBundleModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.AudioModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.AudioModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.BaselibModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.BaselibModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.ClothModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.ClothModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.ClusterInputModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.ClusterInputModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.ClusterRendererModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.ClusterRendererModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.CoreModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.CoreModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.CrashReportingModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.CrashReportingModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.DirectorModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.DirectorModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.FileSystemHttpModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.FileSystemHttpModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.GameCenterModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.GameCenterModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.GridModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.GridModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.HotReloadModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.HotReloadModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.IMGUIModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.IMGUIModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.ImageConversionModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.ImageConversionModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.InputModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.InputModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.JSONSerializeModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.JSONSerializeModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.LocalizationModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.LocalizationModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.Networking.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.Networking.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.ParticleSystemModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.ParticleSystemModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.PerformanceReportingModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.PerformanceReportingModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.Physics2DModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.Physics2DModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.PhysicsModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.PhysicsModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.ProfilerModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.ProfilerModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.ScreenCaptureModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.ScreenCaptureModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.SharedInternalsModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.SharedInternalsModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.SpatialTracking.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.SpatialTracking.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.SpriteMaskModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.SpriteMaskModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.SpriteShapeModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.SpriteShapeModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.StreamingModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.StreamingModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.StyleSheetsModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.StyleSheetsModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.SubstanceModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.SubstanceModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.TLSModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.TLSModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.TerrainModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.TerrainModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.TerrainPhysicsModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.TerrainPhysicsModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.TextCoreModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.TextCoreModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.TextRenderingModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.TextRenderingModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.TilemapModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.TilemapModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.Timeline.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.Timeline.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.TimelineModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.TimelineModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UI.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UI.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UIElementsModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UIElementsModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UIModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UIModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UNETModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UNETModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UmbraModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UmbraModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UnityAnalyticsModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UnityAnalyticsModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UnityConnectModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UnityConnectModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UnityTestProtocolModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UnityTestProtocolModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UnityWebRequestAssetBundleModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UnityWebRequestAssetBundleModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UnityWebRequestAudioModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UnityWebRequestAudioModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UnityWebRequestModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UnityWebRequestModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UnityWebRequestTextureModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UnityWebRequestTextureModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.UnityWebRequestWWWModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.UnityWebRequestWWWModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.VFXModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.VFXModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.VRModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.VRModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.VehiclesModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.VehiclesModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.VideoModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.VideoModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.WindModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.WindModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.XRModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.XRModule.dll
--------------------------------------------------------------------------------
/Libs/UnityEngine.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/UnityEngine.dll
--------------------------------------------------------------------------------
/Libs/mscorlib.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MewoLab/AquaMai/e8bd6c67fd7ee5ebd5d63a2aef7d35576f5883aa/Libs/mscorlib.dll
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AquaMai
2 |
3 | ## Installation
4 |
5 | 1. Build the project or find an existing build somewhere™
6 | 2. Download [MelonLoader.x64.zip](https://github.com/LavaGang/MelonLoader/releases/download/v0.7.0/MelonLoader.x64.zip)
7 | 3. Extract MelonLoader zip to where your Sinmai.exe is
8 | 4. Make a Mods folder and put AquaMai.dll inside it
9 | 5. Pet your cat
10 | 6. Launch!
11 |
12 | ## Features
13 |
14 | **Cheats**
15 |
16 | * Unlock all tickets
17 |
18 | **UX Optimization**
19 |
20 | * Remove the starting logo and warning cutscene
21 | * Single Player (1P) mode
22 | * Skip from card scanning directly to music selection (experimental)
23 | * Disable daily automatic reboot
24 | * Customize version text
25 | * Skip the current song by holding 7
26 | * Skip "new event" and "information" screen for new players.
27 |
28 | **Bug Fixes**
29 |
30 | * Fix crash in the character selection screen
31 |
32 | **Performance**
33 |
34 | * Speed up things
35 |
36 | ## Development
37 |
38 | 1. Copy `Assembly-CSharp.dll` and `AMDaemon.NET.dll` to `Libs` folder.
39 | 1. Install [.NET Framework 4.7.2 Developer Pack](https://dotnet.microsoft.com/en-us/download/dotnet-framework/thank-you/net472-developer-pack-offline-installer)
40 | 1. Run `build.ps1`.
41 | 1. Copy `Output/AquaMai.dll` to `Mods` folder.
42 | 1. Configure and copy `AquaMai.toml` to the same folder as your game executable: `Sinmai.exe`
43 |
44 | ## Relevant Links
45 |
46 | * [MelonLoader Wiki](https://melonwiki.xyz/#/modders/quickstart)
47 | * [Harmony Docs](https://harmony.pardeike.net/articles/patching-prefix.html)
48 |
--------------------------------------------------------------------------------
/build.cake:
--------------------------------------------------------------------------------
1 | #addin nuget:?package=Cake.Git&version=5.0.1
2 | #addin nuget:?package=Cake.FileHelpers&version=7.0.0
3 |
4 | var target = Argument("target", "Build");
5 | var configuration = Argument("configuration", "Release");
6 |
7 | Task("Restore")
8 | .Does(() =>
9 | {
10 | // 运行 dotnet restore
11 | DotNetRestore("./AquaMai.sln");
12 | });
13 |
14 | Task("PreBuild")
15 | .Does(() =>
16 | {
17 | var gitDescribe = GitDescribe(".", GitDescribeStrategy.Tags).Substring(1); // 获取 git describe 的输出
18 | var buildDate = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
19 |
20 | var shortVers = gitDescribe.Split('-');
21 | string shortVer;
22 | if (shortVers.Length > 1)
23 | {
24 | shortVer = $"{shortVers[0]}.{shortVers[1]}";
25 | }
26 | else
27 | {
28 | shortVer = shortVers[0];
29 | }
30 |
31 | var versionContent = $@"
32 | // Auto-generated file. Do not modify manually.
33 | namespace AquaMai;
34 |
35 | public static partial class BuildInfo
36 | {{
37 | public const string Version = ""{shortVer}"";
38 | public const string GitVersion = ""{gitDescribe}"";
39 | public const string BuildDate = ""{buildDate}"";
40 | }}
41 | ";
42 | FileWriteText("./AquaMai/BuildInfo.g.cs", versionContent);
43 | });
44 |
45 | Task("Build")
46 | .IsDependentOn("PreBuild")
47 | .IsDependentOn("Restore")
48 | .Does(() =>
49 | {
50 | // 使用 dotnet build 进行构建
51 | DotNetBuild("./AquaMai.sln", new DotNetBuildSettings
52 | {
53 | Configuration = configuration
54 | });
55 | });
56 |
57 | RunTarget(target);
58 |
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | $ErrorActionPreference = 'Stop'
2 |
3 | Set-Location -LiteralPath $PSScriptRoot
4 |
5 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1'
6 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = '1'
7 | $env:DOTNET_NOLOGO = '1'
8 |
9 | dotnet tool restore
10 | if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
11 |
12 | dotnet cake @args
13 | if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
14 |
--------------------------------------------------------------------------------