-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1535521
Showing
15 changed files
with
991 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
|
||
bin/* | ||
obj/* | ||
*.user | ||
*.xcf | ||
HarmonyPatches/RemoveSplashscreen.cs | ||
HarmonyPatchHelper.cs | ||
Screenshots/SocialPreview.jpg |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
| ||
using System.Runtime.CompilerServices; | ||
using IPA.Config.Stores; | ||
|
||
[assembly: InternalsVisibleTo(GeneratedStore.AssemblyVisibilityTarget)] | ||
namespace OverswingCounter { | ||
internal class Configuration { | ||
public static Configuration Instance { get; set; } | ||
|
||
public virtual int averageCount { get; set; } = 12; | ||
public virtual int decimalPlaces { get; set; } = 0; | ||
public virtual bool underswingByPoints { get; set; } = true; | ||
|
||
public virtual float ignoreCutsWithNoPrecedingWithin { get; set; } = 0.6f; | ||
|
||
public virtual float targetExtraAngle { get; set; } = 13f; | ||
public virtual float lowerWarning { get; set; } = 8f; | ||
public virtual float upperWarning { get; set; } = 13f; | ||
|
||
|
||
/// <summary> | ||
/// This is called whenever BSIPA reads the config from disk (including when file changes are detected). | ||
/// </summary> | ||
public virtual void OnReload() { | ||
// Do stuff after config is read from disk. | ||
} | ||
|
||
/// <summary> | ||
/// Call this to force BSIPA to update the config file. This is also called by BSIPA if it detects the file was modified. | ||
/// </summary> | ||
public virtual void Changed() { | ||
// Do stuff when the config is changed. | ||
} | ||
|
||
/// <summary> | ||
/// Call this to have BSIPA copy the values from <paramref name="other"/> into this config. | ||
/// </summary> | ||
public virtual void CopyFrom(Configuration other) { | ||
// This instance's members populated from other | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<!-- This file contains the build tasks and targets for verifying the manifest, zipping Release builds, | ||
and copying the plugin to to your Beat Saber folder. Only edit this if you know what you are doing. --> | ||
<Project> | ||
<PropertyGroup> | ||
<BuildTargetsVersion>2.0</BuildTargetsVersion> | ||
<!--Set this to true if you edit this file to prevent automatic updates--> | ||
<BuildTargetsModified>false</BuildTargetsModified> | ||
<!--Output assembly path without extension--> | ||
<OutputAssemblyName>$(OutputPath)$(AssemblyName)</OutputAssemblyName> | ||
<!--Path to folder to be zipped. Needs to be relative to the project directory to work without changes to the 'BuildForCI' target.--> | ||
<ArtifactDestination>$(OutputPath)Final</ArtifactDestination> | ||
<ErrorOnMismatchedVersions Condition="'$(Configuration)' == 'Release'">True</ErrorOnMismatchedVersions> | ||
</PropertyGroup> | ||
<!--Build Targets--> | ||
<!--Displays a warning if BeatSaberModdingTools.Tasks is not installed.--> | ||
<Target Name="CheckBSMTInstalled" AfterTargets="BeforeBuild" Condition="'$(BSMTTaskAssembly)' == ''"> | ||
<Warning Text="The BeatSaberModdingTools.Tasks nuget package doesn't seem to be installed, advanced build targets will not work." /> | ||
</Target> | ||
<!--Runs a build task to get info about the project used by later targets.--> | ||
<Target Name="GetProjectInfo" AfterTargets="CheckBSMTInstalled" DependsOnTargets="CheckBSMTInstalled" Condition="'$(BSMTTaskAssembly)' != ''"> | ||
<Message Text="Using AssemblyVersion defined in project instead of 'Properties\AssemblyInfo.cs'" Importance="high" Condition="'$(AssemblyVersion)' != ''" /> | ||
<GetManifestInfo FailOnError="$(ErrorOnMismatchedVersions)"> | ||
<Output TaskParameter="PluginVersion" PropertyName="PluginVersion" /> | ||
<Output TaskParameter="BasePluginVersion" PropertyName="BasePluginVersion" /> | ||
<Output TaskParameter="GameVersion" PropertyName="GameVersion" /> | ||
</GetManifestInfo> | ||
<PropertyGroup> | ||
<AssemblyVersion>$(BasePluginVersion)</AssemblyVersion> | ||
<FileVersion>$(BasePluginVersion)</FileVersion> | ||
<InformationalVersion>$(BasePluginVersion)</InformationalVersion> | ||
</PropertyGroup> | ||
<GetCommitInfo ProjectDir="$(ProjectDir)"> | ||
<Output TaskParameter="CommitHash" PropertyName="CommitHash" /> | ||
<Output TaskParameter="Branch" PropertyName="Branch" /> | ||
<Output TaskParameter="Modified" PropertyName="GitModified" /> | ||
</GetCommitInfo> | ||
<PropertyGroup> | ||
<!--Build name for artifact/zip file--> | ||
<ArtifactName>$(AssemblyName)</ArtifactName> | ||
<ArtifactName Condition="'$(PluginVersion)' != ''">$(ArtifactName)-$(PluginVersion)</ArtifactName> | ||
<ArtifactName Condition="'$(GameVersion)' != ''">$(ArtifactName)-bs$(GameVersion)</ArtifactName> | ||
<ArtifactName Condition="'$(CommitHash)' != '' AND '$(CommitHash)' != 'local'">$(ArtifactName)-$(CommitHash)</ArtifactName> | ||
</PropertyGroup> | ||
</Target> | ||
<!--Build target for Continuous Integration builds. Set up for GitHub Actions.--> | ||
<Target Name="BuildForCI" AfterTargets="Build" DependsOnTargets="GetProjectInfo" Condition="'$(ContinuousIntegrationBuild)' == 'True' AND '$(BSMTTaskAssembly)' != ''"> | ||
<PropertyGroup> | ||
<!--Set 'ArtifactName' if it failed before.--> | ||
<ArtifactName Condition="'$(ArtifactName)' == ''">$(AssemblyName)</ArtifactName> | ||
</PropertyGroup> | ||
<Message Text="Building for CI" Importance="high" /> | ||
<Message Text="PluginVersion: $(PluginVersion), AssemblyVersion: $(AssemblyVersion), GameVersion: $(GameVersion)" Importance="high" /> | ||
<Message Text="::set-output name=filename::$(ArtifactName)" Importance="high" /> | ||
<Message Text="::set-output name=assemblyname::$(AssemblyName)" Importance="high" /> | ||
<Message Text="::set-output name=artifactpath::$(ProjectDir)$(ArtifactDestination)" Importance="high" /> | ||
<Message Text="Copying '$(OutputAssemblyName).dll' to '$(ProjectDir)$(ArtifactDestination)\Plugins\$(AssemblyName).dll'" Importance="high" /> | ||
<Copy SourceFiles="$(OutputAssemblyName).dll" DestinationFiles="$(ProjectDir)$(ArtifactDestination)\Plugins\$(AssemblyName).dll" /> | ||
</Target> | ||
<!--Creates a BeatMods compliant zip file with the release.--> | ||
<Target Name="ZipRelease" AfterTargets="Build" Condition="'$(DisableZipRelease)' != 'True' AND '$(Configuration)' == 'Release' AND '$(BSMTTaskAssembly)' != ''"> | ||
<PropertyGroup> | ||
<!--Set 'ArtifactName' if it failed before.--> | ||
<ArtifactName Condition="'$(ArtifactName)' == ''">$(AssemblyName)</ArtifactName> | ||
<DestinationDirectory>$(OutDir)zip\</DestinationDirectory> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<OldZips Include="$(DestinationDirectory)$(AssemblyName)*.zip"/> | ||
</ItemGroup> | ||
<Copy SourceFiles="$(OutputAssemblyName).dll" DestinationFiles="$(ArtifactDestination)\Plugins\$(AssemblyName).dll" /> | ||
<Message Text="PluginVersion: $(PluginVersion), AssemblyVersion: $(AssemblyVersion), GameVersion: $(GameVersion)" Importance="high" /> | ||
<Delete Files="@(OldZips)" TreatErrorsAsWarnings="true" ContinueOnError="true" /> | ||
<ZipDir SourceDirectory="$(ArtifactDestination)" DestinationFile="$(DestinationDirectory)$(ArtifactName).zip" /> | ||
</Target> | ||
<!--Copies the assembly and pdb to the Beat Saber folder.--> | ||
<Target Name="CopyToPlugins" AfterTargets="Build" Condition="'$(DisableCopyToPlugins)' != 'True' AND '$(ContinuousIntegrationBuild)' != 'True'"> | ||
<PropertyGroup> | ||
<PluginDir>$(BeatSaberDir)\Plugins</PluginDir> | ||
<CanCopyToPlugins>True</CanCopyToPlugins> | ||
<CopyToPluginsError Condition="!Exists('$(PluginDir)')">Unable to copy assembly to game folder, did you set 'BeatSaberDir' correctly in your 'csproj.user' file? Plugins folder doesn't exist: '$(PluginDir)'.</CopyToPluginsError> | ||
<!--Error if 'BeatSaberDir' does not have 'Beat Saber.exe'--> | ||
<CopyToPluginsError Condition="!Exists('$(BeatSaberDir)\Beat Saber.exe')">Unable to copy to Plugins folder, '$(BeatSaberDir)' does not appear to be a Beat Saber game install.</CopyToPluginsError> | ||
<!--Error if 'BeatSaberDir' is the same as 'LocalRefsDir'--> | ||
<CopyToPluginsError Condition="'$(BeatSaberDir)' == '$(LocalRefsDir)' OR '$(BeatSaberDir)' == ''">Unable to copy to Plugins folder, 'BeatSaberDir' has not been set in your 'csproj.user' file.</CopyToPluginsError> | ||
<CanCopyToPlugins Condition="'$(CopyToPluginsError)' != ''">False</CanCopyToPlugins> | ||
</PropertyGroup> | ||
<!--Check if Beat Saber is running--> | ||
<IsProcessRunning ProcessName="Beat Saber" Condition="'$(BSMTTaskAssembly)' != ''"> | ||
<Output TaskParameter="IsRunning" PropertyName="IsRunning" /> | ||
</IsProcessRunning> | ||
<PropertyGroup> | ||
<!--If Beat Saber is running, output to the Pending folder--> | ||
<PluginDir Condition="'$(IsRunning)' == 'True'">$(BeatSaberDir)\IPA\Pending\Plugins</PluginDir> | ||
</PropertyGroup> | ||
<Warning Text="$(CopyToPluginsError)" Condition="'$(CopyToPluginsError)' != ''" /> | ||
<Message Text="Copying '$(OutputAssemblyName).dll' to '$(PluginDir)'." Importance="high" Condition="$(CanCopyToPlugins)" /> | ||
<Copy SourceFiles="$(OutputAssemblyName).dll" DestinationFiles="$(PluginDir)\$(AssemblyName).dll" Condition="$(CanCopyToPlugins)" /> | ||
<Copy SourceFiles="$(OutputAssemblyName).pdb" DestinationFiles="$(PluginDir)\$(AssemblyName).pdb" Condition="'$(CanCopyToPlugins)' == 'True' AND Exists('$(OutputAssemblyName).pdb')" /> | ||
<Warning Text="Beat Saber is running, restart the game to use the latest build." Condition="'$(IsRunning)' == 'True'" /> | ||
</Target> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
using HarmonyLib; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection.Emit; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using UnityEngine; | ||
|
||
namespace OverswingCounter.HarmonyPatches { | ||
[HarmonyPatch(typeof(GameNoteController), nameof(GameNoteController.HandleCut))] | ||
static class GeneralSwingData { | ||
public static Dictionary<SaberType, CutInfo> currentPrimaryCut; | ||
public static Dictionary<SaberType, CutInfo> lastFinishedCut; | ||
|
||
[HarmonyPriority(int.MaxValue)] | ||
static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { | ||
var res = instructions.ToList(); | ||
|
||
res.InsertRange(41, new[] { | ||
new CodeInstruction(OpCodes.Ldarg_1), // 1. Argument, Saber | ||
|
||
new CodeInstruction(OpCodes.Ldloc_S, 6), // The local variable of the new SaberSwingRatingCounter instance | ||
|
||
//new CodeInstruction(OpCodes.Ldarg_0), | ||
//new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(GameNoteController), "_noteTransform")), | ||
|
||
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(GeneralSwingData), nameof(PrepareNewSwingRatingCounter))) | ||
}); | ||
|
||
return res; | ||
} | ||
|
||
public static Dictionary<SaberSwingRatingCounter, CutInfo> swingRatingInfos { get; private set; } = new Dictionary<SaberSwingRatingCounter, CutInfo>(); | ||
|
||
static void PrepareNewSwingRatingCounter(Saber saber, SaberSwingRatingCounter counter/*, Transform transform*/) { | ||
#if TRACE | ||
Console.WriteLine("GameNoteController.HandleCut => PrepareNewSwingRatingCounter()"); | ||
#endif | ||
|
||
if(!swingRatingInfos.TryGetValue(counter, out var existing) || existing.counter != counter) | ||
swingRatingInfos[counter] = new CutInfo(counter, saber/*, transform.localPosition*/); | ||
} | ||
|
||
public static void Clear() { | ||
currentPrimaryCut = new Dictionary<SaberType, CutInfo>(2) { | ||
{ SaberType.SaberA, null }, | ||
{ SaberType.SaberB, null }, | ||
}; | ||
|
||
lastFinishedCut = new Dictionary<SaberType, CutInfo>(2) { | ||
{ SaberType.SaberA, null }, | ||
{ SaberType.SaberB, null }, | ||
}; | ||
|
||
swingRatingInfos.Clear(); | ||
} | ||
|
||
public static Action<CutInfo> newCutCompleted; | ||
} | ||
|
||
public class CutInfo : ISaberSwingRatingCounterDidFinishReceiver { | ||
public SaberSwingRatingCounter counter; | ||
public SaberType saberType; | ||
public Saber saber; | ||
|
||
public float beforeRating = 0f; | ||
public float correctedBeforeRating = 0f; | ||
public float afterRating = 0f; | ||
public float correctedAfterRating = 0f; | ||
public float angle = 0f; | ||
|
||
//public Vector2 notePosition2D; | ||
|
||
public Vector2 swingStart; | ||
public Vector2 swingEnd; | ||
|
||
public float cutTime; | ||
|
||
public bool isDownswing => angle <= 10f || angle >= 170f; | ||
|
||
|
||
public bool isPrimary { get; private set; } = false; | ||
public CutInfo lastFinishedCutToCompareAgainst { get; private set; } | ||
|
||
public CutInfo(SaberSwingRatingCounter counter, Saber saber/*, Vector3 notePosition*/) { | ||
this.counter = counter; | ||
this.saber = saber; | ||
saberType = saber.saberType; | ||
cutTime = Time.realtimeSinceStartup; | ||
//notePosition2D = new Vector2(notePosition.x, notePosition.y); | ||
|
||
lastFinishedCutToCompareAgainst = GeneralSwingData.lastFinishedCut[saberType]; | ||
|
||
isPrimary = GeneralSwingData.currentPrimaryCut[saberType] == null; | ||
if(isPrimary) | ||
GeneralSwingData.currentPrimaryCut[saberType] = this; | ||
|
||
counter.RegisterDidFinishReceiver(this); | ||
} | ||
|
||
public void HandleSaberSwingRatingCounterDidFinish(ISaberSwingRatingCounter saberSwingRatingCounter) { | ||
counter.UnregisterDidFinishReceiver(this); | ||
swingEnd = saber.saberBladeTopPos; | ||
#if DEBUG | ||
Console.WriteLine("Counter {0} (Primary: {5}) finished ({4})! {1:P2}pre, {2:P2}post, {3}angle (isDown: {6})", counter.GetHashCode(), beforeRating, afterRating, angle, saberType, isPrimary, isDownswing); | ||
#endif | ||
counter = null; | ||
|
||
/* | ||
* When you are overswinging, you are always overswinging the exact angle that you do overswing, | ||
* but underswinging is a special case where you can *theoretically* be underswinging, but in reality, | ||
* due to swing points being rounded per cut, you might still be getting full points. We kinda need to | ||
* account for that and calculate your theoretical swing angle based off the points you got | ||
*/ | ||
correctedAfterRating = afterRating; | ||
if(afterRating < 1f) { | ||
var cCorrectedAfterRating = (float)Math.Round(afterRating * ScoreModel.kMaxAfterCutSwingRawScore) / ScoreModel.kMaxAfterCutSwingRawScore; | ||
|
||
// We only want to round UP, not down | ||
if(cCorrectedAfterRating > afterRating) | ||
correctedAfterRating = cCorrectedAfterRating; | ||
} | ||
|
||
correctedBeforeRating = beforeRating; | ||
if(beforeRating < 1f) { | ||
var cCorrectedBeforeRating = (float)Math.Round(beforeRating * ScoreModel.kMaxBeforeCutSwingRawScore) / ScoreModel.kMaxBeforeCutSwingRawScore; | ||
|
||
if(cCorrectedBeforeRating > beforeRating) | ||
correctedBeforeRating = cCorrectedBeforeRating; | ||
} | ||
|
||
GeneralSwingData.lastFinishedCut[saberType] = this; | ||
|
||
if(GeneralSwingData.currentPrimaryCut[saberType] == this) | ||
GeneralSwingData.currentPrimaryCut[saberType] = null; | ||
|
||
if(GeneralSwingData.newCutCompleted != null) | ||
GeneralSwingData.newCutCompleted(this); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
using HarmonyLib; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Reflection.Emit; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using UnityEngine; | ||
|
||
namespace OverswingCounter.HarmonyPatches { | ||
[HarmonyPatch(typeof(SaberSwingRatingCounter), nameof(SaberSwingRatingCounter.ProcessNewData))] | ||
static class GetUnclampedPostSwingData { | ||
static MethodBase SaberSwingRating_AfterCutStepRating = AccessTools.Method(typeof(SaberSwingRating), nameof(SaberSwingRating.AfterCutStepRating)); | ||
static FieldInfo SaberSwingRatingCounter_afterCutRating = AccessTools.Field(typeof(SaberSwingRatingCounter), "_afterCutRating"); | ||
|
||
static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { | ||
var res = instructions.ToList(); | ||
|
||
bool waitForStfld = false; | ||
|
||
var interceptor = new [] { | ||
new CodeInstruction(OpCodes.Ldarg_0), // this | ||
|
||
new CodeInstruction(OpCodes.Ldarg_0), // this._afterCutRating | ||
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(SaberSwingRatingCounter), "_afterCutRating")), | ||
|
||
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(GetUnclampedPostSwingData), nameof(AccumulatorFn))) | ||
}; | ||
|
||
for(var idx = 0; idx < res.Count; idx++) { | ||
var elem = res[idx]; | ||
|
||
if(waitForStfld) { | ||
if(elem.opcode != OpCodes.Stfld || elem.operand != (object)SaberSwingRatingCounter_afterCutRating) | ||
continue; | ||
|
||
res.InsertRange(idx + 1, interceptor); | ||
idx += interceptor.Length; | ||
|
||
waitForStfld = false; | ||
} else if(elem.opcode == OpCodes.Call && elem.operand == (object)SaberSwingRating_AfterCutStepRating) { | ||
waitForStfld = true; | ||
} | ||
} | ||
|
||
return res; | ||
} | ||
|
||
public static void AccumulatorFn(SaberSwingRatingCounter __instance, float tmpRating) { | ||
if(!GeneralSwingData.swingRatingInfos.TryGetValue(__instance, out var info) || info.counter == null) | ||
return; | ||
|
||
if(tmpRating <= 1f) { | ||
info.afterRating = tmpRating; | ||
} else { | ||
info.afterRating += tmpRating - 1f; | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.