Skip to content

Commit 9b7b28a

Browse files
Fix interference with level environment (v1.2.1.1)
New Options: * Minimum LOD - This setting has returned, now that the underlying issue for its removal has been solved in a different way. * Show Data Effect in Ghost Mode - The data materialization spawn effect can now be turned on/off for cars in Ghost Mode. Changes: * Fundamental changes have been made to how car visual styles are assigned. Previously we would modify isGhost and simulateNetworkCar in order to force our ideal ghost, but this actually had a ton of side effects. * For the most part PlayerDataReplay.isGhost_ is used to determine if the replay is in GHOST MODE. When in Ghost Mode, the replay will not interact with the environment. So when setting isGhost_ to false for replay/networked style cars, this gave replay cars access to changing the environment (like road popups, explodable mines, etc.). * The other half of level interference came from the `NitronicCarController` (which simulates fine car movement, like steering and suspension). It's still not clear *what* inside this controller is causing issues, but *this* is the cause of the warp anchor problems in the workshop level Breakout. This controller houses some very intensive CPU calculations, so it's still not clear if this is a lag issue. But for now, the solution is to manually disable the controller when we exceed a certain number of ghost replays/combined with a Minimum LOD that would force the controller to always be enabled. * Because we've solved the issue with `NitronicCarController`, Minimum LOD can now be a setting again. * LOD settings have been completely rebranded to make more sense to the average user (using names seen in Graphics settings): * Speck -> Very Low * Very Far -> Low * Far -> Medium * Medium -> High * Near -> Ultra * In-Focus -> Ultra (In-Focus) * The In-Focus (First Person) LOD option has been completely removed, and how In-Focus LOD types are handled has been changed. Max LOD no longer affects In-Focus LOD types, since these are exclusively used for the car with camera focus. * The In-Focus (Ultra (In-Focus)) setting is now exclusive to Minimum LOD, what this does is allow the LOD for cars to go one step higher than it would normally for unfocused cars. Refactors: * The CreatePlayerDataReplay method patch has been completely removed, in favor of creating our compound data at the start of InitPlayerDataReplay. We can do this now, because we're no longer trying to modify the isGhost argument being passed to InitPlayerDataReplay.
1 parent dc6a04c commit 9b7b28a

15 files changed

Lines changed: 420 additions & 326 deletions

Distance.ReplayIntensifies/ConfigurationLogic.cs

Lines changed: 29 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -71,30 +71,36 @@ public CarLevelOfDetail.Type ReplayDetailType
7171
private const string MaxDetailLevel_ID = "visual.max_level_of_detail";
7272
public CarLevelOfDetail.Level MaxLevelOfDetail
7373
{
74-
get => Get<CarLevelOfDetail.Level>(MaxDetailLevel_ID);
74+
get
75+
{
76+
var value = Get<CarLevelOfDetail.Level>(MaxDetailLevel_ID);
77+
if (value < Mod.MaxMaxLevelOfDetail) value = Mod.MaxMaxLevelOfDetail;
78+
if (value > Mod.MinMaxLevelOfDetail) value = Mod.MinMaxLevelOfDetail;
79+
return value;
80+
}
7581
set
7682
{
83+
if (value < Mod.MaxMaxLevelOfDetail) value = Mod.MaxMaxLevelOfDetail;
84+
if (value > Mod.MinMaxLevelOfDetail) value = Mod.MinMaxLevelOfDetail;
7785
Set(MaxDetailLevel_ID, value);
7886
this.MaxLevelOfDetailCached = value;
7987
}
8088
}
8189

