using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FistVR;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyCompany("NGA")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Persistent player progression! Raid, stash loot, and deploy with seemless scene/loadout saving.")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("NGA.SosigFacesForModders")]
[assembly: AssemblyTitle("BepInEx Plugin Title")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[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 BepInEx
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
internal sealed class BepInAutoPluginAttribute : Attribute
{
public BepInAutoPluginAttribute(string id = null, string name = null, string version = null)
{
}
}
}
namespace BepInEx.Preloader.Core.Patching
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
internal sealed class PatcherAutoPluginAttribute : Attribute
{
public PatcherAutoPluginAttribute(string id = null, string name = null, string version = null)
{
}
}
}
namespace NGA.SosigFacesForModders
{
[BepInPlugin("NGA.SosigFacesForModders", "SosigFacesForModders", "0.0.1")]
[BepInDependency("nrgill28.Sodalite", "1.0.0")]
[BepInProcess("h3vr.exe")]
public class SosigFaces : BaseUnityPlugin
{
[HarmonyPatch(typeof(Sosig))]
[HarmonyPatch("ProcessDamage")]
[HarmonyPatch(new Type[]
{
typeof(float),
typeof(float),
typeof(float),
typeof(float),
typeof(Vector3),
typeof(SosigLink)
})]
public class SosigProcessDamage : MonoBehaviour
{
private static void Postfix(Sosig __instance, float damage_p, float damage_c, float damage_b, float damage_t, Vector3 point, SosigLink link)
{
if (!checkSkip)
{
((Component)__instance).GetComponent<EnemyFaceController>()?.SetState("Damaged");
}
}
}
[HarmonyPatch(typeof(Sosig))]
[HarmonyPatch("SosigDies")]
public class SosigSosigDies : MonoBehaviour
{
private static void Postfix(Sosig __instance, DamageClass damClass, SosigDeathType deathType)
{
if (!checkSkip)
{
((Component)__instance).GetComponent<EnemyFaceController>()?.SetState("Dead");
}
}
}
[HarmonyPatch(typeof(Sosig))]
[HarmonyPatch("BrainUpdate")]
public class SosigBrainUpdate : MonoBehaviour
{
private static void Postfix(Sosig __instance)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Expected I4, but got Unknown
if (!checkSkip)
{
bool flag = false;
SosigOrder currentOrder = __instance.CurrentOrder;
SosigOrder val = currentOrder;
switch ((int)val)
{
case 0:
case 1:
case 3:
case 4:
case 6:
case 8:
case 9:
case 10:
flag = false;
break;
default:
flag = true;
break;
}
string state = (flag ? "Assaulting" : "Idle");
((Component)__instance).GetComponent<EnemyFaceController>()?.SetState(state);
}
}
}
[HarmonyPatch(typeof(Sosig))]
[HarmonyPatch("Start")]
public class SosigAwake : MonoBehaviour
{
private static void Postfix(Sosig __instance)
{
LogWarning("Trying spawn sosig Start 1");
if (checkSkip)
{
return;
}
foreach (SosigLink link in __instance.Links)
{
Transform transform = ((Component)link).transform;
if (((Object)((Component)link).transform).name != "Sosig_Torso")
{
LogWarning("Link not Sosig_Torso");
continue;
}
if ((Object)(object)((Component)link).transform == (Object)(object)GM.CurrentPlayerBody.Torso)
{
LogWarning("This is the player's torso");
break;
}
Transform val = transform.Find("Head");
if ((Object)(object)val == (Object)null)
{
LogWarning("Head not found!");
break;
}
EnemyFaceController enemyFaceController = ((Component)__instance).gameObject.AddComponent<EnemyFaceController>();
enemyFaceController.Init(val);
}
}
}
[HarmonyPatch(typeof(Sosig), "Configure")]
public class SosigConfig : MonoBehaviour
{
private static void Postfix(Sosig __instance, SosigConfigTemplate t)
{
if ((Object)(object)t == (Object)null)
{
LogMessage("[Sosig.Configure] SosigConfigTemplate was null!");
}
__instance.TestingSosigTemplate = t;
if ((Object)(object)__instance.TestingSosigTemplate == (Object)null)
{
LogMessage("[Sosig.Configure] TestingSosigTemplate is null after setting config!");
}
}
}
private class FaceThingComponent : MonoBehaviour
{
public GameObject attached_face = null;
}
public class EnemyFaceController : MonoBehaviour
{
private Material faceMaterial;
private SosigFaceModConfig activeModConfig;
private Coroutine damageCoroutine;
public GameObject attached_face = null;
private Renderer faceRenderer = null;
private string currentState = "";
private bool dead = false;
private bool damaged = false;
private static readonly int MainTexID = Shader.PropertyToID("_MainTex");
private static readonly int EmissionTexID = Shader.PropertyToID("_EmissionMap");
public void Init(Transform headTransform)
{
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_0347: Unknown result type (might be due to invalid IL or missing references)
//IL_0351: Expected O, but got Unknown
Sosig component = ((Component)this).GetComponent<Sosig>();
if ((Object)(object)component == (Object)null || (Object)(object)component.TestingSosigTemplate == (Object)null)
{
Logger.LogError((object)"[EnemyFaceController] Sosig or required references not initialized.");
Object.Destroy((Object)(object)this);
return;
}
SosigEnemyID sosigID = GetIDFromConfig(component.TestingSosigTemplate);
activeModConfig = allConfigs.FirstOrDefault((SosigFaceModConfig c) => c.SosigEnemyIdsApplyTo != null && c.SosigEnemyIdsApplyTo.Contains((int)sosigID));
if (activeModConfig == null)
{
activeModConfig = allConfigs.FirstOrDefault((SosigFaceModConfig c) => c.ApplyToAllSosigs);
}
if (activeModConfig == null)
{
LogMessage($"[EnemyFaceController] No SosigFaceModConfig found for ID: {sosigID}. Destroying component.");
Object.Destroy((Object)(object)this);
return;
}
bool flag = false;
List<string> list = new List<string>();
if (activeModConfig.UseCustomMaterialInFace && !string.IsNullOrEmpty(activeModConfig.CustomMaterialInFaceName))
{
Renderer[] array = component.Links.SelectMany((SosigLink link) => ((Component)link).gameObject.GetComponentsInChildren<Renderer>()).ToArray();
Renderer[] array2 = array;
foreach (Renderer val in array2)
{
for (int j = 0; j < val.sharedMaterials.Length; j++)
{
Material val2 = val.sharedMaterials[j];
list.Add(((Object)val2).name);
if ((Object)(object)val2 != (Object)null && ((Object)val2).name.Equals(activeModConfig.CustomMaterialInFaceName))
{
Material[] materials = val.materials;
faceMaterial = materials[j];
val.materials = materials;
LoadEmotionsForMod(activeModConfig);
faceRenderer = val;
flag = true;
LogMessage("[EnemyFaceController] Found and instantiated custom material: " + activeModConfig.CustomMaterialInFaceName + " on object: " + ((Object)((Component)val).gameObject).name);
break;
}
}
if (flag)
{
break;
}
}
}
if (!flag && activeModConfig.UseCustomMaterialInFace)
{
Logger.LogError((object)("Did not match any materials with name::::->" + activeModConfig.CustomMaterialInFaceName + " (Instance)"));
foreach (string item in list)
{
Logger.LogError((object)item);
}
}
if (!activeModConfig.UseCustomMaterialInFace && !flag && (Object)(object)attached_face != (Object)null)
{
attached_face = SpawnFaceOnHead(headTransform);
Transform val3 = attached_face.transform.Find("default");
if (!((Object)(object)val3 != (Object)null))
{
Logger.LogError((object)"'default' object not found among children of face.");
Object.Destroy((Object)(object)this);
return;
}
Renderer component2 = ((Component)val3).GetComponent<Renderer>();
if (!((Object)(object)component2 != (Object)null))
{
Logger.LogError((object)"Renderer component not found on the 'default' object.");
Object.Destroy((Object)(object)this);
return;
}
faceMaterial = new Material(component2.material);
component2.material = faceMaterial;
faceRenderer = component2;
LogMessage("[EnemyFaceController] Using default attached_face material.");
}
SetState("Idle");
}
public void SetState(string state)
{
if ((Object)(object)faceMaterial == (Object)null || activeModConfig == null || dead)
{
return;
}
List<Texture2D> list = null;
switch (state)
{
case "Dead":
if (damageCoroutine != null)
{
((MonoBehaviour)this).StopCoroutine(damageCoroutine);
}
list = GetTexturesForEmotion("Dead");
SetMaterialTextures(list);
dead = true;
currentState = "Dead";
break;
case "Idle":
if (!damaged && state != currentState)
{
currentState = state;
list = GetTexturesForEmotion("Idle");
SetMaterialTextures(list);
}
break;
case "Assaulting":
if (!damaged && state != currentState)
{
currentState = state;
list = GetTexturesForEmotion("Assaulting");
SetMaterialTextures(list);
}
break;
case "Damaged":
if (!damaged)
{
if (damageCoroutine != null)
{
((MonoBehaviour)this).StopCoroutine(damageCoroutine);
}
list = GetTexturesForEmotion("Damaged");
SetMaterialTextures(list);
damageCoroutine = ((MonoBehaviour)this).StartCoroutine(DamageTimeout());
}
break;
}
}
private void SetMaterialTextures(List<Texture2D> textures)
{
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Expected O, but got Unknown
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Expected O, but got Unknown
if (!((Object)(object)faceMaterial == (Object)null) && !((Object)(object)faceRenderer == (Object)null) && (textures == null || textures.Count != 0))
{
Texture2D randomTexture = GetRandomTexture(textures);
faceMaterial.SetTexture(MainTexID, (Texture)(object)randomTexture);
if (activeModConfig.MakeCustomEmissiveUseTexture)
{
MaterialPropertyBlock val = new MaterialPropertyBlock();
faceRenderer.GetPropertyBlock(val);
val.SetTexture(EmissionTexID, (Texture)(object)randomTexture);
faceRenderer.SetPropertyBlock(val);
faceMaterial.EnableKeyword("_EMISSION");
}
else
{
MaterialPropertyBlock val2 = new MaterialPropertyBlock();
faceRenderer.GetPropertyBlock(val2);
val2.SetTexture(EmissionTexID, (Texture)null);
faceRenderer.SetPropertyBlock(val2);
faceMaterial.DisableKeyword("_EMISSION");
}
}
}
private IEnumerator DamageTimeout()
{
damaged = true;
yield return (object)new WaitForSeconds(2f);
SetState(currentState);
damaged = false;
}
private List<Texture2D> GetTexturesForEmotion(string emotion)
{
string text = activeModConfig.ModFolderName + emotion;
if (textures_cache != null && textures_cache.TryGetValue(text, out var value))
{
return value;
}
Logger.LogError((object)("[EnemyFaceController] No textures found in cache for key: " + text + "."));
return new List<Texture2D>();
}
private Texture2D GetRandomTexture(List<Texture2D> textures)
{
if (textures == null || textures.Count == 0)
{
return null;
}
return textures[Random.Range(0, textures.Count)];
}
private static GameObject SpawnFaceOnHead(Transform headTransform)
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
if (!IM.OD.TryGetValue("NGA_FaceThing", out var value))
{
Logger.LogError((object)"NGA_FaceThing not found in IM.OD!");
return null;
}
GameObject val = Object.Instantiate<GameObject>(((AnvilAsset)value).GetGameObject(), Vector3.zero, Quaternion.identity);
val.transform.SetParent(headTransform, false);
val.transform.localPosition = Vector3.zero;
val.transform.localRotation = Quaternion.identity;
return val;
}
}
private static ConfigEntry<bool> GameEnabled;
private static ConfigEntry<bool> DebugMode;
private static bool checkSkip;
private static bool checkVerbose;
public static List<SosigFaceModConfig> allConfigs;
public static Dictionary<string, List<Texture2D>> textures_cache = new Dictionary<string, List<Texture2D>>();
public static Dictionary<SosigConfigTemplate, SosigEnemyID> configToIDMap = null;
internal static ManualLogSource Logger { get; private set; }
private void Awake()
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Expected O, but got Unknown
Logger = ((BaseUnityPlugin)this).Logger;
Harmony val = new Harmony("NGA.SosigFacesForModders");
LogMessage("New harmony");
SetUpConfigFields();
LogMessage("Setted the fields");
Init();
val.PatchAll();
LogMessage("Hello, world! Sent from NGA.SosigFacesForModders 0.0.1");
}
private void SetUpConfigFields()
{
GameEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Overall", "ON/OFF.", true, "Turn mod On / Off");
DebugMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug Mode", "ON/OFF.", false, "Enable verbose logging in commandline.");
}
private static bool CheckSkip()
{
checkSkip = !GameEnabled.Value;
return checkSkip;
}
private static bool CheckVerbose()
{
checkVerbose = DebugMode.Value;
return checkVerbose;
}
private void Init()
{
LoadConfigs(searchFoldersForFaceJsons());
CheckSkip();
CheckVerbose();
}
private static List<string> searchFoldersForFaceJsons()
{
try
{
string pluginPath = Paths.PluginPath;
string[] files = Directory.GetFiles(pluginPath, "sffm_*.json", SearchOption.AllDirectories);
return new List<string>(files);
}
catch (Exception ex)
{
Logger.LogError((object)("Error while searching for face jsons: " + ex.Message));
return new List<string>();
}
}
public static void LoadConfigs(List<string> allJsons)
{
allConfigs = new List<SosigFaceModConfig>();
foreach (string allJson in allJsons)
{
if (!TryLoadJson<SosigFaceModConfigList>(allJson, out var obj, out var error))
{
Logger.LogError((object)("Bazinga! You coudln't parse SosigFaceModConfig list: " + error + " for file " + allJson));
}
else if (obj != null && obj.Configs != null)
{
allConfigs.AddRange(obj.Configs);
LogMessage($"Loaded {obj.Configs.Count} config(s) from file: {allJson}");
}
else
{
Logger.LogWarning((object)("Config wrapper loaded from " + allJson + ", but 'Configs' list was null or empty."));
}
}
}
public static bool TryLoadJson<T>(string filePath, out T obj, out string error) where T : new()
{
obj = default(T);
error = null;
try
{
if (!File.Exists(filePath))
{
error = "File not found: " + filePath;
return false;
}
string text = File.ReadAllText(filePath);
obj = JsonUtility.FromJson<T>(text);
if (obj == null)
{
error = "JsonUtility.FromJson returned null for " + typeof(T).Name;
return false;
}
return true;
}
catch (Exception ex)
{
error = "LoadJson failed: " + ex.Message;
Logger.LogError((object)error);
return false;
}
}
private static void LoadEmotionsForMod(SosigFaceModConfig mod)
{
//IL_010f: Unknown result type (might be due to invalid IL or missing references)
//IL_0116: Expected O, but got Unknown
string path = Path.Combine(Paths.PluginPath, mod.ModFolderName);
Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>
{
{ "Idle", mod.IdleFacesFileNames },
{ "Assaulting", mod.AssaultingFacesFileNames },
{ "Damaged", mod.DamagedFacesFileNames },
{ "Dead", mod.DeadFacesFileNames }
};
foreach (KeyValuePair<string, List<string>> item in dictionary)
{
string key = item.Key;
List<string> value = item.Value;
if (value == null || value.Count == 0)
{
continue;
}
string text = mod.ModFolderName + key;
if (textures_cache.ContainsKey(text))
{
continue;
}
List<Texture2D> list = new List<Texture2D>();
foreach (string item2 in value)
{
string text2 = Path.Combine(path, item2);
if (File.Exists(text2))
{
try
{
byte[] array = File.ReadAllBytes(text2);
Texture2D val = new Texture2D(2, 2);
val.LoadImage(array);
list.Add(val);
}
catch (Exception ex)
{
Logger.LogError((object)("[SosigFaceMod] Failed to load texture at path: " + text2 + ". Error: " + ex.Message));
}
}
else
{
LogMessage("[SosigFaceMod] Texture file not found: " + text2);
}
}
if (list.Count > 0)
{
textures_cache.Add(text, list);
LogMessage($"[SosigFaceMod] Cached {list.Count} textures for {text}.");
}
}
}
public static void BuildReverseLookupMap()
{
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
LogMessage("Starting to build SosigConfigTemplate reverse lookup map...");
configToIDMap = new Dictionary<SosigConfigTemplate, SosigEnemyID>();
foreach (KeyValuePair<SosigEnemyID, SosigEnemyTemplate> item in ManagerSingleton<IM>.Instance.odicSosigObjsByID)
{
SosigEnemyID key = item.Key;
SosigEnemyTemplate value = item.Value;
List<SosigConfigTemplate> configTemplates = value.ConfigTemplates;
if (configTemplates == null || configTemplates.Count == 0)
{
LogWarning($"Enemy Template {((Object)value).name} (ID: {key}) has an empty or null ConfigTemplates list.");
continue;
}
foreach (SosigConfigTemplate item2 in configTemplates)
{
if ((Object)(object)item2 != (Object)null && !configToIDMap.ContainsKey(item2))
{
configToIDMap.Add(item2, key);
}
}
}
LogMessage($"Finished building map. Total unique Config Templates mapped: {configToIDMap.Count}");
}
public static SosigEnemyID GetIDFromConfig(SosigConfigTemplate config)
{
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
if (configToIDMap == null)
{
BuildReverseLookupMap();
}
if ((Object)(object)config == (Object)null)
{
Logger.LogError((object)"[GetIDFromConfig]: Given config was null!");
return (SosigEnemyID)0;
}
if (configToIDMap.TryGetValue(config, out var value))
{
return value;
}
Logger.LogError((object)("Config Template " + ((Object)config).name + " not found in reverse lookup map!"));
return (SosigEnemyID)0;
}
public static void LogMessage(string str)
{
if (checkVerbose)
{
Logger.LogMessage((object)str);
}
}
public static void LogWarning(string str)
{
if (checkVerbose)
{
Logger.LogWarning((object)str);
}
}
}
[Serializable]
public class SosigFaceModConfig
{
public string ModFolderName;
public float xPosOffset;
public float yPosOffset;
public float zPosOffset;
public List<string> IdleFacesFileNames;
public List<string> AssaultingFacesFileNames;
public List<string> DamagedFacesFileNames;
public List<string> DeadFacesFileNames;
public bool ApplyToAllSosigs;
public List<int> SosigEnemyIdsApplyTo;
public bool UseCustomMaterialInFace;
public string CustomMaterialInFaceName;
public bool MakeCustomEmissiveUseTexture;
}
[Serializable]
public class SosigFaceModConfigList
{
public List<SosigFaceModConfig> Configs;
}
}