From 69f03537411d7b0fde1daeacb4417912e1ea8e9f Mon Sep 17 00:00:00 2001 From: jneb802 <73610029+jneb802@users.noreply.github.com> Date: Sat, 24 Oct 2026 20:36:16 -0700 Subject: [PATCH] Align frost cave interiors with saved dungeon transforms --- Source/World/FrostCaveInterior.cs | 104 ++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 Source/World/FrostCaveInterior.cs diff --git a/Source/World/FrostCaveInterior.cs b/Source/World/FrostCaveInterior.cs new file mode 100644 index 0000000..dd4b54e --- /dev/null +++ b/Source/World/FrostCaveInterior.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; +using HarmonyLib; +using UnityEngine; + +namespace PraetorisClient +{ + // LocationProxy reconstructs the non-networked gateway from today's prefab. + // An existing dungeon still uses its saved generator and room transforms. + internal sealed class FrostCaveInterior : MonoBehaviour + { + private readonly List _objects = new List(); + private Location _location = null!; + private float _nextCheck; + + internal void Initialize(Location location) + { + _location = location; + Update(); + } + + private void Update() + { + if (_location == null || ZDOMan.instance == null || Time.unscaledTime < _nextCheck) + { + return; + } + + _nextCheck = Time.unscaledTime + 1f; + DungeonGenerator generator = _location.m_generator; + Transform interior = _location.m_interiorTransform; + if (generator == null || interior == null || !generator.transform.IsChildOf(interior)) + { + Destroy(this); + return; + } + + int prefabHash = Utils.GetPrefabName(generator.gameObject).GetStableHashCode(); + Vector3 expectedPosition = generator.transform.position; + _objects.Clear(); + ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(expectedPosition), + new SimulationDistance(0, 0, classic: true), _objects); + + ZDO? savedGenerator = null; + foreach (ZDO candidate in _objects) + { + if (candidate.GetPrefab() != prefabHash || + Mathf.Abs(candidate.GetPosition().y - expectedPosition.y) > 1f) + { + continue; + } + + if (savedGenerator != null) + { + PraetorisClientPlugin.Log.LogWarning("Frost cave interior has multiple saved dungeon generators; leaving its transform unchanged."); + Destroy(this); + return; + } + + savedGenerator = candidate; + } + + // On a remote client the generator ZDO may arrive after the proxy. + if (savedGenerator == null) + { + return; + } + + Vector3 savedPosition = savedGenerator.GetPosition(); + Quaternion savedRotation = savedGenerator.GetRotation(); + if (Vector3.Distance(expectedPosition, savedPosition) > 0.01f || + Quaternion.Angle(generator.transform.rotation, savedRotation) > 0.01f) + { + Quaternion correction = savedRotation * Quaternion.Inverse(generator.transform.rotation); + interior.rotation = correction * interior.rotation; + interior.position += savedPosition - generator.transform.position; + PraetorisClientPlugin.Log.LogInfo("Aligned frost cave interior with saved dungeon: " + + expectedPosition + " -> " + savedPosition + "."); + } + + Destroy(this); + } + } + + [HarmonyPatch(typeof(LocationProxy), "SpawnLocation")] + internal static class FrostCaveInteriorSpawnPatch + { + [HarmonyPostfix] + private static void Postfix(LocationProxy __instance, bool __result) + { + if (!__result || __instance.m_instance == null) + { + return; + } + + Location location = __instance.m_instance.GetComponent(); + if (location == null || Utils.GetPrefabName(location.gameObject) != "MountainCave02") + { + return; + } + + location.gameObject.AddComponent().Initialize(location); + } + } +}