Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of WaypointTeleportsOdinHorse v1.2.0
WaypointTeleportsOdinHorse.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [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("RDMods")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Teleports your tamed OdinHorse with you when using Waypoints.")] [assembly: AssemblyFileVersion("1.2.0.0")] [assembly: AssemblyInformationalVersion("1.2.0")] [assembly: AssemblyProduct("WaypointTeleportsOdinHorse")] [assembly: AssemblyTitle("WaypointTeleportsOdinHorse")] [assembly: AssemblyVersion("1.2.0.0")] namespace RDMods.WaypointTeleportsOdinHorse; [BepInPlugin("RDMods.WaypointTeleportsOdinHorse", "WaypointTeleportsOdinHorse", "1.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class WaypointTeleportsOdinHorse : BaseUnityPlugin { private sealed class SnapshottedHorse { public ZDOID ZdoId; public float RegisteredDistance; public string Name; } private sealed class PendingHorseTeleport { public Vector3 OriginPos; public Vector3 TargetPos; public float RegisteredAt; public readonly List<SnapshottedHorse> QualifiedHorses = new List<SnapshottedHorse>(); } [HarmonyPatch(typeof(Player), "TeleportTo", new Type[] { typeof(Vector3), typeof(Quaternion), typeof(bool) })] private static class PlayerTeleportTo_Patch { private static void Prefix(Player __instance, ref Vector3 __state) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) __state = ((Component)__instance).transform.position; LogDebug("TELEPORT_TO Prefix: Player=" + __instance.GetPlayerName() + ", " + $"OriginPos=({__state.x:F1}, {__state.y:F1}, {__state.z:F1}), " + $"m_teleporting={IsWaypointTeleporting()}"); } private static void Postfix(Player __instance, Vector3 pos, Quaternion rot, bool __result, Vector3 __state) { //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) try { if (!__result || (Object)(object)__instance == (Object)null) { LogDebug("TELEPORT_TO Postfix: TeleportTo returned false or player null — not queuing"); } else if (!IsWaypointTeleporting()) { LogDebug("TELEPORT_TO Postfix: not a waypoint teleport — not queuing"); } else { RegisterPendingTeleport(__instance, __state, pos); } } catch (Exception arg) { LogError($"CRITICAL ERROR in TeleportTo Postfix: {arg}"); } } } [HarmonyPatch(typeof(Player), "UpdateTeleport")] private static class PlayerUpdateTeleport_Patch { private static void Postfix(Player __instance) { try { if (!((Object)(object)__instance == (Object)null) && PendingTeleports.Count != 0) { long playerID = __instance.GetPlayerID(); if (PendingTeleports.ContainsKey(playerID)) { TryCompletePendingTeleport(__instance); } } } catch (Exception arg) { LogError($"CRITICAL ERROR in UpdateTeleport Postfix: {arg}"); } } } public const string PluginGUID = "RDMods.WaypointTeleportsOdinHorse"; public const string PluginName = "WaypointTeleportsOdinHorse"; public const string PluginVersion = "1.2.0"; internal static ConfigEntry<bool> EnableDebugLogging; internal static ManualLogSource PluginLogger; private static FieldInfo _waypointTeleportingField; private static readonly Dictionary<long, PendingHorseTeleport> PendingTeleports = new Dictionary<long, PendingHorseTeleport>(); private const float PendingTimeoutSeconds = 180f; private const float MaxFollowDistance = 20f; private const float ScatterRadius = 3f; private void Awake() { //IL_0074: Unknown result type (might be due to invalid IL or missing references) PluginLogger = ((BaseUnityPlugin)this).Logger; EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDebugLogging", true, "Enables detailed debug logging for troubleshooting. Set to false to reduce log spam."); LogInfo("WaypointTeleportsOdinHorse v1.2.0 initializing..."); LogInfo("Debug logging: " + (EnableDebugLogging.Value ? "ENABLED" : "DISABLED")); if (!CacheReflectionMembers()) { LogError("CRITICAL: Failed to cache Waypoints reflection members. Plugin will be disabled."); return; } new Harmony("RDMods.WaypointTeleportsOdinHorse").PatchAll(); LogInfo("Harmony patches applied (Player.TeleportTo, Player.UpdateTeleport)."); LogInfo("WaypointTeleportsOdinHorse initialized successfully."); } private bool CacheReflectionMembers() { try { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "Waypoints"); if (assembly == null) { LogError("Waypoints assembly not found in loaded assemblies. Is Waypoints mod installed?"); return false; } LogDebug("Found Waypoints assembly: " + assembly.FullName); Type type = assembly.GetType("Waypoints.Behaviors.Waypoint"); if (type == null) { LogError("Waypoints.Behaviors.Waypoint type not found in Waypoints assembly."); return false; } _waypointTeleportingField = AccessTools.Field(type, "m_teleporting"); if (_waypointTeleportingField == null) { LogError("Waypoint.m_teleporting field not found."); return false; } LogDebug("Cached m_teleporting field: " + _waypointTeleportingField.Name); return true; } catch (Exception arg) { LogError($"Exception during reflection caching: {arg}"); return false; } } internal static void LogDebug(string message) { if (EnableDebugLogging != null && EnableDebugLogging.Value) { PluginLogger.LogInfo((object)message); } } internal static void LogInfo(string message) { PluginLogger.LogInfo((object)message); } internal static void LogError(string message) { PluginLogger.LogError((object)message); } private static bool IsWaypointTeleporting() { try { if (_waypointTeleportingField == null) { return false; } object value = _waypointTeleportingField.GetValue(null); bool flag = default(bool); int num; if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch (Exception ex) { LogDebug("IsWaypointTeleporting check failed: " + ex.Message); return false; } } private static void SnapshotQualifyingHorses(Player player, Vector3 originPos, PendingHorseTeleport pending) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_007b: 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) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) pending.QualifiedHorses.Clear(); foreach (Character allCharacter in Character.GetAllCharacters()) { if ((Object)(object)allCharacter == (Object)null || !((Object)allCharacter).name.Contains("rae_OdinHorse")) { continue; } Vector3 position = ((Component)allCharacter).transform.position; LogDebug(" SNAPSHOT candidate: name=" + ((Object)allCharacter).name + ", " + $"pos=({position.x:F1}, {position.y:F1}, {position.z:F1})"); if ((Object)(object)((Component)allCharacter).GetComponent<Tameable>() == (Object)null) { LogDebug(" SKIP: No Tameable component (wild/untamed)"); continue; } float num = Vector3.Distance(originPos, position); if (num > 20f) { LogDebug($" SKIP: Too far ({num:F1}m > {20f}m)"); continue; } MonsterAI component = ((Component)allCharacter).GetComponent<MonsterAI>(); if ((Object)(object)component == (Object)null) { LogDebug(" SKIP: No MonsterAI component"); continue; } GameObject followTarget = component.GetFollowTarget(); if ((Object)(object)followTarget != (Object)(object)((Component)player).gameObject) { LogDebug(" SKIP: Not actively following player (followTarget=" + (((followTarget != null) ? ((Object)followTarget).name : null) ?? "null") + ")"); continue; } ZNetView component2 = ((Component)allCharacter).GetComponent<ZNetView>(); if ((Object)(object)component2 == (Object)null || !component2.IsValid()) { LogDebug(" SKIP: No valid ZNetView"); continue; } ZDOID uid = component2.GetZDO().m_uid; pending.QualifiedHorses.Add(new SnapshottedHorse { ZdoId = uid, RegisteredDistance = num, Name = ((Object)allCharacter).name }); LogDebug($" QUALIFIED: ZDOID={uid}, distance={num:F1}m " + "(ownership implied by follow target)"); } LogDebug($"SNAPSHOT complete: {pending.QualifiedHorses.Count} horse(s) qualified"); } private static void RegisterPendingTeleport(Player player, Vector3 originPos, Vector3 targetPos) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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_0015: 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_0060: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) long playerID = player.GetPlayerID(); PendingHorseTeleport pendingHorseTeleport = new PendingHorseTeleport { OriginPos = originPos, TargetPos = targetPos, RegisteredAt = Time.time }; SnapshotQualifyingHorses(player, originPos, pendingHorseTeleport); PendingTeleports[playerID] = pendingHorseTeleport; LogDebug("PENDING: Registered for " + player.GetPlayerName() + ", " + $"Origin=({originPos.x:F1}, {originPos.y:F1}, {originPos.z:F1}), " + $"Target=({targetPos.x:F1}, {targetPos.y:F1}, {targetPos.z:F1}), " + $"Qualified={pendingHorseTeleport.QualifiedHorses.Count}"); } private static void ClearPending(long playerId, string reason) { if (PendingTeleports.Remove(playerId)) { LogDebug($"PENDING: Cleared for playerId={playerId} ({reason})"); } } private static bool PlayerLandedAtDestination(Player player, PendingHorseTeleport pending) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) float num = Vector3.Distance(((Component)player).transform.position, pending.OriginPos); float num2 = Vector3.Distance(((Component)player).transform.position, pending.TargetPos); bool num3 = num2 < num; if (!num3) { LogDebug("PENDING: Player did not land at destination " + $"(distToOrigin={num:F1}m, distToTarget={num2:F1}m) — horse stays put"); } return num3; } private static bool IsDestinationAreaReady(Player player, PendingHorseTeleport pending) { //IL_0015: 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_0020: 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) if ((Object)(object)ZNetScene.instance == (Object)null) { return false; } Vector3 position = ((Component)player).transform.position; if (!ZNetScene.instance.IsAreaReady(position)) { return false; } return ZNetScene.instance.IsAreaReady(pending.TargetPos); } private static bool IsStillFollowingPlayer(Character character, Player player) { MonsterAI component = ((Component)character).GetComponent<MonsterAI>(); if ((Object)(object)component == (Object)null) { return false; } return (Object)(object)component.GetFollowTarget() == (Object)(object)((Component)player).gameObject; } private static bool TeleportSnapshottedHorse(SnapshottedHorse snapshot, Vector3 horseDest, Quaternion rot, Player player) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) if (ZDOMan.instance == null) { return false; } ZDO zDO = ZDOMan.instance.GetZDO(snapshot.ZdoId); if (zDO == null) { LogDebug($" SKIP: ZDO {snapshot.ZdoId} no longer exists"); return false; } GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(snapshot.ZdoId) : null); Character val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Character>() : null); if ((Object)(object)val2 != (Object)null) { if (!((Object)val2).name.Contains("rae_OdinHorse")) { LogDebug(" SKIP: Instance is no longer an OdinHorse (" + ((Object)val2).name + ")"); return false; } if (!IsStillFollowingPlayer(val2, player)) { LogDebug(" SKIP at execute: no longer following player"); return false; } } bool flag = zDO.IsOwner(); if (!flag) { zDO.SetOwner(ZDOMan.GetSessionID()); } horseDest.y = ZoneSystem.instance.GetSolidHeight(horseDest) + 0.5f; zDO.SetPosition(horseDest); zDO.SetRotation(rot); if ((Object)(object)val2 != (Object)null) { ZNetView component = ((Component)val2).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && !component.IsOwner()) { component.ClaimOwnership(); } ((Component)val2).transform.SetPositionAndRotation(horseDest, rot); Rigidbody component2 = ((Component)val2).GetComponent<Rigidbody>(); if ((Object)(object)component2 != (Object)null) { component2.position = horseDest; component2.rotation = rot; component2.linearVelocity = Vector3.zero; } Physics.SyncTransforms(); LogDebug($" SUCCESS via ZDO+instance: {snapshot.Name}, ZDOID={snapshot.ZdoId}, " + $"registeredDistance={snapshot.RegisteredDistance:F1}m, wasOwner={flag} " + $"-> ({horseDest.x:F1}, {horseDest.y:F1}, {horseDest.z:F1})"); } else { LogDebug($" SUCCESS via ZDO only: {snapshot.Name}, ZDOID={snapshot.ZdoId}, " + $"registeredDistance={snapshot.RegisteredDistance:F1}m, wasOwner={flag} " + $"-> ({horseDest.x:F1}, {horseDest.y:F1}, {horseDest.z:F1})"); } return true; } private static void ExecuteHorseTeleports(Player player, PendingHorseTeleport pending) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)player).transform.position; Quaternion rotation = ((Component)player).transform.rotation; LogDebug("EXECUTE: Player=" + player.GetPlayerName() + ", " + $"Landed=({position.x:F1}, {position.y:F1}, {position.z:F1}), " + $"Origin=({pending.OriginPos.x:F1}, {pending.OriginPos.y:F1}, {pending.OriginPos.z:F1}), " + $"Snapshotted={pending.QualifiedHorses.Count}"); int num = 0; foreach (SnapshottedHorse qualifiedHorse in pending.QualifiedHorses) { LogDebug($" TELEPORTING snapshotted horse: {qualifiedHorse.Name}, ZDOID={qualifiedHorse.ZdoId}"); Vector3 horseDest = position + new Vector3(Random.Range(-3f, 3f), 0f, Random.Range(-3f, 3f)); if (TeleportSnapshottedHorse(qualifiedHorse, horseDest, rotation, player)) { num++; } } LogDebug($"EXECUTE complete: {pending.QualifiedHorses.Count} snapshotted, {num} teleported"); } private static void TryCompletePendingTeleport(Player player) { if ((Object)(object)player == (Object)null) { return; } long playerID = player.GetPlayerID(); if (!PendingTeleports.TryGetValue(playerID, out var value)) { return; } if (Time.time - value.RegisteredAt > 180f) { ClearPending(playerID, "timeout"); } else { if (((Character)player).IsTeleporting()) { return; } if (!PlayerLandedAtDestination(player, value)) { ClearPending(playerID, "teleport cancelled or failed"); return; } if (IsDestinationAreaReady(player, value)) { try { if (value.QualifiedHorses.Count == 0) { LogDebug("EXECUTE: No horses were snapshotted at registration — nothing to teleport"); } else { ExecuteHorseTeleports(player, value); } return; } catch (Exception arg) { LogError($"CRITICAL ERROR executing horse teleport: {arg}"); return; } finally { ClearPending(playerID, "completed"); } } LogDebug("PENDING: Waiting for destination area to load for " + player.GetPlayerName() + "..."); } } }