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.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CustomPaintings")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("CustomPaintings")]
[assembly: AssemblyTitle("CustomPaintings")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[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 CustomPaintings
{
[BepInPlugin("UnderratedJunk.CustomPaintings", "CustomPaintings", "1.0.7")]
public class CustomPaintings : BaseUnityPlugin
{
[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}");
CustomPaintingsSwap.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() == CustomPaintingsSwap.ModState.Client)
{
PhotonNetwork.AddCallbackTarget((object)sync);
}
if (swapper.GetModState() == CustomPaintingsSwap.ModState.Host)
{
CustomPaintingsSwap.HostSeed = Random.Range(0, int.MaxValue);
logger.LogInfo($"Generated Hostseed: {CustomPaintingsSwap.HostSeed}");
PhotonNetwork.AddCallbackTarget((object)sync);
sync.SendSeed(CustomPaintingsSwap.HostSeed);
}
}
}
[HarmonyPatch(typeof(NetworkConnect), "TryJoiningRoom")]
public class JoinLobbyPatch
{
private static void Prefix()
{
if (swapper.GetModState() != 0)
{
swapper.SetState(CustomPaintingsSwap.ModState.Client);
}
}
}
[HarmonyPatch(typeof(SteamManager), "HostLobby")]
public class HostLobbyPatch
{
private static bool Prefix()
{
swapper.SetState(CustomPaintingsSwap.ModState.Host);
return true;
}
}
[HarmonyPatch(typeof(SteamManager), "LeaveLobby")]
public class LeaveLobbyPatch
{
private static void Postfix()
{
PhotonNetwork.RemoveCallbackTarget((object)sync);
swapper.SetState(CustomPaintingsSwap.ModState.SinglePlayer);
}
}
private static Logger logger;
private static CustomPaintingsLoader loader;
private static CustomPaintingsSwap swapper;
private static CustomPaintingsSync sync;
public static int? receivedSeed = null;
public static readonly int maxWaitTimeMs = 1000;
private readonly Harmony harmony = new Harmony("UnderratedJunk.CustomPaintings");
private void Awake()
{
logger = new Logger("CustomPaintings");
logger.LogInfo("CustomPaintings mod initialized.");
loader = new CustomPaintingsLoader(logger);
loader.LoadImagesFromAllPlugins();
swapper = new CustomPaintingsSwap(logger, loader);
sync = new CustomPaintingsSync(logger);
harmony.PatchAll();
}
}
public class CustomPaintingsLoader
{
private const string IMAGE_FOLDER_NAME = "CustomPaintings";
private readonly Logger logger;
public List<Material> LoadedMaterials { get; } = new List<Material>();
public CustomPaintingsLoader(Logger logger)
{
this.logger = logger;
}
public void LoadImagesFromAllPlugins()
{
string text = Path.Combine(Paths.PluginPath);
if (!Directory.Exists(text))
{
logger.LogWarning("Plugins directory not found: " + text);
return;
}
string[] directories = Directory.GetDirectories(text, "CustomPaintings", SearchOption.AllDirectories);
if (directories.Length == 0)
{
logger.LogWarning("No 'CustomPaintings' folders found in plugins.");
return;
}
string[] array = directories;
foreach (string text2 in array)
{
logger.LogInfo("Loading images from: " + text2);
LoadImagesFromDirectory(text2);
}
}
private void LoadImagesFromDirectory(string directoryPath)
{
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: 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
};
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: {LoadedMaterials.Count}");
}
private Texture2D LoadTextureFromFile(string filePath)
{
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: 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 CustomPaintingsSwap
{
public enum ModState
{
Host,
Client,
SinglePlayer
}
private readonly Logger logger;
private readonly CustomPaintingsLoader 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 ModState GetModState()
{
return currentState;
}
public CustomPaintingsSwap(Logger logger, CustomPaintingsLoader 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 void ReplacePaintings()
{
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
if (currentState == ModState.SinglePlayer)
{
Seed = randomSeed;
}
if (currentState == ModState.Host)
{
Seed = HostSeed;
}
if (currentState == ModState.Client)
{
Seed = ReceivedSeed;
}
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;
Scene activeScene2 = SceneManager.GetActiveScene();
GameObject[] rootGameObjects = ((Scene)(ref activeScene2)).GetRootGameObjects();
for (int i = 0; i < rootGameObjects.Length; i++)
{
MeshRenderer[] componentsInChildren = rootGameObjects[i].GetComponentsInChildren<MeshRenderer>();
foreach (MeshRenderer val in componentsInChildren)
{
Material[] sharedMaterials = ((Renderer)val).sharedMaterials;
for (int k = 0; k < sharedMaterials.Length; k++)
{
num++;
if ((Object)(object)sharedMaterials[k] != (Object)null && ((Object)sharedMaterials[k]).name.ToLower().Contains("painting") && !((Object)sharedMaterials[k]).name.Contains("Painting Frame Vertical Gold") && !((Object)sharedMaterials[k]).name.Contains("Painting Frame Horizontal Gold") && loader.LoadedMaterials.Count > 0)
{
int index = Mathf.Abs((Seed + paintingsChangedCount) % loader.LoadedMaterials.Count);
sharedMaterials[k] = loader.LoadedMaterials[index];
paintingsChangedCount++;
}
}
((Renderer)val).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 CustomPaintingsSync : MonoBehaviourPunCallbacks, IOnEventCallback
{
private readonly Logger logger;
public const byte SeedEventCode = 1;
public CustomPaintingsSync(Logger logger)
{
this.logger = logger;
}
public void SendSeed(int seed)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: 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_002c: 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_0034: Expected O, but got Unknown
//IL_0037: 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}");
CustomPaintingsSwap.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);
}
}
}
}