11using Reactor . API . Configuration ;
22using System ;
3- using System . Collections ;
43using System . Collections . Generic ;
4+ using System . Linq ;
55using UnityEngine ;
66
77namespace Distance . ReplayIntensifies
@@ -72,14 +72,22 @@ public CarLevelOfDetail.Type ReplayDetailType
7272 public CarLevelOfDetail . Level MaxLevelOfDetail
7373 {
7474 get => Get < CarLevelOfDetail . Level > ( MaxDetailLevel_ID ) ;
75- set => Set ( MaxDetailLevel_ID , value ) ;
75+ set
76+ {
77+ Set ( MaxDetailLevel_ID , value ) ;
78+ this . MaxLevelOfDetailCached = value ;
79+ }
7680 }
7781
7882 private const string MinDetailLevel_ID = "visual.min_level_of_detail" ;
7983 public CarLevelOfDetail . Level MinLevelOfDetail
8084 {
8185 get => Get < CarLevelOfDetail . Level > ( MinDetailLevel_ID ) ;
82- set => Set ( MinDetailLevel_ID , value ) ;
86+ set
87+ {
88+ Set ( MinDetailLevel_ID , value ) ;
89+ this . MinLevelOfDetailCached = value ;
90+ }
8391 }
8492
8593 private const string EnableUnrestrictedOpponentColors_ID = "visual.unrestricted_colors" ;
@@ -89,16 +97,93 @@ public bool EnableUnrestrictedOpponentColors
8997 set => Set ( EnableUnrestrictedOpponentColors_ID , value ) ;
9098 }
9199
100+
101+ private const string UseRivalStyleForGhosts_ID = "visual.use_rival_style_for_ghosts" ;
102+ public bool UseRivalStyleForGhosts
103+ {
104+ get => Get < bool > ( UseRivalStyleForGhosts_ID ) ;
105+ set => Set ( UseRivalStyleForGhosts_ID , value ) ;
106+ }
107+
108+ private const string UseRivalStyleForReplays_ID = "visual.use_rival_style_for_replays" ;
109+ public bool UseRivalStyleForReplays
110+ {
111+ get => Get < bool > ( UseRivalStyleForReplays_ID ) ;
112+ set => Set ( UseRivalStyleForReplays_ID , value ) ;
113+ }
114+
115+ private const string UseRivalStyleForSelf_ID = "visual.use_rival_style_for_self" ;
116+ public bool UseRivalStyleForSelf
117+ {
118+ get => Get < bool > ( UseRivalStyleForSelf_ID ) ;
119+ set => Set ( UseRivalStyleForSelf_ID , value ) ;
120+ }
121+
122+ private const string RivalBrightness_ID = "visual.rival_brightness" ;
123+ public float RivalBrightness
124+ {
125+ get => Get < float > ( RivalBrightness_ID ) ;
126+ set => Set ( RivalBrightness_ID , value ) ;
127+ }
128+
129+ private const string RivalOutline_ID = "visual.rival_outline" ;
130+ public bool RivalOutline
131+ {
132+ get => Get < bool > ( RivalOutline_ID , true ) ;
133+ set => Set ( RivalOutline_ID , value ) ;
134+ }
135+
136+ private const string RivalDetailType_ID = "visual.rival_detail_type" ;
137+ public CarLevelOfDetail . Type RivalDetailType
138+ {
139+ get => Get < CarLevelOfDetail . Type > ( RivalDetailType_ID ) ;
140+ set => Set ( RivalDetailType_ID , value ) ;
141+ }
142+
143+ private const string EnableSteamRivals_ID = "rivals.enabled" ;
144+ public bool EnableSteamRivals
145+ {
146+ // We can't be having this feature enabled for non-steam builds
147+ get => SteamworksManager . IsSteamBuild_ && Get < bool > ( EnableSteamRivals_ID ) ;
148+ set => Set ( EnableSteamRivals_ID , value ) ;
149+ }
150+
151+ private const string HighlightRivalsInLeaderboards_ID = "rivals.highlight_in_leaderboards" ;
152+ public bool HighlightRivalsInLeaderboards
153+ {
154+ get => Get < bool > ( HighlightRivalsInLeaderboards_ID ) ;
155+ set => Set ( HighlightRivalsInLeaderboards_ID , value ) ;
156+ }
157+
158+ private const string SteamRivals_ID = "rivals.steam_ids" ;
159+ public Dictionary < ulong , string > SteamRivals
160+ {
161+ get => Convert < Dictionary < ulong , string > > ( SteamRivals_ID , new Dictionary < ulong , string > ( ) , overwriteNull : true ) ;
162+ private set => Set ( SteamRivals_ID , value ) ;
163+ }
164+
165+ #endregion
166+
167+ #region Cached
168+
169+ // Cached property values for faster accessing.
170+ public CarLevelOfDetail . Level MaxLevelOfDetailCached { get ; private set ; }
171+ public CarLevelOfDetail . Level MinLevelOfDetailCached { get ; private set ; }
172+
92173 #endregion
93174
94175 #region Helpers
95176
96- public CarLevelOfDetail . Type GetCarDetailType ( bool isGhost )
177+ public CarLevelOfDetail . Type GetCarDetailType ( bool isGhost , bool isCarRival )
97178 {
179+ if ( isCarRival )
180+ {
181+ return this . RivalDetailType ;
182+ }
98183 return ( isGhost ) ? this . GhostDetailType : this . ReplayDetailType ;
99184 }
100185
101- public void SetCarDetailType ( bool isGhost , CarLevelOfDetail . Type value )
186+ /* public void SetCarDetailType(bool isGhost, CarLevelOfDetail.Type value)
102187 {
103188 if (isGhost)
104189 {
@@ -108,14 +193,18 @@ public void SetCarDetailType(bool isGhost, CarLevelOfDetail.Type value)
108193 {
109194 this.ReplayDetailType = value;
110195 }
111- }
196+ }*/
112197
113- public bool GetCarOutline ( bool isGhost )
198+ public bool GetCarOutline ( bool isGhost , bool isCarRival )
114199 {
200+ if ( isCarRival )
201+ {
202+ return this . RivalOutline ;
203+ }
115204 return ( isGhost ) ? this . GhostOutline : this . ReplayOutline ;
116205 }
117206
118- public void SetCarOutline ( bool isGhost , bool value )
207+ /* public void SetCarOutline(bool isGhost, bool value)
119208 {
120209 if (isGhost)
121210 {
@@ -125,6 +214,95 @@ public void SetCarOutline(bool isGhost, bool value)
125214 {
126215 this.ReplayOutline = value;
127216 }
217+ }*/
218+
219+ // Determines if this car should be displayed as a Steam Rival (which accounts for settings like 'Use rival style for ghosts/replays', etc.).
220+ public bool IsCarSteamRival ( bool isGhost , long userID ) => IsCarSteamRival ( isGhost , unchecked ( ( ulong ) userID ) ) ;
221+
222+ public bool IsCarSteamRival ( bool isGhost , ulong userID )
223+ {
224+ if ( this . EnableSteamRivals &&
225+ ( ( isGhost && this . UseRivalStyleForGhosts ) || ( ! isGhost && this . UseRivalStyleForReplays ) ) )
226+ {
227+ return IsSteamRival ( userID , false ) ;
228+ }
229+ return false ;
230+ }
231+
232+ #endregion
233+
234+ #region Steam Rivals
235+
236+ // The `excludeSelf` parameter exists in-case there are situations where we want to identify rivals for non-ghost/replay reasons.
237+ public bool IsSteamRival ( long userID , bool excludeSelf = false ) => IsSteamRival ( unchecked ( ( ulong ) userID ) , excludeSelf ) ;
238+
239+ public bool IsSteamRival ( ulong userID , bool excludeSelf = false )
240+ {
241+ if ( SteamworksManager . GetSteamID ( ) == userID )
242+ {
243+ return ! excludeSelf && this . UseRivalStyleForSelf ; // SteamRivals ignores your own user ID in the list.
244+ }
245+ return this . SteamRivals . ContainsKey ( userID ) ;
246+ }
247+
248+ // For getting the name comment attached to a rival. These currently aren't used by the mod though.
249+ public bool TryGetSteamRival ( long userID , out string nameComment ) => TryGetSteamRival ( unchecked ( ( ulong ) userID ) , out nameComment ) ;
250+
251+ public bool TryGetSteamRival ( ulong userID , out string nameComment )
252+ {
253+ return this . SteamRivals . TryGetValue ( userID , out nameComment ) ;
254+ }
255+
256+ public bool AddSteamRival ( long userID , string nameComment , bool autoSave = true ) => AddSteamRival ( unchecked ( ( ulong ) userID ) , nameComment , autoSave ) ;
257+
258+ public bool AddSteamRival ( ulong userID , string nameComment , bool autoSave = true )
259+ {
260+ if ( nameComment == null )
261+ {
262+ nameComment = string . Empty ; // Default to empty string I guess? It doesn't really matter either way, but would be more user friendly.
263+ }
264+
265+ var steamRivals = this . SteamRivals ;
266+ if ( ! steamRivals . ContainsKey ( userID ) )
267+ {
268+ steamRivals [ userID ] = nameComment ; // Name comment to make identifying users in Config.json easier.
269+ if ( autoSave )
270+ {
271+ Save ( ) ;
272+ }
273+ return true ;
274+ }
275+ return false ;
276+ }
277+
278+ public bool RemoveSteamRival ( long userID , bool autoSave = true ) => RemoveSteamRival ( unchecked ( ( ulong ) userID ) , autoSave ) ;
279+
280+ public bool RemoveSteamRival ( ulong userID , bool autoSave = true )
281+ {
282+ if ( this . SteamRivals . Remove ( userID ) )
283+ {
284+ if ( autoSave )
285+ {
286+ Save ( ) ;
287+ }
288+ return true ;
289+ }
290+ return false ;
291+ }
292+
293+ // Shorthand function to reduce the time spent on lookup when a large number of userIDs are in a single collection.
294+ public int CountSteamRivals ( IEnumerable < long > userIDs , bool countSelf = true )
295+ {
296+ return CountSteamRivals ( userIDs . Select ( ( userID ) => unchecked ( ( ulong ) userID ) ) , countSelf ) ;
297+ }
298+
299+ public int CountSteamRivals ( IEnumerable < ulong > userIDs , bool countSelf = true )
300+ {
301+ //var useRivalStyleForSelf = this.UseRivalStyleForSelf;
302+ ulong selfID = SteamworksManager . GetSteamID ( ) ;
303+ var steamRivals = this . SteamRivals ;
304+
305+ return userIDs . Count ( ( userID ) => ( userID == selfID ? countSelf : steamRivals . ContainsKey ( userID ) ) ) ;
128306 }
129307
130308 #endregion
@@ -153,10 +331,21 @@ public void Awake()
153331 Get ( GhostDetailType_ID , CarLevelOfDetail . Type . Ghost ) ;
154332 Get ( ReplayOutline_ID , false ) ;
155333 Get ( ReplayDetailType_ID , CarLevelOfDetail . Type . Replay ) ;
156- Get ( MaxDetailLevel_ID , CarLevelOfDetail . Level . InFocusFP ) ;
157- Get ( MinDetailLevel_ID , CarLevelOfDetail . Level . Speck ) ;
334+ this . MaxLevelOfDetailCached = Get ( MaxDetailLevel_ID , CarLevelOfDetail . Level . InFocusFP ) ;
335+ this . MinLevelOfDetailCached = Get ( MinDetailLevel_ID , CarLevelOfDetail . Level . Speck ) ;
158336 Get ( EnableUnrestrictedOpponentColors_ID , false ) ;
159337
338+ // Experimental (disabled by default)
339+ Get ( EnableSteamRivals_ID , false ) ;
340+ Convert ( SteamRivals_ID , new Dictionary < ulong , string > ( ) , overwriteNull : true ) ;
341+ Get ( HighlightRivalsInLeaderboards_ID , true ) ;
342+ Get ( UseRivalStyleForGhosts_ID , true ) ;
343+ Get ( UseRivalStyleForReplays_ID , false ) ;
344+ Get ( UseRivalStyleForSelf_ID , false ) ;
345+ Get ( RivalBrightness_ID , 1.0f ) ;
346+ Get ( RivalOutline_ID , true ) ;
347+ Get ( RivalDetailType_ID , CarLevelOfDetail . Type . Networked ) ;
348+
160349 // Save settings, and any defaults that may have been added.
161350 Save ( ) ;
162351 }
@@ -172,6 +361,19 @@ public void Set<T>(string key, T value)
172361 Save ( ) ;
173362 }
174363
364+ public T Convert < T > ( string key , T @default = default , bool overwriteNull = false )
365+ {
366+ // Assign the object back after conversion, this allows for deep nested settings
367+ // that can be preserved and updated without reassigning to the root property.
368+ var value = Config . GetOrCreate ( key , @default ) ;
369+ if ( overwriteNull && value == null )
370+ {
371+ value = @default ;
372+ }
373+ Config [ key ] = value ;
374+ return value ;
375+ }
376+
175377 public void Save ( )
176378 {
177379 Config ? . Save ( ) ;
0 commit comments