Manager partial class handling game difficulty settings and persistence. It stores current difficulty, tracks which difficulties have been beaten in a small JSON file, exposes helpers for names, descriptions and label colors, toggles lobby map objects when difficulty changes, and provides gameplay multipliers for enemy health and late damage scaling.
using Sandbox;
using Sandbox.Diagnostics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
public partial class Manager
{
private static string FilePath => "difficulty_wins.json";
[Sync] public int Difficulty { get; set; }
public const int MinDifficulty = 0;
public const int MaxDifficulty = 2;
private Dictionary<int, bool> DifficultyVictories = new();
public int FirstDifficultyWithoutPauseChoosing = 3;
public int FirstDifficultyWithCurses = 2;
//private GameObject _effectsObj;
[Property] public Dictionary<int, GameObject> DifficultyEffects { get; set; }
public void RegisterVictoryForDifficulty( int difficulty )
{
DifficultyVictories[difficulty] = true;
SaveDifficultyVictories();
}
void SaveDifficultyVictories()
{
FileSystem.Data.WriteJson( FilePath, DifficultyVictories );
}
void LoadDifficultyVictories()
{
DifficultyVictories = FileSystem.Data.ReadJsonSafe<Dictionary<int, bool>>( FilePath, new Dictionary<int, bool>() );
}
public bool HasBeatenDifficulty( int difficulty )
{
//return true;
//return false;
return DifficultyVictories.ContainsKey( difficulty ) && DifficultyVictories[difficulty];
}
public static string GetNameForDifficulty( int difficulty )
{
switch ( difficulty )
{
case -1: return "All Difficulties";
case 0: default: return "Normal Difficulty";
case 1: return "Expert Difficulty";
case 2: return "Cursed Difficulty";
}
}
public static string GetDescriptionForDifficulty( int difficulty )
{
switch ( difficulty )
{
case -1: return "";
case 0: default: return "";
case 1: return "";
case 2: return "";
}
}
public static Color GetDifficultyLabelColor( int difficulty )
{
switch ( difficulty )
{
case -1: return new Color( 1f, 1f, 1f, 0.3f );
case 0: default: return new Color( 1f, 1f, 1f, 0.5f );
case 1: return new Color( 1f, 0f, 0f, 0.7f );
case 2: return new Color( 0.7f, 0.2f, 1f, 0.7f );
}
}
[Rpc.Broadcast( NetFlags.Reliable )]
public void SetDifficulty( int diff )
{
for ( int i = 0; i <= MaxDifficulty; i++ )
{
LobbyMaps[i]?.Enabled = diff == i;
LobbyMapObjects[i]?.Enabled = diff == i;
}
Fade( fadeIn: true );
if ( !Networking.IsHost )
return;
if( Difficulty == diff )
return;
Difficulty = diff;
GameSettingsSystem.Save();
//Log.Info( $"Set difficulty to {diff} ({GetNameForDifficulty( diff )})" );
}
//[Rpc.Broadcast] public void SetDifficultyEffect( int diff )
//{
// if ( _effectsObj != null )
// {
// _effectsObj.Enabled = false;
// _effectsObj = null;
// }
// if ( DifficultyEffects != null && DifficultyEffects.ContainsKey( diff ) && DifficultyEffects[diff] != null )
// {
// _effectsObj = DifficultyEffects[diff];
// _effectsObj.Enabled = true;
// }
//}
public float GetEnemyHealthModifier()
{
switch ( Players.Count )
{
case 1: default: return 1f;
case 2: return 1.25f;
case 3: return 1.5f;
}
}
/// <summary>Returns a multiplier for non-self damage taken by the player. 1x until 20 min, ramps to 3x at 50 min, continues linearly forever.</summary>
public float GetLateDamageMultiplier()
{
var minutes = ElapsedTime / 60f;
if ( minutes <= 20f )
return 1f;
return 1f + (minutes - 20f) * (2f / 30f);
}
}