8290
private const string MinDetailLevel_ID = "visual.min_level_of_detail";
83-
/*public CarLevelOfDetail.Level MinLevelOfDetail
91+
public CarLevelOfDetail.Level MinLevelOfDetail
8492
{
85-
get => Get<CarLevelOfDetail.Level>(MinDetailLevel_ID);
86-
set
93+
get
8794
{
88-
Set(MinDetailLevel_ID, value);
89-
this.MinLevelOfDetailCached = value;
95+
var value = Get<CarLevelOfDetail.Level>(MinDetailLevel_ID);
96+
if (value < Mod.MaxMinLevelOfDetail) value = Mod.MaxMinLevelOfDetail;
97+
return value;
9098
}
91-
}*/
92-
// NOTE: Min LOD has to be removed due to affecting the level environment.
93-
public CarLevelOfDetail.Level MinLevelOfDetail
94-
{
95-
get => CarLevelOfDetail.Level.Speck;
9699
set
97100
{
101+
if (value < Mod.MaxMinLevelOfDetail) value = Mod.MaxMinLevelOfDetail;
102+
Set(MinDetailLevel_ID, value);
103+
this.MinLevelOfDetailCached = value;
98104
}
99105
}
100106

@@ -105,6 +111,13 @@ public bool EnableUnrestrictedOpponentColors
105111
set => Set(EnableUnrestrictedOpponentColors_ID, value);
106112
}
107113

114+
private const string ShowDataEffectInGhostMode_ID = "visual.data_effect_in_ghost_mode";
115+
public bool ShowDataEffectInGhostMode
116+
{
117+
get => Get<bool>(ShowDataEffectInGhostMode_ID);
118+
set => Set(ShowDataEffectInGhostMode_ID, value);
119+
}
120+
108121

109122
private const string UseRivalStyleForGhosts_ID = "visual.use_rival_style_for_ghosts";
110123
public bool UseRivalStyleForGhosts
@@ -176,15 +189,7 @@ public Dictionary<ulong, string> SteamRivals
176189

177190
// Cached property values for faster accessing.
178191
public CarLevelOfDetail.Level MaxLevelOfDetailCached { get; private set; }
179-
//public CarLevelOfDetail.Level MinLevelOfDetailCached { get; private set; }
180-
// NOTE: Min LOD has to be removed due to affecting the level environment.
181-
public CarLevelOfDetail.Level MinLevelOfDetailCached
182-
{
183-
get => CarLevelOfDetail.Level.Speck;
184-
set
185-
{
186-
}
187-
}
192+
public CarLevelOfDetail.Level MinLevelOfDetailCached { get; private set; }
188193

189194
#endregion
190195

@@ -199,18 +204,6 @@ public CarLevelOfDetail.Type GetCarDetailType(bool isGhost, bool isCarRival)
199204
return (isGhost) ? this.GhostDetailType : this.ReplayDetailType;
200205
}
201206

202-
/*public void SetCarDetailType(bool isGhost, CarLevelOfDetail.Type value)
203-
{
204-
if (isGhost)
205-
{
206-
this.GhostDetailType = value;
207-
}
208-
else
209-
{
210-
this.ReplayDetailType = value;
211-
}
212-
}*/
213-
214207
public bool GetCarOutline(bool isGhost, bool isCarRival)
215208
{
216209
if (isCarRival)
@@ -220,18 +213,6 @@ public bool GetCarOutline(bool isGhost, bool isCarRival)
220213
return (isGhost) ? this.GhostOutline : this.ReplayOutline;
221214
}
222215

223-
/*public void SetCarOutline(bool isGhost, bool value)
224-
{
225-
if (isGhost)
226-
{
227-
this.GhostOutline = value;
228-
}
229-
else
230-
{
231-
this.ReplayOutline = value;
232-
}
233-
}*/
234-
235216
// Determines if this car should be displayed as a Steam Rival (which accounts for settings like 'Use rival style for ghosts/replays', etc.).
236217
public bool IsCarSteamRival(bool isGhost, long userID) => IsCarSteamRival(isGhost, unchecked((ulong)userID));
237218

