diff --git a/valheimmod/valheimmod1.cs b/valheimmod/valheimmod1.cs
index faa5445..cb229b1 100644
--- a/valheimmod/valheimmod1.cs
+++ b/valheimmod/valheimmod1.cs
@@ -197,6 +197,64 @@ private void Awake()
AddLocs();
AddInputs();
PrefabManager.OnPrefabsRegistered += ModAbilities.Effects.Register;
+
+ // Register RPC methods for ValhallaDome networking (delayed to ensure ZNet is ready)
+ PrefabManager.OnPrefabsRegistered += RegisterRPCMethods;
+ }
+
+ ///
+ /// Register RPC methods for networking between clients and server
+ ///
+ private void RegisterRPCMethods()
+ {
+ try
+ {
+ if (ZRoutedRpc.instance != null)
+ {
+ // Register the RPC handler for dome creation requests
+ ZRoutedRpc.instance.Register("ValhallaDome_RequestCreation", new System.Action(OnDomeCreationRequested));
+ Jotunn.Logger.LogInfo("ValhallaDome: Registered RPC methods successfully");
+ }
+ else
+ {
+ Jotunn.Logger.LogWarning("ValhallaDome: ZRoutedRpc.instance is null, cannot register RPC methods");
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Jotunn.Logger.LogError($"ValhallaDome: Failed to register RPC methods: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Handle dome creation requests from clients (server-side)
+ ///
+ private void OnDomeCreationRequested(long senderUID, float x, float y, float z)
+ {
+ try
+ {
+ Jotunn.Logger.LogInfo($"ValhallaDome: Received dome creation request from client {senderUID} at position ({x}, {y}, {z})");
+
+ if (ZNet.instance == null || !ZNet.instance.IsServer())
+ {
+ Jotunn.Logger.LogWarning("ValhallaDome: Received RPC on client or ZNet not ready, ignoring");
+ return;
+ }
+
+ Vector3 position = new Vector3(x, y, z);
+ if (ModAbilities.ValhallaDome.Instance != null)
+ {
+ ModAbilities.ValhallaDome.Instance.CreateDomeAtPosition(position);
+ }
+ else
+ {
+ Jotunn.Logger.LogError("ValhallaDome: Instance is null, cannot create dome");
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Jotunn.Logger.LogError($"ValhallaDome: Error handling dome creation request: {ex.Message}");
+ }
}
private void Update()
@@ -661,7 +719,17 @@ class Hud_UpdateStatusEffects_Patch
{
static void Postfix(Hud __instance, List statusEffects)
{
- ModAbilities.Effects.UpdateStatusEffect(__instance, statusEffects);
+ try
+ {
+ if (__instance != null && statusEffects != null)
+ {
+ ModAbilities.Effects.UpdateStatusEffect(__instance, statusEffects);
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Jotunn.Logger.LogError($"Error in Hud_UpdateStatusEffects_Patch: {ex.Message}");
+ }
}
}
@@ -780,5 +848,127 @@ static void Prefix()
Jotunn.Logger.LogInfo("Transitioning to main scene, resetting LoggedIn to false.");
}
}
+
+ // Patch to detect and setup Valhalla Domes on all clients
+ [HarmonyPatch(typeof(ShieldGenerator), "Start")]
+ public static class ShieldGenerator_Start_ValhallaDome_Patch
+ {
+ static void Postfix(ShieldGenerator __instance)
+ {
+ try
+ {
+ if (__instance == null) return;
+
+ var znetView = __instance.GetComponent();
+ if (znetView != null && znetView.IsValid())
+ {
+ // Check if this is marked as a Valhalla Dome
+ if (znetView.GetZDO().GetBool("valhalla_dome_setup", false))
+ {
+ Jotunn.Logger.LogInfo($"ValhallaDome: Detected Valhalla Dome on client, setting up...");
+
+ // Start coroutine to setup the dome on the next frame
+ if (Player.m_localPlayer != null)
+ {
+ Player.m_localPlayer.StartCoroutine(SetupValhallaDomeCoroutine(__instance.gameObject));
+ }
+ }
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Jotunn.Logger.LogError($"ValhallaDome: Error in ShieldGenerator patch: {ex.Message}");
+ }
+ }
+
+ private static System.Collections.IEnumerator SetupValhallaDomeCoroutine(GameObject dome)
+ {
+ yield return null; // Wait one frame
+
+ if (dome == null)
+ {
+ Jotunn.Logger.LogError("ValhallaDome: Dome GameObject is null in coroutine");
+ yield break;
+ }
+
+ var shieldGen = dome.GetComponent();
+ if (shieldGen != null)
+ {
+ Jotunn.Logger.LogInfo("ValhallaDome: Setting up shield generator for client");
+
+ shieldGen.m_offWhenNoFuel = false;
+ shieldGen.m_minShieldRadius = 4f;
+ shieldGen.m_maxShieldRadius = 4f;
+ shieldGen.SetFuel(shieldGen.m_maxFuel);
+ shieldGen.UpdateShield();
+
+ // Wait another frame for the shield dome to be created
+ yield return null;
+
+ // Hide all MeshRenderers except the dome
+ foreach (var renderer in dome.GetComponentsInChildren(true))
+ {
+ if (shieldGen.m_shieldDome == null || !renderer.transform.IsChildOf(shieldGen.m_shieldDome.transform))
+ {
+ renderer.enabled = false;
+ Jotunn.Logger.LogInfo($"ValhallaDome: Hiding renderer on {renderer.name}");
+ }
+ }
+
+ // Setup the dome's mob-repelling functionality
+ var domeObj = shieldGen.m_shieldDome;
+ if (domeObj != null)
+ {
+ Jotunn.Logger.LogInfo("ValhallaDome: Setting up dome collider and mob repelling");
+
+ // Remove existing colliders except the dome's own
+ foreach (var col in dome.GetComponentsInChildren())
+ {
+ if (col.transform != domeObj.transform)
+ {
+ UnityEngine.Object.Destroy(col);
+ }
+ }
+
+ // Add the sphere collider for mob detection
+ var collider = domeObj.GetComponent();
+ if (collider == null)
+ {
+ collider = domeObj.AddComponent();
+ collider.isTrigger = true;
+ float visualRadius = shieldGen.m_maxShieldRadius;
+ float scale = domeObj.transform.lossyScale.x;
+ float fudge = 0.3f;
+ collider.radius = (visualRadius / scale) * fudge;
+
+ // Add the mob-only shield component
+ var mobShield = domeObj.GetComponent();
+ if (mobShield == null)
+ {
+ domeObj.AddComponent();
+ }
+ }
+
+ domeObj.layer = LayerMask.NameToLayer("character");
+ }
+
+ // Setup timed destruction
+ var timedDestruction = dome.GetComponent();
+ if (timedDestruction == null)
+ {
+ timedDestruction = dome.AddComponent();
+ }
+ timedDestruction.m_forceTakeOwnershipAndDestroy = true;
+ timedDestruction.m_timeout = ModAbilities.ValhallaDome.Instance.ttl;
+ timedDestruction.Trigger();
+
+ Jotunn.Logger.LogInfo("ValhallaDome: Client setup complete");
+ }
+ else
+ {
+ Jotunn.Logger.LogError("ValhallaDome: ShieldGenerator component not found on dome");
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/valheimmod/valheimmod3.cs b/valheimmod/valheimmod3.cs
index afe00c1..b7657e8 100644
--- a/valheimmod/valheimmod3.cs
+++ b/valheimmod/valheimmod3.cs
@@ -252,14 +252,29 @@ public static void Load(bool onDeath = false)
public static void UpdateStatusEffect(Hud __instance, List statusEffectsList, Dictionary durationDict = null)
{
+ // Add null checks to prevent NullReferenceException
+ if (__instance == null || statusEffectsList == null || specialAbilities == null)
+ return;
+
// Update the textures for each status effect in the list
{
foreach (var ability in specialAbilities)
{
+ if (ability == null) continue;
+
for (int j = 0; j < statusEffectsList.Count; j++)
{
StatusEffect statusEffect = statusEffectsList[j];
- ability?.updateTexture(__instance, statusEffect, j);
+ if (statusEffect == null) continue;
+
+ try
+ {
+ ability.updateTexture(__instance, statusEffect, j);
+ }
+ catch (System.Exception ex)
+ {
+ Jotunn.Logger.LogError($"Error updating texture for ability {ability.GetType().Name}: {ex.Message}");
+ }
}
}
}
@@ -762,19 +777,39 @@ public override void updateDuration(Dictionary statusEffectDict)
}
public override void updateTexture(Hud __instance, StatusEffect statusEffect, int index)
{
+ // Add null checks to prevent NullReferenceException
+ if (statusEffect?.m_name == null || SpecialEffect?.StatusEffect?.m_name == null)
+ return;
+
+ if (Player.m_localPlayer == null)
+ return;
+
if (statusEffect.m_name == SpecialEffect.StatusEffect.m_name)
{
// Find the correct icon for the current arrow count
int arrowsLeft = 3 - (ShotsFired.ContainsKey(Player.m_localPlayer) ? ShotsFired[Player.m_localPlayer] : 0);
if (arrowsLeft > 0 && arrowsLeft <= 3)
{
+ // Add null checks for HUD components
+ if (__instance?.m_statusEffects == null || index < 0 || index >= __instance.m_statusEffects.Count)
+ return;
+
// Update the icon in the HUD
RectTransform val2 = __instance.m_statusEffects[index];
- Image component = ((UnityEngine.Component)((Transform)val2).Find("Icon")).GetComponent();
- component.sprite = textures[arrowsLeft - 1];
+ if (val2 == null)
+ return;
+
+ Transform iconTransform = ((Transform)val2).Find("Icon");
+ if (iconTransform == null)
+ return;
+
+ Image component = iconTransform.GetComponent();
+ if (component != null && textures != null && arrowsLeft - 1 < textures.Length)
+ {
+ component.sprite = textures[arrowsLeft - 1];
+ }
}
}
-
}
}
@@ -872,29 +907,47 @@ public override void updateDuration(Dictionary statusEffectDict)
public class MobOnlyShield : MonoBehaviour
{
private float repelForce = 30f; // Adjust this value to change the force applied to mobs
+
private void OnTriggerEnter(Collider other)
{
Jotunn.Logger.LogInfo($"MobOnlyShield OnTriggerEnter called for {other.name}");
- Character character = other.GetComponent();
- if (character != null && character.IsMonsterFaction(0f))
- {
- Jotunn.Logger.LogInfo($"Repelling mob: {character.name}");
- Vector3 repelDir = (character.transform.position - transform.position).normalized;
- character.m_body?.AddForce(repelDir * repelForce, ForceMode.VelocityChange);
-
- }
- else if (character != null)
- {
- Jotunn.Logger.LogInfo($"Ignoring non-monster character: {character.name}");
- }
+ HandleMobRepelling(other);
}
+
private void OnTriggerStay(Collider other)
+ {
+ HandleMobRepelling(other, true);
+ }
+
+ private void HandleMobRepelling(Collider other, bool isStay = false)
{
Character character = other.GetComponent();
- if (character != null && character.IsMonsterFaction(0f))
+ if (character == null) return;
+
+ // Check if it's a monster/hostile creature
+ if (character.IsMonsterFaction(0f))
{
- Vector3 repelDir = (character.transform.position - transform.position).normalized;
- character.m_body?.AddForce(repelDir * repelForce, ForceMode.VelocityChange); // Use Force for continuous push
+ if (!isStay)
+ Jotunn.Logger.LogInfo($"Repelling mob: {character.name}");
+
+ // Only apply force if we can (either local player or have authority)
+ if (character.m_nview != null && (character.m_nview.IsOwner() || Player.m_localPlayer != null))
+ {
+ Vector3 repelDir = (character.transform.position - transform.position).normalized;
+ if (character.m_body != null)
+ {
+ character.m_body.AddForce(repelDir * repelForce, ForceMode.VelocityChange);
+ }
+ else
+ {
+ // Fallback: directly modify position if no rigidbody
+ character.transform.position += repelDir * 0.1f;
+ }
+ }
+ }
+ else if (character != null && !isStay)
+ {
+ Jotunn.Logger.LogInfo($"Ignoring non-monster character: {character.name}");
}
}
}
@@ -972,9 +1025,205 @@ public override void CallPending(valheimmod instance = null)
{
return;
}
- Player.m_localPlayer.m_seman.AddStatusEffect(SpecialEffect.StatusEffect, true);
- ValhallaDome.Instance.abilityUsed = true; // Reset the active dome
- CallManual();
+
+ // Try to create the dome first, only add status effect if successful
+ bool domeCreated = TryCreateDome();
+ if (domeCreated)
+ {
+ // Add the status effect only after successful dome creation
+ Player.m_localPlayer.m_seman.AddStatusEffect(SpecialEffect.StatusEffect, true);
+ ValhallaDome.Instance.abilityUsed = true;
+ }
+ else
+ {
+ // If dome creation failed, show a message but don't add cooldown
+ Player.m_localPlayer.Message(MessageHud.MessageType.Center, "Failed to create dome");
+ }
+ }
+ }
+
+ ///
+ /// Try to create a dome, return true if successful
+ ///
+ private bool TryCreateDome()
+ {
+ try
+ {
+ Jotunn.Logger.LogInfo("ValhallaDome: TryCreateDome called");
+ Vector3 position = Player.m_localPlayer.transform.position;
+
+ // For now, use the simple approach like the original CallManual
+ // The networking can be added back later once this works
+ if (Player.m_localPlayer == null)
+ {
+ Jotunn.Logger.LogError("ValhallaDome: Player.m_localPlayer is null");
+ return false;
+ }
+
+ if (ZNetScene.instance == null)
+ {
+ Jotunn.Logger.LogError("ValhallaDome: ZNetScene.instance is null");
+ return false;
+ }
+
+ GameObject domePrefab = ZNetScene.instance.GetPrefab("piece_shieldgenerator");
+ if (domePrefab != null)
+ {
+ Jotunn.Logger.LogInfo("ValhallaDome: Found shield generator prefab, creating dome");
+ Quaternion rot = Quaternion.identity;
+ ActiveDome = UnityEngine.Object.Instantiate(domePrefab, position, rot);
+ Jotunn.Logger.LogInfo($"ValhallaDome: Instantiated dome at position {position}");
+
+ var znetView = ActiveDome.GetComponent();
+ if (znetView != null && znetView.IsValid())
+ {
+ Jotunn.Logger.LogInfo("ValhallaDome: ZNetView is valid, setting up dome");
+ string uniqueId = System.Guid.NewGuid().ToString();
+ znetView.GetZDO().Set(dome_uid, uniqueId);
+ znetView.GetZDO().Set("valhalla_dome_setup", true); // Mark this as a valhalla dome
+
+ Instance.LastDomeUID = uniqueId;
+ PlayerPrefs.SetString("Dome_LastDomeUID", uniqueId);
+ PlayerPrefs.Save();
+
+ Jotunn.Logger.LogInfo($"ValhallaDome: Dome created with UID: {uniqueId}");
+
+ // Start a coroutine to set up the shield after one frame
+ Player.m_localPlayer.StartCoroutine(SetupShieldNextFrame(ActiveDome));
+ return true;
+ }
+ else
+ {
+ Jotunn.Logger.LogError("ValhallaDome: ZNetView is null or invalid");
+ if (znetView == null)
+ Jotunn.Logger.LogError("ValhallaDome: ZNetView component not found");
+ else
+ Jotunn.Logger.LogError("ValhallaDome: ZNetView is not valid");
+ return false;
+ }
+ }
+ else
+ {
+ Jotunn.Logger.LogError("ValhallaDome: Could not find piece_shieldgenerator prefab");
+ return false;
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Jotunn.Logger.LogError($"ValhallaDome: Error creating dome: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// Request dome creation at the specified position. Handles both server and client cases.
+ ///
+ public void RequestDomeCreation(Vector3 position)
+ {
+ try
+ {
+ if (ZNet.instance == null)
+ {
+ Jotunn.Logger.LogError("ValhallaDome: ZNet.instance is null, cannot create dome");
+ return;
+ }
+
+ if (ZNet.instance.IsServer())
+ {
+ // If we're the server, create the dome directly
+ Jotunn.Logger.LogInfo("ValhallaDome: Server creating dome directly");
+ CreateDomeAtPosition(position);
+ }
+ else
+ {
+ // If we're a client, send an RPC request to the server
+ Jotunn.Logger.LogInfo("ValhallaDome: Client requesting dome creation from server");
+ if (ZRoutedRpc.instance != null && ZNet.instance.GetServerPeer() != null)
+ {
+ ZRoutedRpc.instance.InvokeRoutedRPC(ZNet.instance.GetServerPeer().m_uid, "ValhallaDome_RequestCreation",
+ position.x, position.y, position.z);
+ }
+ else
+ {
+ Jotunn.Logger.LogError("ValhallaDome: Cannot send RPC - ZRoutedRpc or server peer is null");
+ }
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Jotunn.Logger.LogError($"ValhallaDome: Error in RequestDomeCreation: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Actually creates the dome at the specified position. Only called on server.
+ ///
+ public void CreateDomeAtPosition(Vector3 position)
+ {
+ try
+ {
+ if (ZNet.instance == null || !ZNet.instance.IsServer())
+ {
+ Jotunn.Logger.LogWarning("ValhallaDome: CreateDomeAtPosition called on client, ignoring");
+ return;
+ }
+
+ GameObject domePrefab = ZNetScene.instance?.GetPrefab("piece_shieldgenerator");
+ if (domePrefab != null)
+ {
+ Quaternion rot = Quaternion.identity;
+
+ // Create the dome through the network spawning system
+ GameObject spawnedDome = UnityEngine.Object.Instantiate(domePrefab, position, rot);
+ var znetView = spawnedDome.GetComponent();
+
+ if (znetView != null)
+ {
+ // Take ownership of the networked object
+ znetView.ClaimOwnership();
+
+ if (znetView.IsValid())
+ {
+ string uniqueId = System.Guid.NewGuid().ToString();
+ znetView.GetZDO().Set(dome_uid, uniqueId);
+ znetView.GetZDO().Set("valhalla_dome_setup", true); // Mark this as a valhalla dome
+
+ Instance.LastDomeUID = uniqueId;
+ PlayerPrefs.SetString("Dome_LastDomeUID", uniqueId);
+ PlayerPrefs.Save();
+
+ Jotunn.Logger.LogInfo($"ValhallaDome: Server created dome with UID: {uniqueId} at position {position}");
+
+ // Store reference locally for the server
+ if (ActiveDome == null) // Only set if we don't have an active dome
+ {
+ ActiveDome = spawnedDome;
+ }
+
+ // Setup the dome immediately for the server/host
+ if (Player.m_localPlayer != null)
+ {
+ Player.m_localPlayer.StartCoroutine(SetupShieldNextFrame(spawnedDome));
+ }
+ }
+ else
+ {
+ Jotunn.Logger.LogError("ValhallaDome: ZNetView is not valid, cannot create dome");
+ }
+ }
+ else
+ {
+ Jotunn.Logger.LogError("ValhallaDome: No ZNetView component found on dome prefab");
+ }
+ }
+ else
+ {
+ Jotunn.Logger.LogError("ValhallaDome: Could not find piece_shieldgenerator prefab");
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Jotunn.Logger.LogError($"ValhallaDome: Error in CreateDomeAtPosition: {ex.Message}");
}
}