mirror of
https://github.com/Theaninova/HitScoreVisualizer.git
synced 2025-12-27 19:16:15 +00:00
Compare commits
12 Commits
master
...
v1.0.1-com
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bde6b9fcef | ||
|
|
7b8f210e65 | ||
|
|
337c3b3f3b | ||
|
|
232264ea1b | ||
|
|
0ebc4fe99f | ||
|
|
5232e704f6 | ||
|
|
4cc7cb0856 | ||
|
|
f8a54ee407 | ||
|
|
ca796ed1c1 | ||
|
|
f15dd367a2 | ||
|
|
45658d5d46 | ||
|
|
a30fd6a772 |
@@ -14,6 +14,28 @@ namespace HitScoreVisualizer
|
||||
{
|
||||
public static Config instance;
|
||||
|
||||
public const string mode_hardcoreCompetetive = "hardcoreCompetetive";
|
||||
public const string mode_format = "format";
|
||||
public const string mode_textOnly = "textOnly";
|
||||
public const string mode_numeric = "numeric";
|
||||
public const string mode_scoreOnTop = "scoreOnTop";
|
||||
|
||||
public const char format_indicator = '%';
|
||||
public const char format_beforeCutScore = 'b';
|
||||
public const char format_accuracyScore = 'c';
|
||||
public const char format_afterCutScore = 'a';
|
||||
public const char FORMAT_BEFORECUTSCORE = 'B';
|
||||
public const char FORMAT_ACCUTRACYSCORE = 'C';
|
||||
public const char FORMAT_AFTERCUTSCORE = 'A';
|
||||
public const char format_score = 's';
|
||||
public const char format_indicatorChar = format_indicator;
|
||||
public const char format_lineBreak = 'n';
|
||||
|
||||
public const bool percentages = true;
|
||||
public const int maxBeforeCutScore = 70;
|
||||
public const int maxAfterCutScore = 30;
|
||||
public const int maxAccuracyScore = 10;
|
||||
|
||||
// If true, this config will not overwrite the existing config file.
|
||||
// (This gets set if a config from a newer version is detected.)
|
||||
[JsonIgnore]
|
||||
@@ -102,6 +124,7 @@ namespace HitScoreVisualizer
|
||||
|
||||
// path to where the config is saved
|
||||
private const string FILE_PATH = "/UserData/HitScoreVisualizerConfig.json";
|
||||
private const string FILE_PATH_RATINGS = "\\Ratings\\Latest.txt";
|
||||
|
||||
private const string DEFAULT_JSON = @"{
|
||||
""majorVersion"": 2,
|
||||
@@ -233,6 +256,7 @@ namespace HitScoreVisualizer
|
||||
};
|
||||
|
||||
public static string fullPath => Environment.CurrentDirectory.Replace('\\', '/') + FILE_PATH;
|
||||
public static string fullPathRatings => Environment.CurrentDirectory + FILE_PATH_RATINGS;
|
||||
|
||||
public static void load()
|
||||
{
|
||||
@@ -339,6 +363,11 @@ namespace HitScoreVisualizer
|
||||
instance = DEFAULT_CONFIG;
|
||||
}
|
||||
|
||||
public static string floatToHexColor(float dec)
|
||||
{
|
||||
return ((int)(255f * dec)).ToString("X2");
|
||||
}
|
||||
|
||||
public static void judge(FlyingScoreTextEffect text, NoteCutInfo noteCutInfo, SaberAfterCutSwingRatingCounter saberAfterCutSwingRatingCounter, ref Color color, int score)
|
||||
{
|
||||
Judgment judgment = DEFAULT_JUDGMENT;
|
||||
@@ -365,7 +394,77 @@ namespace HitScoreVisualizer
|
||||
color = toColor(judgment.color);
|
||||
}
|
||||
|
||||
if (instance.displayMode == "format")
|
||||
//Hardcore Competetive Mode by wulkanat
|
||||
if (instance.displayMode == mode_hardcoreCompetetive)
|
||||
{
|
||||
float beforeCut = noteCutInfo.swingRating;
|
||||
float accuracy = 1f - Mathf.Clamp01(noteCutInfo.cutDistanceToCenter / 0.2f);
|
||||
float afterCut = 0f;
|
||||
if (saberAfterCutSwingRatingCounter != null)
|
||||
afterCut = saberAfterCutSwingRatingCounter.rating;
|
||||
|
||||
int beforeCutScore, accuracyScore, afterCutScore;
|
||||
int beforeMax, accuracyMax, afterMax;
|
||||
|
||||
if (percentages)
|
||||
{
|
||||
beforeMax = 100;
|
||||
accuracyMax = 100;
|
||||
afterMax = 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
beforeMax = 70;
|
||||
accuracyMax = 10;
|
||||
afterMax = 30;
|
||||
}
|
||||
|
||||
beforeCutScore = Mathf.RoundToInt(beforeMax * beforeCut);
|
||||
accuracyScore = Mathf.RoundToInt(accuracyMax * accuracy);
|
||||
afterCutScore = Mathf.RoundToInt(afterMax * afterCut);
|
||||
|
||||
if (beforeCutScore == beforeMax && accuracyScore == accuracyMax && afterCutScore == afterMax)
|
||||
{
|
||||
text.text = "Perfect";
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder formattedBuilder = new StringBuilder();
|
||||
|
||||
if (percentages)
|
||||
formattedBuilder.Append( "<color=#" + floatToHexColor(1f - score / 110f) + floatToHexColor(score / 110f) + "00>" + (int) ((score / 110f) * 100f) + "%\n");
|
||||
else
|
||||
formattedBuilder.Append("<color=#" + floatToHexColor(1f - score / 110f) + floatToHexColor(score / 110f) + "00>" + score + "\n");
|
||||
|
||||
if (beforeCutScore == beforeMax)
|
||||
formattedBuilder.Append("<color=#FFFFFF>P ");
|
||||
else
|
||||
formattedBuilder.Append("<color=#" + floatToHexColor(1f - beforeCut) + (floatToHexColor(beforeCut) + "00>" + beforeCutScore + " "));
|
||||
|
||||
if (accuracyScore == accuracyMax)
|
||||
formattedBuilder.Append("<color=#FFFFFF>P ");
|
||||
else
|
||||
formattedBuilder.Append("<color=#" + floatToHexColor(1f - accuracy) + floatToHexColor(accuracy) + "00>" + accuracyScore + " ");
|
||||
|
||||
if (afterCutScore == afterMax)
|
||||
formattedBuilder.Append("<color=#FFFFFF>P");
|
||||
else
|
||||
formattedBuilder.Append("<color=#" + floatToHexColor(1f - afterCut) + floatToHexColor(afterCut) + "00>" + afterCutScore);
|
||||
|
||||
text.text = formattedBuilder.ToString();
|
||||
|
||||
//Feature doesn't work
|
||||
/*if (saberAfterCutSwingRatingCounter.didFinish)
|
||||
{
|
||||
using (StreamWriter file = File.AppendText(fullPathRatings))
|
||||
{
|
||||
file.WriteLine(saberAfterCutSwingRatingCounter.RequestId + " <<>> " + beforeCut + " " + accuracy + " " + afterCut + " | " + score);
|
||||
}
|
||||
}*/
|
||||
|
||||
return;
|
||||
}
|
||||
else if (instance.displayMode == mode_format)
|
||||
{
|
||||
int beforeCutScore, accuracyScore, afterCutScore;
|
||||
|
||||
@@ -380,7 +479,7 @@ namespace HitScoreVisualizer
|
||||
|
||||
StringBuilder formattedBuilder = new StringBuilder();
|
||||
string formatString = judgment.text;
|
||||
int nextPercentIndex = formatString.IndexOf('%');
|
||||
int nextPercentIndex = formatString.IndexOf(format_indicator);
|
||||
while (nextPercentIndex != -1)
|
||||
{
|
||||
formattedBuilder.Append(formatString.Substring(0, nextPercentIndex));
|
||||
@@ -392,62 +491,54 @@ namespace HitScoreVisualizer
|
||||
|
||||
switch (specifier)
|
||||
{
|
||||
case 'b':
|
||||
case format_beforeCutScore:
|
||||
formattedBuilder.Append(beforeCutScore);
|
||||
break;
|
||||
case 'c':
|
||||
case format_accuracyScore:
|
||||
formattedBuilder.Append(accuracyScore);
|
||||
break;
|
||||
case 'a':
|
||||
case format_afterCutScore:
|
||||
formattedBuilder.Append(afterCutScore);
|
||||
break;
|
||||
case 'B':
|
||||
case FORMAT_BEFORECUTSCORE:
|
||||
formattedBuilder.Append(judgeSegment(beforeCutScore, instance.beforeCutAngleJudgments));
|
||||
break;
|
||||
case 'C':
|
||||
case FORMAT_ACCUTRACYSCORE:
|
||||
formattedBuilder.Append(judgeSegment(accuracyScore, instance.accuracyJudgments));
|
||||
break;
|
||||
case 'A':
|
||||
case FORMAT_AFTERCUTSCORE:
|
||||
formattedBuilder.Append(judgeSegment(afterCutScore, instance.afterCutAngleJudgments));
|
||||
break;
|
||||
case 's':
|
||||
case format_score:
|
||||
formattedBuilder.Append(score);
|
||||
break;
|
||||
case '%':
|
||||
formattedBuilder.Append("%");
|
||||
case format_indicatorChar:
|
||||
formattedBuilder.Append(format_indicatorChar);
|
||||
break;
|
||||
case 'n':
|
||||
case format_lineBreak:
|
||||
formattedBuilder.Append("\n");
|
||||
break;
|
||||
default:
|
||||
formattedBuilder.Append("%" + specifier);
|
||||
formattedBuilder.Append(format_indicator + specifier);
|
||||
break;
|
||||
}
|
||||
|
||||
formatString = formatString.Remove(0, nextPercentIndex + 2);
|
||||
nextPercentIndex = formatString.IndexOf('%');
|
||||
nextPercentIndex = formatString.IndexOf(format_indicator);
|
||||
}
|
||||
formattedBuilder.Append(formatString);
|
||||
|
||||
text.text = formattedBuilder.ToString();
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance.displayMode == "textOnly")
|
||||
{
|
||||
else if (instance.displayMode == mode_textOnly)
|
||||
text.text = judgment.text;
|
||||
else if (instance.displayMode == mode_numeric)
|
||||
return;
|
||||
}
|
||||
if (instance.displayMode == "numeric")
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (instance.displayMode == "scoreOnTop")
|
||||
{
|
||||
else if (instance.displayMode == mode_scoreOnTop)
|
||||
text.text = score + "\n" + judgment.text + "\n";
|
||||
return;
|
||||
}
|
||||
text.text = judgment.text + "\n" + score + "\n";
|
||||
else
|
||||
text.text = judgment.text + "\n" + score + "\n";
|
||||
}
|
||||
|
||||
public static Color toColor(float[] rgba)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<OutputPath>..\..\..\..\Steam Games\steamapps\common\Beat Saber\Plugins\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
@@ -31,16 +31,16 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony">
|
||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\0Harmony.dll</HintPath>
|
||||
<HintPath>..\..\..\..\Steam Games\steamapps\common\Beat Saber\Beat Saber_Data\Managed\0Harmony.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp">
|
||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<HintPath>..\..\..\..\Steam Games\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp-firstpass">
|
||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||
<HintPath>..\..\..\..\Steam Games\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IllusionPlugin">
|
||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\IllusionPlugin.dll</HintPath>
|
||||
<HintPath>..\..\..\..\Steam Games\steamapps\common\Beat Saber\Beat Saber_Data\Managed\IllusionPlugin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
@@ -54,10 +54,10 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="UnityEngine">
|
||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.dll</HintPath>
|
||||
<HintPath>..\..\..\..\Steam Games\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
<HintPath>..\..\..\..\Steam Games\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace HitScoreVisualizer
|
||||
|
||||
public void OnApplicationStart()
|
||||
{
|
||||
System.IO.File.WriteAllText(Config.fullPathRatings, "NO DATA");
|
||||
|
||||
SceneManager.activeSceneChanged += SceneManagerOnActiveSceneChanged;
|
||||
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
|
||||
try
|
||||
@@ -33,8 +35,27 @@ namespace HitScoreVisualizer
|
||||
Config.load();
|
||||
}
|
||||
|
||||
private void SceneManagerOnActiveSceneChanged(Scene arg0, Scene arg1)
|
||||
private void SceneManagerOnActiveSceneChanged(Scene arg0, Scene newScene)
|
||||
{
|
||||
switch (newScene.buildIndex)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
// Return if we're not playing in a song.
|
||||
return;
|
||||
case 8:
|
||||
case 9:
|
||||
// We're in a song
|
||||
//System.IO.File.WriteAllText(Config.fullPathRatings, newScene.name);
|
||||
return;
|
||||
|
||||
default:
|
||||
// Unknown, fallback to matching the name.
|
||||
if (newScene.name.Contains("Environment"))
|
||||
goto case 8;
|
||||
else
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
|
||||
|
||||
28
README.md
Normal file
28
README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
### Changelog
|
||||
2.0.2: Bug fixes and performance improvements. If you've been noticing lag, try disabling the fade option on all judgments in the config.
|
||||
2.1.0: Added display mode "format" (see below).
|
||||
***
|
||||
Colors the score popups that appear when hitting notes. Also adds judgment text above (or below) the score.
|
||||
|
||||
After installing the plugin, run the game once to generate the config file at `UserData/HitScoreVisualizerConfig.json` in your Beat Saber install folder. A graphical editor for this file is in the works; until then, editing the config is considered an advanced feature, as JSON is not particularly human-friendly. If you want to edit it anyway, here's the documentation:
|
||||
* Remove the `isDefaultConfig` line. If this option is set to true, your config will get overwritten by the updated default config with every plugin update.
|
||||
* Valid options for `displayMode` are "numeric" (displays only the score), "textOnly" (displays only the judgment text), "scoreOnTop" (displays the score above the judgment text), "format" (see "Formatting" below), or any other string (displays the judgment text above the score).
|
||||
* Put your judgments in descending order; the first one encountered with `threshold` <= the score earned for a note will be applied.
|
||||
* Include exactly 4 numbers in each judgment's `color` array. These represent red, green, blue, and a 4th channel which, knowing Beat Saber's shaders, could be glow, transparency, or even both at once. Modify the 4th number if you like, but I can't say what effect it will have.
|
||||
* If you include more or fewer than 4 numbers in a judgment's color array, your entire config will be ignored (but not overwritten).
|
||||
* If you include the line `"fade": true` in a judgment, the color for scores earning that judgment will be interpolated between that judgment's color and the color of the previous judgment in the list, based on how close to the threshold for that judgment the score was. Use this to create a smooth gradient of color if you want one.
|
||||
* If you enable `fade` for the first judgment in the list, the plugin and Beat Saber itself will quite possibly die in a fire if that judgment is ever earned. Don't enable `fade` for the first judgment in the list. You have been warned.
|
||||
* Judgment text supports [TextMeshPro formatting!](http://digitalnativestudios.com/textmeshpro/docs/rich-text/)
|
||||
* **Formatting**
|
||||
In displayMode "format", a number of HitScoreVisualizer format specifiers are available (as well as TextMeshPro's formatting tools). Your formatted string will replace the score text (so include %s if you want to see your score).
|
||||
* %b: The score contributed by the part of the swing before cutting the block.
|
||||
* %c: The score contributed by the accuracy of the cut.
|
||||
* %a: The score contributed by the part of the swing after cutting the block.
|
||||
* %B, %C, %A: As above, except using the appropriate judgment from that part of the swing (as configured for "beforeCutAngleJudgments", "accuracyJudgments", or "afterCutAngleJudgments").
|
||||
* %s: The total score for the cut.
|
||||
* %%: A literal percent symbol.
|
||||
* %n: A newline.
|
||||
* Note that before-swing/accuracy/after-swing judgments don't support color or fading, although you can put TextMeshPro color tags in them if you want to.
|
||||
If you make your own config, feel free to share it in #other-files!
|
||||
|
||||
Plugin originally requested by @AntRazor on the modding discord. Thanks go to @AntRazor and @wulkanat for input on the default config released with this update, as well as everyone in #mod-development and the entire server for the love and support. :')
|
||||
Reference in New Issue
Block a user