using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MuckMapFix.Model;
using UnityEngine;
using UnityEngine.AI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("MuckMapFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Fixes Muck's blank map (M) on Linux: draws the island texture through the default UI material.")]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: AssemblyInformationalVersion("0.2.1+1911ecd07d605294f346388086fc942f4c18d4fa")]
[assembly: AssemblyProduct("Map Fix")]
[assembly: AssemblyTitle("MuckMapFix")]
[assembly: InternalsVisibleTo("MuckMapFix.Tests")]
[assembly: AssemblyVersion("0.2.1.0")]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
internal sealed class IsReadOnlyAttribute : Attribute
{
}
}
namespace MuckMapFix
{
[HarmonyPatch(typeof(Map), "GenerateMap")]
internal static class GenerateMapPatch
{
private static readonly FieldInfo MapRenderField = AccessTools.Field(typeof(Map), "mapRender");
[HarmonyPostfix]
private static void Postfix(Map __instance)
{
try
{
Apply(__instance);
}
catch (Exception arg)
{
MapFixPlugin.Log.LogError((object)$"Map Fix could not redraw the map, so it stays vanilla: {arg}");
}
}
private static void Apply(Map map)
{
bool flag = (Object)(object)map != (Object)null;
Object val = (Object)(flag ? /*isinst with value type is only supported in some contexts*/: null);
Material val2 = (flag ? map.mapTextureMaterial : null);
Texture materialTexture = (((Object)(object)val2 != (Object)null) ? val2.mainTexture : null);
MapFixChoice<Texture2D> mapFixChoice = MapFixRules.Choose<Texture2D>(MapFixPlugin.Cfg.Enabled.Value, flag, val != (Object)null, materialTexture, IsVanillaMapTexture);
if (mapFixChoice.Skip == MapFixSkip.Disabled)
{
return;
}
if (mapFixChoice.Skip != MapFixSkip.None)
{
MapFixPlugin.Log.LogWarning((object)$"Map Fix left the map vanilla: {mapFixChoice.Skip}.");
return;
}
PropertyInfo propertyInfo = AccessTools.Property(((object)val).GetType(), "texture");
PropertyInfo propertyInfo2 = AccessTools.Property(((object)val).GetType(), "material");
if (propertyInfo == null || propertyInfo2 == null)
{
MapFixPlugin.Log.LogWarning((object)("Map Fix left the map vanilla: " + ((object)val).GetType().FullName + " has no texture or material property."));
return;
}
Texture2D texture = mapFixChoice.Texture;
byte[] rawTextureData = texture.GetRawTextureData();
MapFixRules.MakeOpaque(rawTextureData);
texture.LoadRawTextureData(rawTextureData);
((Texture)texture).filterMode = (FilterMode)0;
texture.Apply(false);
propertyInfo.SetValue(val, texture);
propertyInfo2.SetValue(val, null);
MapFixPlugin.Log.LogInfo((object)$"Map Fix drew the {((Texture)texture).width}x{((Texture)texture).height} map texture through the default UI material.");
}
private static bool IsVanillaMapTexture(Texture2D texture)
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Invalid comparison between Unknown and I4
if ((Object)(object)texture != (Object)null && ((Texture)texture).isReadable && (int)texture.format == 4)
{
return ((Texture)texture).mipmapCount == 1;
}
return false;
}
}
internal sealed class MapFixConfig
{
public readonly ConfigEntry<bool> Enabled;
public readonly ConfigEntry<bool> MobPositionFix;
public MapFixConfig(ConfigFile cfg)
{
Enabled = cfg.Bind<bool>("General", "Enabled", true, "Draw the map (M) through the default UI material so the island shows on Linux. Off leaves the map exactly as vanilla. Only changes what this player sees.");
MobPositionFix = cfg.Bind<bool>("Mobs", "PositionFix", true, "On a client, move each enemy all the way to the host's position, and jump it there when it is more than 4 m off. Vanilla only corrects enemies on one axis, so they drift and other players see them somewhere else. The host is unaffected.");
}
}
[BepInPlugin("evan.muck.mapfix", "Map Fix", "0.2.1")]
public class MapFixPlugin : BaseUnityPlugin
{
public const string Guid = "evan.muck.mapfix";
public const string Name = "Map Fix";
public const string Version = "0.2.1";
internal static ManualLogSource Log;
internal static MapFixConfig Cfg;
private static readonly HashSet<string> Logged = new HashSet<string>();
private void Awake()
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
Cfg = new MapFixConfig(((BaseUnityPlugin)this).Config);
Harmony val = new Harmony("evan.muck.mapfix");
try
{
val.CreateClassProcessor(typeof(GenerateMapPatch)).Patch();
}
catch (Exception arg)
{
Log.LogError((object)$"Map Fix failed to patch Map.GenerateMap, so the map stays vanilla: {arg}");
}
try
{
val.CreateClassProcessor(typeof(MobPositionPatches)).Patch();
}
catch (Exception arg2)
{
val.Unpatch((MethodBase)AccessTools.Method(typeof(Mob), "SetPosition", (Type[])null, (Type[])null), (HarmonyPatchType)0, "evan.muck.mapfix");
val.Unpatch((MethodBase)AccessTools.Method(typeof(Mob), "UpdateOffsetPosition", (Type[])null, (Type[])null), (HarmonyPatchType)0, "evan.muck.mapfix");
Log.LogError((object)$"Map Fix failed to patch Mob position sync, so enemies sync as in vanilla: {arg2}");
}
try
{
val.CreateClassProcessor(typeof(StaleInteractPatch)).Patch();
}
catch (Exception arg3)
{
Log.LogError((object)$"Map Fix failed to patch DetectInteractables.Update, so the interact prompt stays vanilla: {arg3}");
}
Log.LogInfo((object)("Map Fix 0.2.1 loaded. Map redraw " + (Cfg.Enabled.Value ? "on" : "off") + ", enemy position fix " + (Cfg.MobPositionFix.Value ? "on" : "off") + "."));
}
internal static void LogOnce(string key, string message)
{
if (Logged.Add(key))
{
Log.LogError((object)message);
}
}
}
[HarmonyPatch]
internal static class MobPositionPatches
{
private static readonly FieldRef<Mob, Vector3> OffsetField = AccessTools.FieldRefAccess<Mob, Vector3>("offsetPosition");
private static readonly FieldRef<Mob, float> SyncSpeed = AccessTools.FieldRefAccess<Mob, float>("syncSpeed");
private static bool Active
{
get
{
if (MapFixPlugin.Cfg != null && MapFixPlugin.Cfg.MobPositionFix.Value)
{
return !LocalClient.serverOwner;
}
return false;
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(Mob), "SetPosition")]
private static bool SetPositionPrefix(Mob __instance, Vector3 nextPosition)
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: 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_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: 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_004b: Unknown result type (might be due to invalid IL or missing references)
if (!Active)
{
return true;
}
try
{
Vector3 val = nextPosition - ((Component)__instance).transform.position;
if (MobSyncRules.ShouldSnap(((Vector3)(ref val)).magnitude))
{
NavMeshAgent agent = __instance.agent;
if ((Object)(object)agent != (Object)null && ((Behaviour)agent).enabled && agent.isOnNavMesh)
{
agent.Warp(nextPosition);
}
else
{
((Component)__instance).transform.position = nextPosition;
}
OffsetField.Invoke(__instance) = Vector3.zero;
return false;
}
OffsetField.Invoke(__instance) = val;
return false;
}
catch (Exception arg)
{
MapFixPlugin.LogOnce("set-position", $"Map Fix: mob position sync failed, vanilla handles it. {arg}");
return true;
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(Mob), "UpdateOffsetPosition")]
private static bool UpdateOffsetPositionPrefix(Mob __instance)
{
//IL_0014: 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_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: 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_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
if (!Active)
{
return true;
}
Vector3 val = OffsetField.Invoke(__instance);
if (val == Vector3.zero)
{
return false;
}
(Offset Move, Offset Left) tuple = MobSyncRules.Glide(new Offset(val.x, val.y, val.z), SyncSpeed.Invoke(__instance), Time.deltaTime);
Offset item = tuple.Move;
Offset item2 = tuple.Left;
Transform transform = ((Component)__instance).transform;
transform.position += new Vector3(item.X, item.Y, item.Z);
OffsetField.Invoke(__instance) = new Vector3(item2.X, item2.Y, item2.Z);
return false;
}
}
[HarmonyPatch(typeof(DetectInteractables), "Update")]
internal static class StaleInteractPatch
{
private static readonly FieldInfo CurrentCollider = AccessTools.Field(typeof(DetectInteractables), "currentCollider");
private static void Prefix(DetectInteractables __instance)
{
if (__instance.currentInteractable == null)
{
CurrentCollider.SetValue(__instance, null);
}
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "MuckMapFix";
public const string PLUGIN_NAME = "Map Fix";
public const string PLUGIN_VERSION = "0.2.1";
}
}
namespace MuckMapFix.Model
{
internal enum MapFixSkip
{
None,
Disabled,
NoMap,
NoMapRender,
NoTexture
}
internal readonly struct MapFixChoice<T> where T : class
{
public readonly MapFixSkip Skip;
public readonly T Texture;
public MapFixChoice(MapFixSkip skip, T texture)
{
Skip = skip;
Texture = texture;
}
}
internal static class MapFixRules
{
public static MapFixChoice<T> Choose<T>(bool enabled, bool hasMap, bool hasMapRender, object materialTexture, Func<T, bool> usable) where T : class
{
if (!enabled)
{
return new MapFixChoice<T>(MapFixSkip.Disabled, null);
}
if (!hasMap)
{
return new MapFixChoice<T>(MapFixSkip.NoMap, null);
}
if (!hasMapRender)
{
return new MapFixChoice<T>(MapFixSkip.NoMapRender, null);
}
if (!(materialTexture is T val) || !usable(val))
{
return new MapFixChoice<T>(MapFixSkip.NoTexture, null);
}
return new MapFixChoice<T>(MapFixSkip.None, val);
}
public static void MakeOpaque(byte[] rgba32)
{
for (int i = 3; i < rgba32.Length; i += 4)
{
rgba32[i] = byte.MaxValue;
}
}
}
internal readonly struct Offset
{
public readonly float X;
public readonly float Y;
public readonly float Z;
public float Length => (float)Math.Sqrt(X * X + Y * Y + Z * Z);
public Offset(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public Offset Scale(float s)
{
return new Offset(X * s, Y * s, Z * s);
}
}
internal static class MobSyncRules
{
public const float SnapDistance = 4f;
public static bool ShouldSnap(float distance)
{
return distance > 4f;
}
public static (Offset Move, Offset Left) Glide(Offset remaining, float speed, float deltaTime)
{
float length = remaining.Length;
if (length <= 0f || speed <= 0f || deltaTime <= 0f)
{
return (Move: new Offset(0f, 0f, 0f), Left: remaining);
}
float num = Math.Min(length, speed * deltaTime);
Offset item = remaining.Scale(num / length);
return (Move: item, Left: new Offset(remaining.X - item.X, remaining.Y - item.Y, remaining.Z - item.Z));
}
}
}