Skip to content

Commit f8f0aae

Browse files
committed
Bye Sweet Carole
1 parent 67dfc12 commit f8f0aae

9 files changed

Lines changed: 533 additions & 0 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ByeSweetCarole", "ByeSweetCarole\ByeSweetCarole.csproj", "{D59DBE8D-FE14-4B42-84A4-F8665C60F46C}"
4+
EndProject
5+
Global
6+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7+
Debug|Any CPU = Debug|Any CPU
8+
Release|Any CPU = Release|Any CPU
9+
EndGlobalSection
10+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
11+
{D59DBE8D-FE14-4B42-84A4-F8665C60F46C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
12+
{D59DBE8D-FE14-4B42-84A4-F8665C60F46C}.Debug|Any CPU.Build.0 = Debug|Any CPU
13+
{D59DBE8D-FE14-4B42-84A4-F8665C60F46C}.Release|Any CPU.ActiveCfg = Release|Any CPU
14+
{D59DBE8D-FE14-4B42-84A4-F8665C60F46C}.Release|Any CPU.Build.0 = Release|Any CPU
15+
EndGlobalSection
16+
EndGlobal
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<Import Project="Environment.props" Condition="Exists('Environment.props')"/>
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Product>ByeSweetCarole Ultra-Wide</Product>
7+
<Version>0.1.0</Version>
8+
<AssemblyName>$(Product)</AssemblyName>
9+
<AssemblyVersion>$(Version)</AssemblyVersion>
10+
<FileVersion>$(Version)</FileVersion>
11+
<PlatformTarget>x64</PlatformTarget>
12+
<RestoreAdditionalProjectSources>
13+
https://api.nuget.org/v3/index.json;
14+
https://nuget.bepinex.dev/v3/index.json;
15+
https://nuget.samboy.dev/v3/index.json
16+
</RestoreAdditionalProjectSources>
17+
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
18+
<LangVersion>latest</LangVersion>
19+
<DebugType>embedded</DebugType>
20+
<DebugSymbols>true</DebugSymbols>
21+
<PathMap>$(MSBuildProjectDirectory)=.</PathMap>
22+
<Deterministic>true</Deterministic>
23+
<IsPackable>false</IsPackable>
24+
<OutputPath>$(ModsOutputPath)</OutputPath>
25+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
26+
<DefineConstants>Melon</DefineConstants>
27+
</PropertyGroup>
28+
29+
<ItemDefinitionGroup>
30+
<Reference>
31+
<Private>False</Private>
32+
</Reference>
33+
<ProjectReference>
34+
<Private>False</Private>
35+
</ProjectReference>
36+
<PackageReference>
37+
<Private>False</Private>
38+
</PackageReference>
39+
</ItemDefinitionGroup>
40+
41+
<ItemGroup>
42+
<!-- MelonLoader Core References -->
43+
<Reference Include="0Harmony">
44+
<HintPath>$(MelonLoaderNet6Path)\0Harmony.dll</HintPath>
45+
</Reference>
46+
<Reference Include="Il2CppInterop.Runtime">
47+
<HintPath>$(MelonLoaderNet6Path)\Il2CppInterop.Runtime.dll</HintPath>
48+
</Reference>
49+
<Reference Include="MelonLoader">
50+
<HintPath>$(MelonLoaderNet6Path)\MelonLoader.dll</HintPath>
51+
</Reference>
52+
53+
<!-- Game-Specific Assemblies -->
54+
<Reference Include="Assembly-CSharp">
55+
<HintPath>$(Il2CppAssembliesPath)\Assembly-CSharp.dll</HintPath>
56+
</Reference>
57+
<Reference Include="Il2Cppmscorlib">
58+
<HintPath>$(Il2CppAssembliesPath)\Il2Cppmscorlib.dll</HintPath>
59+
</Reference>
60+
61+
62+
<!-- Unity Modules -->
63+
64+
<Reference Include="Unity.RenderPipeline.Universal.ShaderLibrary">
65+
<HintPath>$(Il2CppAssembliesPath)\Unity.RenderPipeline.Universal.ShaderLibrary.dll</HintPath>
66+
</Reference>
67+
<Reference Include="Unity.RenderPipelines.Core.Runtime">
68+
<HintPath>$(Il2CppAssembliesPath)\Unity.RenderPipelines.Core.Runtime.dll</HintPath>
69+
</Reference>
70+
<Reference Include="Unity.RenderPipelines.Universal.Runtime">
71+
<HintPath>$(Il2CppAssembliesPath)\Unity.RenderPipelines.Universal.Runtime.dll</HintPath>
72+
</Reference>
73+
<Reference Include="UnityEngine.AIModule">
74+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.AIModule.dll</HintPath>
75+
</Reference>
76+
<Reference Include="UnityEngine.AnimationModule">
77+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.AnimationModule.dll</HintPath>
78+
</Reference>
79+
<Reference Include="UnityEngine.AssetBundleModule">
80+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.AssetBundleModule.dll</HintPath>
81+
</Reference>
82+
<Reference Include="UnityEngine.AudioModule">
83+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.AudioModule.dll</HintPath>
84+
</Reference>
85+
<Reference Include="UnityEngine.CoreModule">
86+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.CoreModule.dll</HintPath>
87+
</Reference>
88+
<Reference Include="UnityEngine.IMGUIModule">
89+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.IMGUIModule.dll</HintPath>
90+
</Reference>
91+
<Reference Include="UnityEngine.InputLegacyModule">
92+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.InputLegacyModule.dll</HintPath>
93+
</Reference>
94+
<Reference Include="UnityEngine.ParticleSystemModule">
95+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.ParticleSystemModule.dll</HintPath>
96+
</Reference>
97+
<Reference Include="UnityEngine.Physics2DModule">
98+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.Physics2DModule.dll</HintPath>
99+
</Reference>
100+
<Reference Include="UnityEngine.PhysicsModule">
101+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.PhysicsModule.dll</HintPath>
102+
</Reference>
103+
<Reference Include="UnityEngine.SharedInternalsModule">
104+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.SharedInternalsModule.dll</HintPath>
105+
</Reference>
106+
<Reference Include="UnityEngine.TerrainModule">
107+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.TerrainModule.dll</HintPath>
108+
</Reference>
109+
<Reference Include="UnityEngine.TerrainPhysicsModule">
110+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.TerrainPhysicsModule.dll</HintPath>
111+
</Reference>
112+
<Reference Include="UnityEngine.TextRenderingModule">
113+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.TextRenderingModule.dll</HintPath>
114+
</Reference>
115+
<Reference Include="UnityEngine.UI">
116+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.UI.dll</HintPath>
117+
</Reference>
118+
<Reference Include="UnityEngine.UIModule">
119+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.UIModule.dll</HintPath>
120+
</Reference>
121+
<Reference Include="UnityEngine.UnityWebRequestModule">
122+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.UnityWebRequestModule.dll</HintPath>
123+
</Reference>
124+
<Reference Include="UnityEngine.UnityWebRequestWWWModule">
125+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
126+
</Reference>
127+
<Reference Include="UnityEngine.VideoModule">
128+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.VideoModule.dll</HintPath>
129+
</Reference>
130+
<Reference Include="UnityEngine.WindModule">
131+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.WindModule.dll</HintPath>
132+
</Reference>
133+
<Reference Include="UnityEngine.XRModule">
134+
<HintPath>$(Il2CppAssembliesPath)\UnityEngine.XRModule.dll</HintPath>
135+
</Reference>
136+
</ItemGroup>
137+
138+
<ItemGroup>
139+
<PackageReference Remove="BepInEx.AssemblyPublicizer.MSBuild" />
140+
</ItemGroup>
141+
142+
</Project>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project>
2+
<PropertyGroup>
3+
<!-- Path to your ML game installation -->
4+
<GamePath>F:\SteamLibrary\steamapps\common\Bye Sweet Carole</GamePath>
5+
6+
<!-- MelonLoader paths -->
7+
<MelonLoaderPath>$(GamePath)\MelonLoader</MelonLoaderPath>
8+
<MelonLoaderNet6Path>$(MelonLoaderPath)\net6</MelonLoaderNet6Path>
9+
<Il2CppAssembliesPath>$(MelonLoaderPath)\Il2CppAssemblies</Il2CppAssembliesPath>
10+
11+
<!-- Output path for MelonLoader mods -->
12+
<ModsOutputPath>$(GamePath)\Mods</ModsOutputPath>
13+
</PropertyGroup>
14+
</Project>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Global using directives
2+
3+
global using System.Diagnostics.CodeAnalysis;
4+
global using System.Globalization;
5+
global using ByeSweetCarole;
6+
global using HarmonyLib;
7+
global using Il2Cpp;
8+
global using MelonLoader;
9+
global using Il2CppInterop.Runtime.Injection;
10+
global using UnityEngine;
11+
global using UnityEngine.Rendering;
12+
global using UnityEngine.Rendering.Universal;
13+
global using UnityEngine.SceneManagement;
14+
global using UnityEngine.UI;
15+
global using UnityEngine.Video;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace ByeSweetCarole.Misc;
2+
3+
public static class Extensions
4+
{
5+
public static string GetFullPath(this Transform current)
6+
{
7+
var path = current.name;
8+
while (current.parent)
9+
{
10+
current = current.parent;
11+
path = current.name + "/" + path;
12+
}
13+
14+
return path;
15+
}
16+
17+
// Attempts to add a component of type T to the GameObject if it doesn't already have one.
18+
// Returns the existing component if found, or the newly added component otherwise.
19+
public static T TryAddComponent<T>(this GameObject gameObject) where T : Component
20+
{
21+
if (!gameObject)
22+
{
23+
throw new ArgumentNullException(nameof(gameObject));
24+
}
25+
26+
// Check if the GameObject already has the component - only call GetComponent once
27+
var component = gameObject.GetComponent<T>();
28+
29+
return component ? component : gameObject.AddComponent<T>();
30+
}
31+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
[assembly: MelonInfo(typeof(Mod), "Bye Sweet Carole Ultra-Wide", "0.1.0", "p1xel8ted")]
2+
3+
namespace ByeSweetCarole;
4+
5+
public class Mod : MelonMod
6+
{
7+
public enum TargetFrameRateOption
8+
{
9+
Sixty = 60,
10+
OneTwenty = 120,
11+
OneEighty = 180,
12+
TwoForty = 240,
13+
ThreeSixty = 360,
14+
MaxRefresh = -1
15+
}
16+
17+
18+
public enum VsyncOption
19+
{
20+
Disabled,
21+
EveryRefresh,
22+
EverySecondRefresh
23+
}
24+
25+
#if DEBUG
26+
internal static readonly int NativeDisplayWidth = 3200;
27+
internal static readonly int NativeDisplayHeight = 900;
28+
#else
29+
internal static readonly int NativeDisplayWidth = Display.main.systemWidth; //3440
30+
internal static readonly int NativeDisplayHeight = Display.main.systemHeight; //1440
31+
#endif
32+
33+
34+
internal static readonly int MaxRefresh = Screen.resolutions.Max(a => a.refreshRate); //144
35+
internal static float MainAspect => NativeDisplayWidth / (float)NativeDisplayHeight; //2.3888
36+
internal static Rect CameraRect => new(0, 0, NativeDisplayWidth, NativeDisplayHeight); //(0,0,3440,1440)
37+
private static float NormalWidth => NativeDisplayHeight * 16f / 9f; //2560
38+
internal static float PositiveScaleFactor => NativeDisplayWidth / NormalWidth; //1.34375
39+
40+
41+
private static MelonPreferences_Entry<bool> RunInBackground { get; set; }
42+
private static MelonPreferences_Entry<bool> MuteInBackground { get; set; }
43+
44+
internal static MelonPreferences_Entry<FullScreenMode> FullScreenModeConfig { get; private set; }
45+
46+
47+
private static MelonPreferences_Entry<TargetFrameRateOption> TargetFramerate { get; set; }
48+
internal static float NativeAspectRatio => 16f / 9f; //1.7777
49+
private static float CameraWidth => NativeDisplayHeight * NativeAspectRatio; //2560
50+
internal static float BlackBarSize => (NativeDisplayWidth - CameraWidth) / 2f; //440
51+
52+
private static MelonPreferences_Entry<VsyncOption> VSyncSetting { get; set; }
53+
private static bool RequiresUpdate { get; set; }
54+
55+
private static void UpdateCanvasScalers()
56+
{
57+
foreach (var scaler in Patches.Patches.Scalers.Where(scaler => scaler))
58+
{
59+
Patches.Patches.ProcessScaler(scaler);
60+
}
61+
}
62+
63+
private static void FocusChanged(bool focus)
64+
{
65+
Application.runInBackground = RunInBackground.Value;
66+
var audioSources = Resources.FindObjectsOfTypeAll<AudioSource>();
67+
foreach (var audioSource in audioSources)
68+
{
69+
audioSource.mute = !focus && MuteInBackground.Value;
70+
}
71+
}
72+
73+
public override void OnInitializeMelon()
74+
{
75+
var category = MelonPreferences.CreateCategory("ByeSweetCaroleUltraWideFix", "Bye Sweet Carole Ultra-Wide Settings");
76+
category.SetFilePath(Path.Combine("UserData", "Bye Sweet Carole Ultra-Wide Settings.cfg"), true, true);
77+
78+
// Display Entries
79+
FullScreenModeConfig = category.CreateEntry("FullScreenMode", FullScreenMode.FullScreenWindow,
80+
"Full Screen Mode",
81+
"Select how the game displays on your screen:\n" +
82+
"- FullScreenWindow (Recommended): Runs the game in a borderless window that covers the entire screen. " +
83+
"This mode offers seamless alt-tabbing and behaves like Exclusive Fullscreen on most modern Windows versions.\n" +
84+
"- Exclusive Fullscreen: Attempts to take direct control of the display for scenarios like legacy compatibility or HDR management. " +
85+
"On modern systems, its behavior is nearly identical to FullScreenWindow, so this mode is generally not needed.\n" +
86+
"- Windowed: Runs the game in a resizable window.");
87+
FullScreenModeConfig.OnEntryValueChanged.Subscribe((_, _) => { UpdateAll(true); });
88+
89+
VSyncSetting = category.CreateEntry("VSyncSetting", VsyncOption.Disabled,
90+
"VSync Setting",
91+
"Control how VSync synchronizes the game's frame rate with your monitor's refresh rate to prevent screen tearing:\n" +
92+
"- Disabled (Higher Performance): Turns off synchronization, potentially improving performance but may cause screen tearing.\n" +
93+
"- Enabled (Every Refresh): Synchronizes with every monitor refresh, ensuring smooth visuals but might slightly reduce performance.\n" +
94+
"- Enabled (Every 2nd Refresh): Synchronizes with every second monitor refresh, effectively halving the refresh rate for weaker hardware or demanding setups.");
95+
VSyncSetting.OnEntryValueChanged.Subscribe((_, _) => { UpdateAll(true); });
96+
97+
TargetFramerate = category.CreateEntry("TargetFramerate", TargetFrameRateOption.MaxRefresh,
98+
"Target Framerate",
99+
"Set the target framerate - this will only function when VSYNC is OFF (0)");
100+
TargetFramerate.OnEntryValueChanged.Subscribe((_, _) => { UpdateAll(true); });
101+
102+
// Background Entries
103+
RunInBackground = category.CreateEntry("RunInBackground", true,
104+
"Run In Background",
105+
"Allows the game to run even when not in focus.");
106+
107+
MuteInBackground = category.CreateEntry("MuteInBackground", false,
108+
"Mute In Background",
109+
"Mutes the game's audio when it is not in focus.");
110+
111+
112+
Application.focusChanged += (Il2CppSystem.Action<bool>)FocusChanged;
113+
114+
MelonLogger.Msg($"Plugin WitchSpring R Ultra-Wide is loaded!");
115+
116+
category.SaveToFile(false);
117+
MelonPreferences.Save();
118+
}
119+
120+
121+
private static void UpdateAll(bool force = false)
122+
{
123+
UpdateDisplay(force);
124+
}
125+
126+
public override void OnSceneWasLoaded(int buildIndex, string sceneName)
127+
{
128+
base.OnSceneWasLoaded(buildIndex, sceneName);
129+
130+
UpdateCanvasScalers();
131+
UpdateAll(true);
132+
}
133+
134+
private static void UpdateDisplay(bool force = false)
135+
{
136+
// Apply VSync setting using the mapped value
137+
138+
QualitySettings.vSyncCount = (int)VSyncSetting.Value;
139+
140+
141+
// Apply target frame rate only if VSync is off
142+
Application.targetFrameRate = QualitySettings.vSyncCount == 0 ? (int)TargetFramerate.Value : -1;
143+
144+
if (force)
145+
{
146+
RequiresUpdate = true;
147+
}
148+
149+
if (!RequiresUpdate) return;
150+
151+
#if DEBUG
152+
Screen.SetResolution(NativeDisplayWidth, NativeDisplayHeight, FullScreenMode.Windowed, MaxRefresh);
153+
#else
154+
Screen.SetResolution(NativeDisplayWidth, NativeDisplayHeight, FullScreenModeConfig.Value, MaxRefresh);
155+
#endif
156+
MelonLogger.Msg($"Resolution updated: {NativeDisplayWidth}x{NativeDisplayHeight}, Full Screen Mode={FullScreenModeConfig.Value}, Refresh Rate={MaxRefresh}Hz");
157+
158+
RequiresUpdate = false;
159+
}
160+
}

0 commit comments

Comments
 (0)