@@ -347,9 +328,11 @@ public void Awake()
347328
Get(GhostDetailType_ID, CarLevelOfDetail.Type.Ghost);
348329
Get(ReplayOutline_ID, false);
349330
Get(ReplayDetailType_ID, CarLevelOfDetail.Type.Replay);
350-
this.MaxLevelOfDetailCached = Get(MaxDetailLevel_ID, CarLevelOfDetail.Level.InFocusFP);
351-
this.MinLevelOfDetailCached = Get(MinDetailLevel_ID, CarLevelOfDetail.Level.Speck);
331+
// Assign back to non-cached properties to handle automatic clamping and caching.
332+
this.MaxLevelOfDetail = Get(MaxDetailLevel_ID, Mod.MaxMaxLevelOfDetail);
333+
this.MinLevelOfDetail = Get(MinDetailLevel_ID, CarLevelOfDetail.Level.Speck);
352334
Get(EnableUnrestrictedOpponentColors_ID, false);
335+
Get(ShowDataEffectInGhostMode_ID, false);
353336

354337
// Experimental (disabled by default)
355338
Get(EnableSteamRivals_ID, false);

Distance.ReplayIntensifies/Distance.ReplayIntensifies.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@
9090
<Compile Include="Harmony\Assembly-CSharp\LevelSelectLeaderboardButton\OnDisplayedVirtual.cs" />
9191
<Compile Include="Harmony\Assembly-CSharp\LevelSelectLeaderboardMenu\Display.cs" />
9292
<Compile Include="Harmony\Assembly-CSharp\LevelSelectLeaderboardMenu\Update.cs" />
93-
<Compile Include="Harmony\Assembly-CSharp\PlayerDataReplay\CreatePlayerDataReplay.cs" />
93+
<Compile Include="Harmony\Assembly-CSharp\PlayerDataReplay\get_CarScreenType_.cs" />
94+
<Compile Include="Harmony\Assembly-CSharp\PlayerDataReplay\get_CreateCarScreen_.cs" />
95+
<Compile Include="Harmony\Assembly-CSharp\PlayerDataReplay\InitCarVirtual.cs" />
9496
<Compile Include="Harmony\Assembly-CSharp\PlayerDataReplay\OnEventReplayOptionsMenuClosed.cs" />
9597
<Compile Include="Helpers\SteamworksHelper.cs" />
9698
<Compile Include="Scripts\PlayerDataReplayCompoundData.cs" />

Distance.ReplayIntensifies/Harmony/Assembly-CSharp/CarLevelOfDetail/SetLevelOfDetail.cs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using HarmonyLib;
2-
using System;
3-
using UnityEngine;
42

