using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace FittingPaintings
{
[BepInPlugin("ZeroTails.FittingPaintings", "FittingPaintings", "1.0.2")]
public class FittingPaintings : BaseUnityPlugin
{
public class PaintingGroup
{
public string paintingType;
public string paintingFolderName;
public HashSet<string> targetMaterials;
public List<Material> loadedMaterials;
public PaintingGroup(string paintingType, string paintingFolderName, HashSet<string> targetMaterials)
{
this.paintingType = paintingType;
this.paintingFolderName = paintingFolderName;
this.targetMaterials = targetMaterials;
loadedMaterials = new List<Material>();
}
}
[HarmonyPatch(typeof(LoadingUI), "LevelAnimationComplete")]
public class PaintingSwapPatch
{
private static void Postfix()
{
Task.Run(async delegate
{
int waited = 0;
int interval = 50;
while (!receivedSeed.HasValue && waited < maxWaitTimeMs)
{
await Task.Delay(interval);
waited += interval;
}
if (receivedSeed.HasValue)
{
logger.LogInfo($"[Postfix] Client using received seed: {receivedSeed.Value}");
FittingPaintingsSwap.ReceivedSeed = receivedSeed.Value;
receivedSeed = null;
}
else
{
logger.LogWarning("[Postfix] Client did not receive seed in time. Proceeding without it.");
}
swapper.ReplacePaintings();
});
}
private static void Prefix()
{
if (swapper.GetModState() == FittingPaintingsSwap.ModState.Client)
{
PhotonNetwork.AddCallbackTarget((object)sync);
}
if (swapper.GetModState() == FittingPaintingsSwap.ModState.Host)
{
FittingPaintingsSwap.HostSeed = Random.Range(0, int.MaxValue);
logger.LogInfo($"Generated Hostseed: {FittingPaintingsSwap.HostSeed}");
PhotonNetwork.AddCallbackTarget((object)sync);
sync.SendSeed(FittingPaintingsSwap.HostSeed);
}
}
}
[HarmonyPatch(typeof(NetworkConnect), "TryJoiningRoom")]
public class JoinLobbyPatch
{
private static void Prefix()
{
logger.LogInfo("JoinLobbyPatch Prefix called.");
if (swapper.GetModState() == FittingPaintingsSwap.ModState.SinglePlayer)
{
swapper.SetState(FittingPaintingsSwap.ModState.Client);
}
}
}
[HarmonyPatch(typeof(SteamManager), "HostLobby")]
public class HostLobbyPatch
{
private static bool Prefix()
{
logger.LogInfo("HostLobbyPatch Prefix called.");
if (swapper.GetModState() != 0)
{
swapper.SetState(FittingPaintingsSwap.ModState.Host);
}
return true;
}
}
[HarmonyPatch(typeof(SteamManager), "LeaveLobby")]
public class LeaveLobbyPatch
{
private static void Postfix()
{
PhotonNetwork.RemoveCallbackTarget((object)sync);
swapper.SetState(FittingPaintingsSwap.ModState.SinglePlayer);
}
}
public static List<PaintingGroup> paintingGroups = new List<PaintingGroup>
{
new PaintingGroup("Landscape", "FittingSwapLandscapePaintings", new HashSet<string> { "Painting_H_Landscape" }),
new PaintingGroup("Portrait", "FittingSwapPortraitPaintings", new HashSet<string> { "Painting_V_Furman", "painting teacher01", "painting teacher02", "painting teacher03", "painting teacher04", "Painting_S_Tree" }),
new PaintingGroup("Square", "FittingSwapSquarePaintings", new HashSet<string> { "Painting_S_Creep", "Painting_S_Creep 2_0", "Painting_S_Creep 2", "Painting Wizard Class" })
};
private static Logger logger = null;
private static FittingPaintingsLoader loader = null;
private static FittingPaintingsSwap swapper = null;
private static FittingPaintingsSync sync = null;
public static int? receivedSeed = null;
public static readonly int maxWaitTimeMs = 3000;
private readonly Harmony harmony = new Harmony("ZeroTails.FittingPaintings");
private void Awake()
{
logger = new Logger("FittingPaintings");
logger.LogInfo("FittingPaintings mod initialized.");
loader = new FittingPaintingsLoader(logger);
loader.LoadImagesFromAllPlugins();
swapper = new FittingPaintingsSwap(logger, loader);
sync = new FittingPaintingsSync(logger);
harmony.PatchAll();
}
}
public class FittingPaintingsLoader
{
private readonly Logger logger;
public List<Material> LoadedMaterials { get; } = new List<Material>();
public FittingPaintingsLoader(Logger logger)
{
this.logger = logger;
}
public void LoadImagesFromAllPlugins()
{
string pluginPath = Paths.PluginPath;
if (!Directory.Exists(pluginPath))
{
logger.LogWarning("Plugins directory not found: " + pluginPath);
return;
}
foreach (FittingPaintings.PaintingGroup paintingGroup in FittingPaintings.paintingGroups)
{
string[] directories = Directory.GetDirectories(pluginPath, paintingGroup.paintingFolderName, SearchOption.AllDirectories);
if (directories.Length == 0)
{
logger.LogWarning("No '" + paintingGroup.paintingFolderName + "' folders found in plugins.");
continue;
}
string[] array = directories;
foreach (string text in array)
{
logger.LogInfo("Loading images from: " + text);
LoadImagesFromDirectory(paintingGroup, text);
}
}
}
private void LoadImagesFromDirectory(FittingPaintings.PaintingGroup paintingGroup, string directoryPath)
{
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_00e8: Expected O, but got Unknown
if (!Directory.Exists(directoryPath))
{
logger.LogWarning("Directory does not exist: " + directoryPath);
return;
}
string[] validExtensions = new string[4] { ".png", ".jpg", ".jpeg", ".bmp" };
string[] array = (from file in Directory.EnumerateFiles(directoryPath, "*.*", SearchOption.AllDirectories)
where validExtensions.Contains(Path.GetExtension(file).ToLower())
select file).ToArray();
if (array.Length == 0)
{
logger.LogWarning("No images found in " + directoryPath);
return;
}
for (int i = 0; i < array.Length; i++)
{
string text = array[i];
Texture2D val = LoadTextureFromFile(text);
if ((Object)(object)val != (Object)null)
{
Material item = new Material(Shader.Find("Standard"))
{
mainTexture = (Texture)(object)val
};
paintingGroup.loadedMaterials.Add(item);
logger.LogInfo($"Loaded image #{i + 1}: {Path.GetFileName(text)}");
}
else
{
logger.LogWarning($"Failed to load image #{i + 1}: {text}");
}
}
logger.LogInfo($"Total images loaded: {paintingGroup.loadedMaterials.Count}");
}
private Texture2D? LoadTextureFromFile(string filePath)
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Expected O, but got Unknown
byte[] array = File.ReadAllBytes(filePath);
Texture2D val = new Texture2D(2, 2);
if (ImageConversion.LoadImage(val, array))
{
val.Apply();
return val;
}
return null;
}
}
public class FittingPaintingsSwap
{
public enum ModState
{
Host,
Client,
SinglePlayer
}
private readonly Logger logger;
private readonly FittingPaintingsLoader loader;
private static int randomSeed = 0;
public static int HostSeed = 0;
public static int ReceivedSeed = 0;
public static int Seed = 0;
private int paintingsChangedCount;
private static ModState currentState = ModState.SinglePlayer;
public FittingPaintingsSwap(Logger logger, FittingPaintingsLoader loader)
{
this.logger = logger;
this.loader = loader;
logger.LogInfo($"Initial ModState: {currentState}");
if (randomSeed == 0)
{
randomSeed = Random.Range(0, int.MaxValue);
logger.LogInfo($"Generated initial random seed: {randomSeed}");
}
}
public ModState GetModState()
{
return currentState;
}
public void ReplacePaintings()
{
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
ModState modState = currentState;
if (1 == 0)
{
}
int seed = modState switch
{
ModState.SinglePlayer => randomSeed,
ModState.Host => HostSeed,
ModState.Client => ReceivedSeed,
_ => Seed,
};
if (1 == 0)
{
}
Seed = seed;
Scene activeScene = SceneManager.GetActiveScene();
logger.LogInfo($"Applying seed {Seed} for painting swaps in scene: {((Scene)(ref activeScene)).name}");
logger.LogInfo("Replacing all materials containing 'painting' with custom images...");
paintingsChangedCount = 0;
int num = 0;
GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects();
foreach (GameObject val in rootGameObjects)
{
MeshRenderer[] componentsInChildren = val.GetComponentsInChildren<MeshRenderer>();
foreach (MeshRenderer val2 in componentsInChildren)
{
Material[] sharedMaterials = ((Renderer)val2).sharedMaterials;
for (int k = 0; k < sharedMaterials.Length; k++)
{
num++;
foreach (FittingPaintings.PaintingGroup paintingGroup in FittingPaintings.paintingGroups)
{
if ((Object)(object)sharedMaterials[k] != (Object)null && paintingGroup.targetMaterials.Contains(((Object)sharedMaterials[k]).name) && paintingGroup.loadedMaterials.Count > 0)
{
int index = Mathf.Abs((Seed + paintingsChangedCount) % paintingGroup.loadedMaterials.Count);
sharedMaterials[k] = paintingGroup.loadedMaterials[index];
paintingsChangedCount++;
}
}
}
((Renderer)val2).sharedMaterials = sharedMaterials;
}
}
logger.LogInfo($"Total materials checked: {num}");
logger.LogInfo($"Total paintings changed in this scene: {paintingsChangedCount}");
logger.LogInfo($"RandomSeed = {randomSeed}");
logger.LogInfo($"HostSeed = {HostSeed}");
logger.LogInfo($"ReceivedSeed = {ReceivedSeed}");
}
public void SetState(ModState newState)
{
currentState = newState;
logger.LogInfo($"Mod state set to: {currentState}");
}
}
public class FittingPaintingsSync : MonoBehaviourPunCallbacks, IOnEventCallback
{
private readonly Logger logger;
public const byte SeedEventCode = 1;
public FittingPaintingsSync(Logger logger)
{
this.logger = logger;
}
public void SendSeed(int seed)
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Expected O, but got Unknown
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
object[] array = new object[1] { seed };
logger.LogInfo("Sharing seed with other clients");
RaiseEventOptions val = new RaiseEventOptions
{
Receivers = (ReceiverGroup)1,
CachingOption = (EventCaching)4
};
PhotonNetwork.RaiseEvent((byte)1, (object)array, val, SendOptions.SendReliable);
}
public void OnEvent(EventData photonEvent)
{
if (photonEvent.Code == 1)
{
int num = (int)((object[])photonEvent.CustomData)[0];
logger.LogInfo($"Received seed: {num}");
FittingPaintingsSwap.ReceivedSeed = num;
}
}
}
public class Logger
{
private readonly string logFilePath;
private readonly ManualLogSource logSource;
public Logger(string modName)
{
string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
logFilePath = Path.Combine(directoryName, modName + "_log.txt");
if (File.Exists(logFilePath))
{
File.Delete(logFilePath);
}
logSource = Logger.CreateLogSource(modName);
}
public void LogInfo(string message)
{
WriteLog("INFO", message);
logSource.LogInfo((object)message);
}
public void LogWarning(string message)
{
WriteLog("WARNING", message);
logSource.LogWarning((object)message);
}
public void LogError(string message)
{
WriteLog("ERROR", message);
logSource.LogError((object)message);
}
private void WriteLog(string level, string message)
{
string text = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{level}] {message}";
File.AppendAllText(logFilePath, text + Environment.NewLine);
}
public void LogMaterial(Material material)
{
if ((Object)(object)material != (Object)null && ((Object)material).name.ToLower().Contains("painting"))
{
LogInfo("Material containing 'painting': " + ((Object)material).name);
}
}
public void ClearLog()
{
if (File.Exists(logFilePath))
{
File.Delete(logFilePath);
}
}
}
}
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}