53
namespace Distance.ReplayIntensifies.Harmony
64
{
@@ -16,25 +14,29 @@ internal static class CarLevelOfDetail__SetLevelOfDetail
1614
[HarmonyPrefix]
1715
internal static bool Prefix(CarLevelOfDetail __instance, CarLevelOfDetail.Level newLevel)
1816
{
17+
CarLevelOfDetail.Level origNewLevel = newLevel;
18+
1919
// Avoid clamping if the max is unchanged (InFocusFP), just in-case other mods mess with this...
2020
// NOTE: Highest LOD is lowest-value enum, so compare using less than.
2121
CarLevelOfDetail.Level maxLevel = Mod.Instance.Config.MaxLevelOfDetailCached;
22+
CarLevelOfDetail.Level minLevel = Mod.Instance.Config.MinLevelOfDetailCached;
2223

2324
// NOTE: Replay-type cars will NOT RENDER A SOLID BODY at Speck, so force the minimum to VeryFar.
2425
// Interestingly, networked cars WILL render a solid body at Speck.
2526
if (__instance.type_ == CarLevelOfDetail.Type.Replay && maxLevel > CarLevelOfDetail.Level.VeryFar)
2627
{
27-
maxLevel = CarLevelOfDetail.Level.VeryFar;
28+
maxLevel = CarLevelOfDetail.Level.VeryFar; // Max LOD cannot be lower than Very Far, when in replay mode.
2829
}
29-
if (newLevel < maxLevel && maxLevel != CarLevelOfDetail.Level.InFocusFP)
30+
31+
// Exclude In-Focus/In-Focus FP from max LOD checks (these are only used for replay cars with camera focus).
32+
if (newLevel >= CarLevelOfDetail.Level.Near && newLevel < maxLevel /*&& maxLevel != CarLevelOfDetail.Level.InFocusFP*/)
3033
{
3134
newLevel = maxLevel;
3235
}
3336
else
3437
{
3538
// Avoid clamping if the min is unchanged (Speck), just in-case other mods mess with this...
3639
// MaxLevel has priority over MinLevel (when the range is invalid)
37-
CarLevelOfDetail.Level minLevel = Mod.Instance.Config.MinLevelOfDetailCached;
3840
if (newLevel > minLevel && minLevel != CarLevelOfDetail.Level.Speck)
3941
{
4042
newLevel = minLevel;
@@ -47,6 +49,25 @@ internal static bool Prefix(CarLevelOfDetail __instance, CarLevelOfDetail.Level
4749
__instance.levelSwitchTimer_ = 0f;
4850
}
4951

52+
// The NitronicCarController houses the most CPU-intensive processes for a car's LOD.
53+
// These processes are so intensive, that they can actually mess up warp anchor physics when used with too many cars.
54+
// (i.e. try playing Breakout with ~40-50 ghosts, >= Medium min LOD, and replay/networked car styles)
55+
CarLevelOfDetail.Level controllerLevel = (__instance.type_ == CarLevelOfDetail.Type.Ghost) ? CarLevelOfDetail.Level.InFocus : CarLevelOfDetail.Level.Medium;
56+
57+
// To fix this, we have a threshold to disable the NitronicCarController regardless of what LOD it'd be enabled at.
58+
if (minLevel <= controllerLevel && origNewLevel > CarLevelOfDetail.Level.InFocus)
59+
{
60+
// Override the NitronicCarController enabled state if our min LOD forces it to always be enabled.
61+
// But only override the NitronicCarController enabled state when the car isn't *actually* in-focus.
62+
__instance.nitronicCarController_.enabled = (PlayerDataReplay.ReplayPlayers_.Count <= 20);
63+
}
64+
else
65+
{
66+
// Default enabled state, though this could be changed by modifying `controllerLevel`.
67+
// (e.g. it may be beneficial to place the controllerLevel at Near for non-ghost detail types).
68+
__instance.nitronicCarController_.enabled = (newLevel <= controllerLevel);// && PlayerDataReplay.ReplayPlayers_.Count <= 20;
69+
}
70+
5071
return false; // Don't run original method
5172
}
5273
}

Distance.ReplayIntensifies/Harmony/Assembly-CSharp/PlayerDataReplay/CreatePlayerDataReplay.cs

Lines changed: 0 additions & 121 deletions
This file was deleted.

Distance.ReplayIntensifies/Harmony/Assembly-CSharp/PlayerDataReplay/GetLevelOfDetailType.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
using Distance.ReplayIntensifies.Scripts;
22
using HarmonyLib;
3-
using System;
4-
using UnityEngine;
53

64
namespace Distance.ReplayIntensifies.Harmony
75
{
86
/// <summary>
97
/// Patch to change the value of <see cref="PlayerDataReplay.simulateNetworkCar_"/> for the duration of the function.
108
/// </summary>
119
/// <remarks>
12-
/// Required For: Car Visual Style (part 2/5).
10+
/// Required For: Car Visual Style (part 3/7).
1311
/// </remarks>
1412
[HarmonyPatch(typeof(PlayerDataReplay), nameof(PlayerDataReplay.GetLevelOfDetailType))]
1513
internal static class PlayerDataReplay__GetLevelOfDetailType

0 commit comments

Comments
 (0)