using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GUIFramework; using HarmonyLib; using LostScrollsII.Companions; using LostScrollsII.Integration; using Microsoft.CodeAnalysis; using Splatform; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using ValheimServerGuide.Triggers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("LostScrollsII")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.0.0")] [assembly: AssemblyInformationalVersion("0.2.0+66965495cf4375659e26678c299e75e011841e2f")] [assembly: AssemblyProduct("LostScrollsII")] [assembly: AssemblyTitle("LostScrollsII")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.2.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 LostScrollsII { [BepInPlugin("com.lostscrollsii", "Lost Scrolls II", "0.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.lostscrollsii"; public const string PluginName = "Lost Scrolls II"; public const string PluginVersion = "0.2.0"; private Harmony _harmony; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } public static ConfigEntry CommunionKey { get; private set; } public static ConfigEntry ChoreAssignKey { get; private set; } public static ConfigEntry ChoreAssignRadius { get; private set; } public static ConfigEntry DuelSelectKey { get; private set; } public static ConfigEntry StanceCycleKey { get; private set; } public static ConfigEntry InventoryKey { get; private set; } public static ConfigEntry ShowMapPins { get; private set; } public static ConfigEntry MapPinIcon { get; private set; } private void Awake() { //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; CommunionKey = ((BaseUnityPlugin)this).Config.Bind("Recruitment", "CommunionKey", (KeyCode)103, "Key held while hovering a subdued Dvergr to perform the Communion Rite and recruit it."); ChoreAssignKey = ((BaseUnityPlugin)this).Config.Bind("Chores", "ChoreAssignKey", (KeyCode)104, "Key pressed while hovering a workstation (Smelter-family), a ripe crop, or a tamed animal to assign/unassign the nearest matching-caste companion to that chore."); ChoreAssignRadius = ((BaseUnityPlugin)this).Config.Bind("Chores", "ChoreAssignRadius", 10f, "Max distance from the player to look for a recruited companion to assign to a chore."); DuelSelectKey = ((BaseUnityPlugin)this).Config.Bind("Duels", "DuelSelectKey", (KeyCode)106, "Press while hovering your own recruited companion to toggle its duel mode. A duel-mode companion fights other players' duel-mode companions and ignores everyone else."); StanceCycleKey = ((BaseUnityPlugin)this).Config.Bind("Companions", "StanceCycleKey", (KeyCode)101, "Press while hovering your companion to cycle its stance: Follow -> Guard -> Standby."); InventoryKey = ((BaseUnityPlugin)this).Config.Bind("Companions", "InventoryKey", (KeyCode)121, "Press while hovering your companion to open its inventory (a chest-like panel that also lets you rename it)."); ShowMapPins = ((BaseUnityPlugin)this).Config.Bind("Companions", "ShowMapPins", true, "Show a live minimap pin at each of your own recruited companions. Pins are client-side — other players never see your companions, and you never see theirs."); MapPinIcon = ((BaseUnityPlugin)this).Config.Bind("Companions", "MapPinIcon", 3, "Which vanilla map-pin icon (0-4) to use for companion pins."); _harmony = new Harmony("com.lostscrollsii"); _harmony.PatchAll(); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); Log.LogInfo((object)"Lost Scrolls II v0.2.0 loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void Update() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_00a6: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !TextInput.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !CompanionInventoryGui.IsTyping) { if (Input.GetKeyDown(CommunionKey.Value)) { HandleCommunionInput(localPlayer); } if (Input.GetKeyDown(ChoreAssignKey.Value)) { HandleChoreAssignInput(localPlayer); } if (Input.GetKeyDown(DuelSelectKey.Value)) { HandleDuelInput(localPlayer); } if (Input.GetKeyDown(StanceCycleKey.Value)) { HandleStanceCycleInput(localPlayer); } if (Input.GetKeyDown(InventoryKey.Value)) { HandleInventoryInput(localPlayer); } } } private void HandleInventoryInput(Player player) { GameObject hoverObject = ((Humanoid)player).GetHoverObject(); if ((Object)(object)hoverObject == (Object)null) { return; } Character componentInParent = hoverObject.GetComponentInParent(); DvergrCompanion dvergrCompanion = ((componentInParent != null) ? ((Component)componentInParent).GetComponent() : null); if ((Object)(object)dvergrCompanion == (Object)null) { return; } if (!dvergrCompanion.IsOwner(player)) { MessageHud.instance.ShowMessage((MessageType)2, "This companion answers to another.", 0, (Sprite)null, false); return; } CompanionInventory companionInventory = ((Component)dvergrCompanion).GetComponent(); if ((Object)(object)companionInventory == (Object)null) { companionInventory = ((Component)dvergrCompanion).gameObject.AddComponent(); } CompanionInventoryGui.Open(dvergrCompanion, companionInventory); } private void HandleCommunionInput(Player player) { GameObject hoverObject = ((Humanoid)player).GetHoverObject(); if ((Object)(object)hoverObject == (Object)null) { return; } Character componentInParent = hoverObject.GetComponentInParent(); if ((Object)(object)componentInParent == (Object)null) { return; } DvergrCompanion component = ((Component)componentInParent).GetComponent(); if ((Object)(object)component != (Object)null) { bool flag = component.IsOwner(player); if (MeadFeedingService.TryFeed(componentInParent, player)) { MessageHud.instance.ShowMessage((MessageType)2, flag ? "Your ally drinks deep." : (component.DisplayName + " drinks deep."), 0, (Sprite)null, false); } else { MessageHud.instance.ShowMessage((MessageType)2, "You have no health mead to offer.", 0, (Sprite)null, false); } return; } if (!CommunionService.IsSubduedDvergr(componentInParent)) { MessageHud.instance.ShowMessage((MessageType)2, "It is not yet ready for Communion.", 0, (Sprite)null, false); return; } DvergrCaste dvergrCaste = CommunionService.DetectCaste(componentInParent); Log.LogInfo((object)$"[recruit] '{((Object)componentInParent).name}' detected as caste {dvergrCaste}."); if (CommunionService.TryRecruit(componentInParent, player, dvergrCaste)) { MessageHud.instance.ShowMessage((MessageType)2, "The shadow's grip loosens — a " + dvergrCaste.Display() + " joins you.", 0, (Sprite)null, false); } } private void HandleChoreAssignInput(Player player) { //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) GameObject hoverObject = ((Humanoid)player).GetHoverObject(); if ((Object)(object)hoverObject == (Object)null) { return; } Character componentInParent = hoverObject.GetComponentInParent(); DvergrCompanion dvergrCompanion = (((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).GetComponent() : null); if ((Object)(object)dvergrCompanion != (Object)null) { if (!dvergrCompanion.IsOwner(player)) { MessageHud.instance.ShowMessage((MessageType)2, "This companion answers to another.", 0, (Sprite)null, false); return; } ChoreAI component = ((Component)dvergrCompanion).GetComponent(); if ((Object)(object)component != (Object)null && component.IsAssigned) { component.Unassign(); MessageHud.instance.ShowMessage((MessageType)2, "Ally returns to your side.", 0, (Sprite)null, false); } else { MessageHud.instance.ShowMessage((MessageType)2, "This ally has no chore to leave.", 0, (Sprite)null, false); } return; } Smelter componentInParent2 = hoverObject.GetComponentInParent(); Fermenter val = (((Object)(object)componentInParent2 == (Object)null) ? hoverObject.GetComponentInParent() : null); CookingStation val2 = (((Object)(object)componentInParent2 == (Object)null && (Object)(object)val == (Object)null) ? hoverObject.GetComponentInParent() : null); Pickable val3 = (((Object)(object)componentInParent2 == (Object)null && (Object)(object)val == (Object)null && (Object)(object)val2 == (Object)null) ? hoverObject.GetComponentInParent() : null); ItemStand val4 = null; Character val5 = null; Container val6 = null; if ((Object)(object)componentInParent2 == (Object)null && (Object)(object)val == (Object)null && (Object)(object)val2 == (Object)null && (Object)(object)val3 == (Object)null) { ItemStand componentInParent3 = hoverObject.GetComponentInParent(); if ((Object)(object)componentInParent3 != (Object)null && componentInParent3.HaveAttachment() && componentInParent3.GetAttachedItem() == "Cultivator") { val4 = componentInParent3; } if ((Object)(object)val4 == (Object)null) { Character componentInParent4 = hoverObject.GetComponentInParent(); if ((Object)(object)componentInParent4 != (Object)null && componentInParent4.IsTamed() && (Object)(object)((Component)componentInParent4).GetComponent() == (Object)null) { val5 = componentInParent4; } if ((Object)(object)val5 == (Object)null) { val6 = hoverObject.GetComponentInParent(); } } } if ((Object)(object)componentInParent2 == (Object)null && (Object)(object)val == (Object)null && (Object)(object)val2 == (Object)null && (Object)(object)val3 == (Object)null && (Object)(object)val4 == (Object)null && (Object)(object)val5 == (Object)null && (Object)(object)val6 == (Object)null) { return; } ChoreAI choreAI = ChoreAI.ClaimantOf(((Object)(object)componentInParent2 != (Object)null) ? ((Component)componentInParent2).gameObject : (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : (((Object)(object)val2 != (Object)null) ? ((Component)val2).gameObject : (((Object)(object)val3 != (Object)null) ? ((Component)val3).gameObject : (((Object)(object)val4 != (Object)null) ? ((Component)val4).gameObject : (((Object)(object)val5 != (Object)null) ? ((Component)val5).gameObject : (((Object)(object)val6 != (Object)null) ? ((Component)val6).gameObject : null))))))); if ((Object)(object)choreAI == (Object)null && (Object)(object)val5 != (Object)null) { choreAI = ChoreAI.FeederCovering(((Component)val5).transform.position); } if ((Object)(object)choreAI != (Object)null) { DvergrCompanion component2 = ((Component)choreAI).GetComponent(); if ((Object)(object)component2 != (Object)null && component2.IsOwner(player)) { choreAI.Unassign(); MessageHud.instance.ShowMessage((MessageType)2, "Ally returns to your side.", 0, (Sprite)null, false); } else { MessageHud.instance.ShowMessage((MessageType)2, choreAI.WorkerName + " is already working here.", 0, (Sprite)null, false); } return; } DvergrCaste? requiredCaste = (((Object)(object)componentInParent2 != (Object)null) ? ChoreRules.RequiredCaste(componentInParent2) : new DvergrCaste?((!((Object)(object)val6 != (Object)null)) ? DvergrCaste.SupportMage : DvergrCaste.Rogue)); DvergrCompanion dvergrCompanion2 = FindNearestRecruitedCompanion(((Component)player).transform.position, ChoreAssignRadius.Value, requiredCaste, player, freeOnly: true); if ((Object)(object)dvergrCompanion2 == (Object)null) { string text = (requiredCaste.HasValue ? ("Only your own free " + ChoreRules.DisplayName(requiredCaste.Value) + " can do this — none nearby.") : "No free ally of yours nearby to assign."); MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false); return; } ChoreAI choreAI2 = ((Component)dvergrCompanion2).GetComponent(); if ((Object)(object)choreAI2 == (Object)null) { choreAI2 = ((Component)dvergrCompanion2).gameObject.AddComponent(); } if ((Object)(object)componentInParent2 != (Object)null) { choreAI2.AssignToSmelter(componentInParent2); MessageHud.instance.ShowMessage((MessageType)2, "Ally tends the station.", 0, (Sprite)null, false); } else if ((Object)(object)val != (Object)null) { choreAI2.AssignToFermenter(val); MessageHud.instance.ShowMessage((MessageType)2, "Ally tends the brew.", 0, (Sprite)null, false); } else if ((Object)(object)val2 != (Object)null) { choreAI2.AssignToCooking(val2); MessageHud.instance.ShowMessage((MessageType)2, "Ally tends the cookfire.", 0, (Sprite)null, false); } else if ((Object)(object)val3 != (Object)null || (Object)(object)val4 != (Object)null) { choreAI2.AssignToFarm(((Object)(object)val3 != (Object)null) ? ((Component)val3).gameObject : ((Component)val4).gameObject); MessageHud.instance.ShowMessage((MessageType)2, "Ally tends the field.", 0, (Sprite)null, false); } else if ((Object)(object)val5 != (Object)null) { choreAI2.AssignToFeedAnimals(((Component)val5).gameObject); MessageHud.instance.ShowMessage((MessageType)2, "Ally tends the animals.", 0, (Sprite)null, false); } else { choreAI2.AssignToHaul(val6); MessageHud.instance.ShowMessage((MessageType)2, "Ally hauls to this chest.", 0, (Sprite)null, false); } } private void HandleDuelInput(Player player) { GameObject hoverObject = ((Humanoid)player).GetHoverObject(); if (!((Object)(object)hoverObject == (Object)null)) { Character componentInParent = hoverObject.GetComponentInParent(); DvergrCompanion dvergrCompanion = (((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).GetComponent() : null); if ((Object)(object)dvergrCompanion == (Object)null) { MessageHud.instance.ShowMessage((MessageType)2, "That is not an ally you can pit in a duel.", 0, (Sprite)null, false); } else if (!dvergrCompanion.IsOwner(player)) { MessageHud.instance.ShowMessage((MessageType)2, "Only its owner can send this companion to duel.", 0, (Sprite)null, false); } else if (dvergrCompanion.DuelMode) { dvergrCompanion.ExitDuelMode(DvergrCompanion.DuelExitReason.OwnerStopped); } else if (!dvergrCompanion.EnterDuelMode()) { MessageHud.instance.ShowMessage((MessageType)2, "This companion cannot duel right now — unassign its chore first.", 0, (Sprite)null, false); } } } private void HandleStanceCycleInput(Player player) { GameObject hoverObject = ((Humanoid)player).GetHoverObject(); if ((Object)(object)hoverObject == (Object)null) { return; } Character componentInParent = hoverObject.GetComponentInParent(); DvergrCompanion dvergrCompanion = ((componentInParent != null) ? ((Component)componentInParent).GetComponent() : null); if ((Object)(object)dvergrCompanion == (Object)null) { return; } if (!dvergrCompanion.IsOwner(player)) { MessageHud.instance.ShowMessage((MessageType)2, "This companion answers to another.", 0, (Sprite)null, false); return; } ChoreAI component = ((Component)componentInParent).GetComponent(); if ((component != null && component.IsAssigned) || dvergrCompanion.DuelMode) { MessageHud.instance.ShowMessage((MessageType)2, "Ally is busy — unassign it first.", 0, (Sprite)null, false); return; } CompanionStance companionStance = ((dvergrCompanion.Stance == CompanionStance.Follow) ? CompanionStance.Guard : ((dvergrCompanion.Stance == CompanionStance.Guard) ? CompanionStance.Standby : CompanionStance.Follow)); dvergrCompanion.SetStance(companionStance, ((Component)player).gameObject); MessageHud.instance.ShowMessage((MessageType)2, "Ally stance: " + companionStance switch { CompanionStance.Guard => "Guard", CompanionStance.Follow => "Follow", _ => "Standby", } + ".", 0, (Sprite)null, false); } private static DvergrCompanion FindNearestRecruitedCompanion(Vector3 origin, float radius, DvergrCaste? requiredCaste = null, Player owner = null, bool freeOnly = false) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) List list = new List(); Character.GetCharactersInRange(origin, radius, list); DvergrCompanion result = null; float num = float.MaxValue; foreach (Character item in list) { DvergrCompanion component = ((Component)item).GetComponent(); if ((Object)(object)component == (Object)null || (requiredCaste.HasValue && component.Caste != requiredCaste.Value) || ((Object)(object)owner != (Object)null && !component.IsOwner(owner))) { continue; } if (freeOnly) { ChoreAI component2 = ((Component)item).GetComponent(); if (component2 != null && component2.IsAssigned) { continue; } } float num2 = Vector3.Distance(origin, ((Component)item).transform.position); if (num2 < num) { num = num2; result = component; } } return result; } } } namespace LostScrollsII.Integration { public static class ServerGuideBridge { private const string ServerGuideGuid = "com.valheimserverguide"; public static bool IsLoaded => Chainloader.PluginInfos.ContainsKey("com.valheimserverguide"); public static void RaiseRecruited(DvergrCaste caste) { if (!IsLoaded) { return; } try { RaiseRecruitedInternal(caste); } catch (Exception arg) { Plugin.Log.LogWarning((object)$"ServerGuide integration (dvergr_recruited) failed: {arg}"); } } public static void RaiseLevelUp(DvergrCaste caste, int level) { if (!IsLoaded) { return; } try { RaiseLevelUpInternal(caste, level); } catch (Exception arg) { Plugin.Log.LogWarning((object)$"ServerGuide integration (dvergr_level_up) failed: {arg}"); } } public static void RaiseDuelWon(DvergrCaste winner, DvergrCaste loser) { if (!IsLoaded) { return; } try { RaiseDuelWonInternal(winner, loser); } catch (Exception arg) { Plugin.Log.LogWarning((object)$"ServerGuide integration (dvergr_duel_won) failed: {arg}"); } } private static void RaiseRecruitedInternal(DvergrCaste caste) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown GuidanceDispatcher.Raise(new TriggerEvent { Type = "dvergr_recruited", Subject = caste.ToString() }); } private static void RaiseLevelUpInternal(DvergrCaste caste, int level) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown GuidanceDispatcher.Raise(new TriggerEvent { Type = "dvergr_level_up", Subject = $"{caste}:{level}" }); } private static void RaiseDuelWonInternal(DvergrCaste winner, DvergrCaste loser) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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_004b: Expected O, but got Unknown GuidanceDispatcher.Raise(new TriggerEvent { Type = "dvergr_duel_won", Subject = winner.ToString(), Extra = new Dictionary { { "loserCaste", loser.ToString() } } }); } } } namespace LostScrollsII.Companions { public class ChoreAI : MonoBehaviour { public enum ChoreKind { None, Smelter, Farm, FeedAnimals, Fermenter, Cooking, Haul } public const string ZdoKeyChoreKind = "DE_ChoreKind"; public const string ZdoKeyChorePos = "DE_ChorePos"; private const float RestoreResolveRadius = 3f; private static readonly Dictionary s_claims = new Dictionary(); private const float ArrivalRange = 4.5f; private const float ContainerSearchRadius = 8f; private const float WorkRadius = 10f; private const float FeedInterval = 5f; private const float NotifyInterval = 60f; private const float NotifyRange = 20f; private Humanoid _humanoid; private MonsterAI _ai; private DvergrCompanion _companion; private ZNetView _znv; private float _lastSayTime = -999f; private bool _pendingRestore; private ChoreKind _restoreKind; private Vector3 _restorePos; private float _restoreTimer; private Container _openChest; private float _closeChestTime; private ChoreKind _kind; private Smelter _station; private Fermenter _fermenter; private CookingStation _cooker; private Container _haulChest; private GameObject _anchorObject; private GameObject _claimedAnchor; private float _feedTimer; public string WorkerName { get { if (!((Object)(object)_companion != (Object)null)) { if (!((Object)(object)_humanoid != (Object)null)) { return "An ally"; } return ((Character)_humanoid).GetHoverName(); } return _companion.DisplayName; } } public bool IsAssigned => _kind != ChoreKind.None; public static ChoreAI ClaimantOf(GameObject anchor) { if ((Object)(object)anchor == (Object)null) { return null; } if (s_claims.TryGetValue(anchor, out var value)) { if ((Object)(object)value != (Object)null && value.IsAssigned) { return value; } s_claims.Remove(anchor); } return null; } public static ChoreAI FeederCovering(Vector3 pos) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) foreach (DvergrCompanion item in DvergrCompanion.All) { if (!((Object)(object)item == (Object)null)) { ChoreAI component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null && component.IsAssigned && component._kind == ChoreKind.FeedAnimals && Vector3.Distance(component.CurrentCenter(), pos) <= 10f) { return component; } } } return null; } private void Awake() { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) _humanoid = ((Component)this).GetComponent(); _ai = ((Component)this).GetComponent(); _companion = ((Component)this).GetComponent(); _znv = ((Component)this).GetComponent(); if ((Object)(object)_znv != (Object)null && _znv.IsValid()) { int num = _znv.GetZDO().GetInt("DE_ChoreKind", 0); if (num != 0) { _restoreKind = (ChoreKind)num; _restorePos = _znv.GetZDO().GetVec3("DE_ChorePos", ((Component)this).transform.position); _pendingRestore = true; } } } public static bool HasPersistedChore(ZNetView znv) { if ((Object)(object)znv != (Object)null && znv.IsValid()) { return znv.GetZDO().GetInt("DE_ChoreKind", 0) != 0; } return false; } public void AssignToSmelter(Smelter station) { _kind = ChoreKind.Smelter; _station = station; BeginChore(((Object)(object)station != (Object)null) ? ((Component)station).gameObject : null); } public void AssignToFarm(GameObject cropAnchor) { _kind = ChoreKind.Farm; BeginChore(cropAnchor); } public void AssignToFeedAnimals(GameObject animalAnchor) { _kind = ChoreKind.FeedAnimals; BeginChore(animalAnchor); } public void AssignToFermenter(Fermenter fermenter) { _kind = ChoreKind.Fermenter; _fermenter = fermenter; BeginChore(((Object)(object)fermenter != (Object)null) ? ((Component)fermenter).gameObject : null); } public void AssignToCooking(CookingStation cooker) { _kind = ChoreKind.Cooking; _cooker = cooker; BeginChore(((Object)(object)cooker != (Object)null) ? ((Component)cooker).gameObject : null); } public void AssignToHaul(Container chest) { _kind = ChoreKind.Haul; _haulChest = chest; BeginChore(((Object)(object)chest != (Object)null) ? ((Component)chest).gameObject : null); } private void BeginChore(GameObject anchor) { _anchorObject = anchor; _feedTimer = 0f; Claim(anchor); PersistChore(anchor); if ((Object)(object)_ai != (Object)null && (Object)(object)anchor != (Object)null) { _ai.SetFollowTarget(anchor); } if ((Object)(object)_companion != (Object)null) { _companion.ChoreActive = true; _companion.SetPassive(passive: true); } } private void PersistChore(GameObject anchor) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_znv == (Object)null) && _znv.IsValid()) { ZDO zDO = _znv.GetZDO(); zDO.Set("DE_ChoreKind", (int)_kind); zDO.Set("DE_ChorePos", ((Object)(object)anchor != (Object)null) ? anchor.transform.position : ((Component)this).transform.position); } } private void ClearPersistedChore() { if (!((Object)(object)_znv == (Object)null) && _znv.IsValid()) { _znv.GetZDO().Set("DE_ChoreKind", 0); } } private void TryRestore() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNetScene.instance == (Object)null) { return; } GameObject val = FindRestoreTarget(_restoreKind, _restorePos); if ((Object)(object)val != (Object)null) { _pendingRestore = false; ResumeChore(_restoreKind, val); return; } _restoreTimer += Time.deltaTime; if (_restoreTimer > 60f) { _pendingRestore = false; ClearPersistedChore(); } } private GameObject FindRestoreTarget(ChoreKind kind, Vector3 pos) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) float num = ((kind == ChoreKind.Farm || kind == ChoreKind.FeedAnimals) ? 10f : 3f); Collider[] array = Physics.OverlapSphere(pos, num); foreach (Collider val in array) { switch (kind) { case ChoreKind.Smelter: { Smelter componentInParent2 = ((Component)val).GetComponentInParent(); if ((Object)(object)componentInParent2 != (Object)null) { return ((Component)componentInParent2).gameObject; } break; } case ChoreKind.Fermenter: { Fermenter componentInParent4 = ((Component)val).GetComponentInParent(); if ((Object)(object)componentInParent4 != (Object)null) { return ((Component)componentInParent4).gameObject; } break; } case ChoreKind.Cooking: { CookingStation componentInParent8 = ((Component)val).GetComponentInParent(); if ((Object)(object)componentInParent8 != (Object)null) { return ((Component)componentInParent8).gameObject; } break; } case ChoreKind.Haul: { Container componentInParent3 = ((Component)val).GetComponentInParent(); if ((Object)(object)componentInParent3 != (Object)null) { return ((Component)componentInParent3).gameObject; } break; } case ChoreKind.Farm: { ItemStand componentInParent5 = ((Component)val).GetComponentInParent(); if ((Object)(object)componentInParent5 != (Object)null && componentInParent5.HaveAttachment() && componentInParent5.GetAttachedItem() == "Cultivator") { return ((Component)componentInParent5).gameObject; } Pickable componentInParent6 = ((Component)val).GetComponentInParent(); if ((Object)(object)componentInParent6 != (Object)null) { return ((Component)componentInParent6).gameObject; } Plant componentInParent7 = ((Component)val).GetComponentInParent(); if ((Object)(object)componentInParent7 != (Object)null) { return ((Component)componentInParent7).gameObject; } break; } case ChoreKind.FeedAnimals: { Character componentInParent = ((Component)val).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && componentInParent.IsTamed() && (Object)(object)((Component)componentInParent).GetComponent() == (Object)null) { return ((Component)componentInParent).gameObject; } break; } } } return null; } private void ResumeChore(ChoreKind kind, GameObject go) { switch (kind) { case ChoreKind.Smelter: { Smelter componentInParent4 = go.GetComponentInParent(); if ((Object)(object)componentInParent4 != (Object)null) { AssignToSmelter(componentInParent4); } break; } case ChoreKind.Fermenter: { Fermenter componentInParent2 = go.GetComponentInParent(); if ((Object)(object)componentInParent2 != (Object)null) { AssignToFermenter(componentInParent2); } break; } case ChoreKind.Cooking: { CookingStation componentInParent3 = go.GetComponentInParent(); if ((Object)(object)componentInParent3 != (Object)null) { AssignToCooking(componentInParent3); } break; } case ChoreKind.Haul: { Container componentInParent = go.GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { AssignToHaul(componentInParent); } break; } case ChoreKind.Farm: AssignToFarm(go); break; case ChoreKind.FeedAnimals: AssignToFeedAnimals(go); break; } if (!IsAssigned) { ClearPersistedChore(); } } private void Claim(GameObject anchor) { ReleaseClaim(); if (!((Object)(object)anchor == (Object)null)) { s_claims[anchor] = this; _claimedAnchor = anchor; } } private void ReleaseClaim() { if (!((Object)(object)_claimedAnchor == (Object)null)) { if (s_claims.TryGetValue(_claimedAnchor, out var value) && (Object)(object)value == (Object)(object)this) { s_claims.Remove(_claimedAnchor); } _claimedAnchor = null; } } private void OnDisable() { ReleaseClaim(); } public void Unassign() { _kind = ChoreKind.None; _station = null; _fermenter = null; _cooker = null; _haulChest = null; _anchorObject = null; _pendingRestore = false; ReleaseClaim(); ClearPersistedChore(); ClearSpeech(); CloseChest(); if ((Object)(object)_companion != (Object)null) { _companion.ChoreActive = false; DvergrCompanion companion = _companion; Player obj = _companion.OwnerPlayer(); companion.SetStance(CompanionStance.Follow, (obj != null) ? ((Component)obj).gameObject : null); } else if ((Object)(object)_ai != (Object)null) { _ai.SetFollowTarget((GameObject)null); } } private void FaceToward(Vector3 pos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) Vector3 val = pos - ((Component)this).transform.position; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude > 0.01f && (Object)(object)_humanoid != (Object)null) { ((Character)_humanoid).SetLookDir(((Vector3)(ref val)).normalized, 0f); } } private void OpenChest(Container c) { if (!((Object)(object)c == (Object)null)) { if ((Object)(object)_openChest != (Object)(object)c) { CloseChest(); c.SetInUse(true); _openChest = c; } _closeChestTime = Time.time + 1.5f; } } private void CloseChest() { if ((Object)(object)_openChest != (Object)null) { _openChest.SetInUse(false); _openChest = null; } } private Vector3 CurrentCenter() { //IL_0022: 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_0072: 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) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) if (_kind == ChoreKind.Smelter && (Object)(object)_station != (Object)null) { return ((Component)_station).transform.position; } if (_kind == ChoreKind.Fermenter && (Object)(object)_fermenter != (Object)null) { return ((Component)_fermenter).transform.position; } if (_kind == ChoreKind.Cooking && (Object)(object)_cooker != (Object)null) { return ((Component)_cooker).transform.position; } if (_kind == ChoreKind.Haul && (Object)(object)_haulChest != (Object)null) { return ((Component)_haulChest).transform.position; } if ((Object)(object)_anchorObject != (Object)null) { return _anchorObject.transform.position; } return ((Component)this).transform.position; } private void Update() { //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_openChest != (Object)null && Time.time > _closeChestTime) { CloseChest(); } if (_pendingRestore) { TryRestore(); } else { if (_kind == ChoreKind.None || (Object)(object)_humanoid == (Object)null || ((Object)(object)_znv != (Object)null && _znv.IsValid() && !_znv.IsOwner())) { return; } if ((Object)(object)_anchorObject == (Object)null) { Unassign(); return; } _feedTimer += Time.deltaTime; if (_feedTimer < 5f) { return; } _feedTimer = 0f; if (Vector3.Distance(((Component)this).transform.position, CurrentCenter()) > 4.5f) { Say((_kind == ChoreKind.Farm) ? "I can't reach the field!" : ((_kind == ChoreKind.FeedAnimals) ? "I can't reach the pen!" : ((_kind == ChoreKind.Haul) ? "I can't reach the chest!" : "I can't reach my station!"))); return; } switch (_kind) { case ChoreKind.Smelter: ServiceStation(); break; case ChoreKind.Farm: ServiceFarm(); break; case ChoreKind.FeedAnimals: ServiceAnimals(); break; case ChoreKind.Fermenter: ServiceFermenter(); break; case ChoreKind.Cooking: ServiceCooking(); break; case ChoreKind.Haul: ServiceHaul(); break; } } } private void ServiceStation() { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) bool flag = _station.GetQueueSize() < _station.m_maxOre; bool flag2 = (Object)(object)_station.m_fuelItem != (Object)null && _station.GetFuel() < (float)_station.m_maxFuel; if (!flag && !flag2) { ClearSpeech(); return; } string text = (((Object)(object)_station.m_fuelItem != (Object)null) ? ((Object)_station.m_fuelItem).name : null); bool fuelMissing = flag2; bool oreMissing = flag; Inventory inventory = _humanoid.GetInventory(); Collider[] array = Physics.OverlapSphere(((Component)_station).transform.position, 8f); bool flag3 = false; Collider[] array2 = array; for (int i = 0; i < array2.Length; i++) { Container componentInParent = ((Component)array2[i]).GetComponentInParent(); Inventory val = (((Object)(object)componentInParent != (Object)null) ? componentInParent.GetInventory() : null); if (val == null) { continue; } flag3 = true; foreach (ItemData item in new List(val.GetAllItems())) { string text2 = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null); if (text2 == null) { continue; } if (flag2 && text2 == text) { if (val.RemoveItem(item, 1)) { inventory.AddItem(item.Clone()); OpenChest(componentInParent); FaceToward(((Component)_station).transform.position); _station.SetFuel(_station.GetFuel() + 1f); PlayEffects(_station.m_fuelAddedEffects, ((Component)_station).transform); flag2 = false; fuelMissing = false; } } else if (flag && _station.GetItemConversion(text2) != null && val.RemoveItem(item, 1)) { inventory.AddItem(item.Clone()); OpenChest(componentInParent); FaceToward(((Component)_station).transform.position); _station.QueueOre(text2); PlayEffects(_station.m_oreAddedEffects, ((Component)_station).transform); flag = false; oreMissing = false; } } if (!flag && !flag2) { break; } } if (!flag3) { Say("I have no chest to draw from!"); } else { ReportMissing(oreMissing, fuelMissing); } } private string InputNames() { HashSet hashSet = new HashSet(); if ((Object)(object)_station != (Object)null && _station.m_conversion != null) { foreach (ItemConversion item in _station.m_conversion) { if (item != null && (Object)(object)item.m_from != (Object)null && item.m_from.m_itemData != null && item.m_from.m_itemData.m_shared != null) { hashSet.Add(Localization.instance.Localize(item.m_from.m_itemData.m_shared.m_name)); } } } if (hashSet.Count <= 0) { return "materials"; } return string.Join(" or ", hashSet); } private string FuelName() { if ((Object)(object)_station != (Object)null && (Object)(object)_station.m_fuelItem != (Object)null && _station.m_fuelItem.m_itemData != null && _station.m_fuelItem.m_itemData.m_shared != null) { return Localization.instance.Localize(_station.m_fuelItem.m_itemData.m_shared.m_name); } return "fuel"; } private void ServiceFarm() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) Vector3 val = CurrentCenter(); Inventory val2 = FirstContainerInventory(val); Collider[] array = Physics.OverlapSphere(val, 10f); for (int i = 0; i < array.Length; i++) { Pickable componentInParent = ((Component)array[i]).GetComponentInParent(); if ((Object)(object)componentInParent == (Object)null || !componentInParent.CanBePicked()) { continue; } if (val2 == null) { Say("I have no chest for the harvest."); return; } GameObject itemPrefab = componentInParent.m_itemPrefab; int num = Mathf.Max(1, componentInParent.m_amount); if (!((Object)(object)itemPrefab == (Object)null)) { if (!val2.CanAddItem(itemPrefab, num)) { Say("The harvest chest is full!"); return; } val2.AddItem(itemPrefab, num); componentInParent.SetPicked(true); FaceToward(((Component)componentInParent).transform.position); PlayEffects(componentInParent.m_pickEffector, null, ((Component)componentInParent).transform.position); ClearSpeech(); return; } } if (val2 == null) { Say("I have no chest for the seeds."); return; } ItemData val3 = null; GameObject val4 = null; bool flag = false; foreach (ItemData allItem in val2.GetAllItems()) { if (PlantingCatalog.TryGetSapling(((Object)(object)allItem.m_dropPrefab != (Object)null) ? ((Object)allItem.m_dropPrefab).name : null, out var sapling)) { if (CanGrowInBiome(sapling.GetComponent(), val)) { val3 = allItem; val4 = sapling; break; } flag = true; } } Vector3 spot; if ((Object)(object)val4 == (Object)null) { Say(flag ? "These seeds won't grow in this land." : "No crops are ready, and no seeds to plant."); } else if (!TryFindPlantSpot(val, val4, out spot)) { Say("There's no room left to plant."); } else if (val2.RemoveItem(val3, 1)) { Object.Instantiate(val4, spot, Quaternion.Euler(0f, Random.Range(0f, 360f), 0f)); Piece component = val4.GetComponent(); if ((Object)(object)component != (Object)null) { PlayEffects(component.m_placeEffect, null, spot); } FaceToward(spot); Plugin.Log.LogInfo((object)$"[farm] planted '{((Object)val4).name}' at {spot} (biome {CurrentBiome(spot)})."); ClearSpeech(); } } private bool TryFindPlantSpot(Vector3 center, GameObject sapling, out Vector3 spot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_0046: 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) //IL_004f: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) spot = center; Plant component = sapling.GetComponent(); float radius = (((Object)(object)component != (Object)null) ? Mathf.Max(component.m_growRadius, 0.5f) : 1f); ZoneSystem instance = ZoneSystem.instance; Vector3 val2 = default(Vector3); for (int i = 0; i < 24; i++) { Vector2 val = Random.insideUnitCircle * 8.5f; ((Vector3)(ref val2))..ctor(center.x + val.x, center.y, center.z + val.y); if ((Object)(object)instance != (Object)null) { val2.y = instance.GetGroundHeight(val2); } Heightmap val3 = Heightmap.FindHeightmap(val2); if (!((Object)(object)val3 == (Object)null) && val3.IsCultivated(val2) && CanGrowInBiome(component, val2) && IsSpotClear(val2, radius)) { spot = val2; return true; } } return false; } private static bool IsSpotClear(Vector3 p, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Collider[] array = Physics.OverlapSphere(p, radius); foreach (Collider val in array) { if ((Object)(object)((Component)val).GetComponentInParent() != (Object)null) { return false; } Pickable componentInParent = ((Component)val).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && !componentInParent.GetPicked()) { return false; } } return true; } private static bool CanGrowInBiome(Plant plant, Vector3 pos) { //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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_0029: Invalid comparison between Unknown and I4 if ((Object)(object)plant == (Object)null) { return true; } if (WorldGenerator.instance == null) { return true; } Biome biome = WorldGenerator.instance.GetBiome(pos); return (plant.m_biome & biome) > 0; } private static Biome CurrentBiome(Vector3 pos) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (WorldGenerator.instance == null) { return (Biome)0; } return WorldGenerator.instance.GetBiome(pos); } private void ServiceAnimals() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) Vector3 val = CurrentCenter(); List list = new List(); Collider[] array = Physics.OverlapSphere(val, 10f); for (int i = 0; i < array.Length; i++) { Character componentInParent = ((Component)array[i]).GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null) && componentInParent.IsTamed() && !((Object)(object)((Component)componentInParent).GetComponent() != (Object)null)) { Tameable component = ((Component)componentInParent).GetComponent(); if ((Object)(object)component != (Object)null && component.IsHungry() && !list.Contains(componentInParent)) { list.Add(componentInParent); } } } if (list.Count == 0) { Say("The animals aren't hungry."); return; } Inventory val2 = FirstContainerInventory(val); if (val2 == null) { Say("I have no food chest nearby."); return; } foreach (Character item in list) { MonsterAI component2 = ((Component)item).GetComponent(); if (!((Object)(object)component2 == (Object)null) && component2.m_consumeItems != null && !ItemAlreadyDroppedNear(((Component)item).transform.position)) { ItemData val3 = FindAcceptedFood(val2, component2.m_consumeItems); if (val3 == null) { Say("I have no food to give."); return; } if (val2.RemoveItem(val3, 1)) { ItemData obj = val3.Clone(); obj.m_stack = 1; ItemDrop.DropItem(obj, 1, ((Component)item).transform.position + Vector3.up * 0.3f, Quaternion.identity); ClearSpeech(); return; } } } ClearSpeech(); } private static ItemData FindAcceptedFood(Inventory chest, List consumeItems) { foreach (ItemData allItem in chest.GetAllItems()) { string text = (((Object)(object)allItem.m_dropPrefab != (Object)null) ? ((Object)allItem.m_dropPrefab).name : null); if (text == null) { continue; } foreach (ItemDrop consumeItem in consumeItems) { if ((Object)(object)consumeItem != (Object)null && ((Object)consumeItem).name == text) { return allItem; } } } return null; } private static bool ItemAlreadyDroppedNear(Vector3 pos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Collider[] array = Physics.OverlapSphere(pos, 2f); for (int i = 0; i < array.Length; i++) { if ((Object)(object)((Component)array[i]).GetComponentInParent() != (Object)null) { return true; } } return false; } private void ServiceFermenter() { //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_000c: 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_0020: Expected I4, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) Status status = _fermenter.GetStatus(); switch (status - 1) { case 2: _fermenter.Interact(_humanoid, false, false); ClearSpeech(); return; case 0: ClearSpeech(); return; case 1: Say("The brew is exposed to the sky!"); return; } Inventory val = FirstContainerInventory(CurrentCenter()); if (val == null) { Say("I have no chest to brew from!"); return; } foreach (ItemData item in new List(val.GetAllItems())) { string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null); if (text != null && _fermenter.IsItemAllowed(text) && val.RemoveItem(item, 1)) { ItemData val2 = item.Clone(); val2.m_stack = 1; _humanoid.GetInventory().AddItem(val2); if (_fermenter.AddItem(_humanoid, val2)) { ClearSpeech(); return; } } } Say("I have nothing to brew!"); } private void ServiceCooking() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) if (_cooker.HaveDoneItem()) { _cooker.OnInteract(_humanoid); ClearSpeech(); return; } if ((Object)(object)_cooker.m_fuelItem != (Object)null && _cooker.GetFuel() < (float)_cooker.m_maxFuel) { Inventory val = FirstContainerInventory(CurrentCenter()); string name = ((Object)_cooker.m_fuelItem).name; if (val != null) { foreach (ItemData item in new List(val.GetAllItems())) { if (!((((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null) != name) && val.RemoveItem(item, 1)) { _humanoid.GetInventory().AddItem(item.Clone()); _cooker.SetFuel(_cooker.GetFuel() + 1f); ClearSpeech(); return; } } } } if (_cooker.m_requireFire && !_cooker.IsFireLit()) { Say("The cooking fire is out!"); return; } if (_cooker.GetFreeSlot() < 0) { ClearSpeech(); return; } Inventory val2 = FirstContainerInventory(CurrentCenter()); if (val2 == null) { Say("I have no chest to cook from!"); return; } foreach (ItemData item2 in new List(val2.GetAllItems())) { string text = (((Object)(object)item2.m_dropPrefab != (Object)null) ? ((Object)item2.m_dropPrefab).name : null); if (text != null && _cooker.GetItemConversion(text) != null && val2.RemoveItem(item2, 1)) { ItemData val3 = item2.Clone(); val3.m_stack = 1; _humanoid.GetInventory().AddItem(val3); if (_cooker.CookItem(_humanoid, val3)) { ClearSpeech(); return; } } } Say("I have nothing to cook!"); } private void ServiceHaul() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) Inventory val = (((Object)(object)_haulChest != (Object)null) ? _haulChest.GetInventory() : null); if (val == null) { return; } Collider[] array = Physics.OverlapSphere(CurrentCenter(), 10f); for (int i = 0; i < array.Length; i++) { ItemDrop componentInParent = ((Component)array[i]).GetComponentInParent(); if ((Object)(object)componentInParent == (Object)null || !componentInParent.CanPickup(false)) { continue; } ZNetView component = ((Component)componentInParent).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsValid()) { continue; } ItemData itemData = componentInParent.m_itemData; if (itemData != null) { if (!val.CanAddItem(itemData, itemData.m_stack)) { Say("The haul chest is full!"); return; } val.AddItem(itemData.Clone()); OpenChest(_haulChest); FaceToward(((Component)_haulChest).transform.position); component.ClaimOwnership(); ZNetScene.instance.Destroy(((Component)componentInParent).gameObject); ClearSpeech(); return; } } ClearSpeech(); } private Inventory FirstContainerInventory(Vector3 center) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Collider[] array = Physics.OverlapSphere(center, 8f); for (int i = 0; i < array.Length; i++) { Container componentInParent = ((Component)array[i]).GetComponentInParent(); Inventory val = (((Object)(object)componentInParent != (Object)null) ? componentInParent.GetInventory() : null); if (val != null) { return val; } } return null; } private void PlayEffects(EffectList effects, Transform parent, Vector3? at = null) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (effects != null) { Vector3 val = (Vector3)(((??)at) ?? (((Object)(object)parent != (Object)null) ? parent.position : ((Component)this).transform.position)); effects.Create(val, Quaternion.identity, parent, 1f, -1); } } private void ReportMissing(bool oreMissing, bool fuelMissing) { string text = ((oreMissing && fuelMissing) ? ("I need more " + InputNames() + " and " + FuelName() + "!") : (fuelMissing ? ("I need more " + FuelName() + "!") : (oreMissing ? ("I need more " + InputNames() + "!") : null))); if (text == null) { ClearSpeech(); } else { Say(text); } } private void Say(string text) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Chat.instance == (Object)null) && !((Object)(object)_companion == (Object)null) && _companion.IsOwnerNear(20f) && !(Time.time - _lastSayTime < 60f)) { _lastSayTime = Time.time; Chat.instance.SetNpcText(((Component)this).gameObject, Vector3.up * 2.2f, 20f, 8f, string.Empty, text, false); } } private void ClearSpeech() { if ((Object)(object)Chat.instance != (Object)null) { Chat.instance.ClearNpcText(((Component)this).gameObject); } } } public static class ChoreRules { public static DvergrCaste? RequiredCaste(Smelter station) { string text = (((Object)(object)station != (Object)null) ? ((Object)station).name.ToLowerInvariant() : string.Empty); if (text.Contains("smelter") || text.Contains("blast") || text.Contains("charcoal")) { return DvergrCaste.FireMage; } if (text.Contains("eitr") || text.Contains("spinning") || text.Contains("windmill")) { return DvergrCaste.IceMage; } return null; } public static string DisplayName(DvergrCaste caste) { return caste.Display(); } } public static class CommunionService { public const float SubdueHealthThreshold = 0.2f; public static DvergrCaste DetectCaste(Character target, bool log = true) { if ((Object)(object)target == (Object)null) { return DvergrCaste.Rogue; } Humanoid component = ((Component)target).GetComponent(); if ((Object)(object)component != (Object)null) { string[] array = new string[5] { ItemName(component.GetCurrentWeapon()), ItemName(component.m_rightItem), ItemName(component.m_hiddenRightItem), ItemName(component.m_leftItem), ItemName(component.m_hiddenLeftItem) }; foreach (string text in array) { DvergrCaste? dvergrCaste = ClassifyByElement(text); if (dvergrCaste.HasValue) { if (log) { Plugin.Log.LogInfo((object)$"[recruit] caste {dvergrCaste.Value} from weapon slot '{text}'."); } return dvergrCaste.Value; } } Inventory inventory = component.GetInventory(); if (inventory != null) { foreach (ItemData allItem in inventory.GetAllItems()) { string text2 = ItemName(allItem); DvergrCaste? dvergrCaste2 = ((text2 != null && text2.ToLowerInvariant().Contains("staff")) ? ClassifyByElement(text2) : ((DvergrCaste?)null)); if (dvergrCaste2.HasValue) { if (log) { Plugin.Log.LogInfo((object)$"[recruit] caste {dvergrCaste2.Value} from inventory item '{text2}'."); } return dvergrCaste2.Value; } } } } string name = ((Object)target).name; if (!string.IsNullOrEmpty(name)) { if (name.IndexOf("Fire", StringComparison.OrdinalIgnoreCase) >= 0) { return DvergrCaste.FireMage; } if (name.IndexOf("Ice", StringComparison.OrdinalIgnoreCase) >= 0) { return DvergrCaste.IceMage; } if (name.IndexOf("Support", StringComparison.OrdinalIgnoreCase) >= 0) { return DvergrCaste.SupportMage; } } if (log) { LogDetectionMiss(target, component); } return DvergrCaste.Rogue; } private static DvergrCaste? ClassifyByElement(string name) { if (string.IsNullOrEmpty(name)) { return null; } string text = name.ToLowerInvariant(); if (text.Contains("fire")) { return DvergrCaste.FireMage; } if (text.Contains("ice") || text.Contains("frost")) { return DvergrCaste.IceMage; } if (text.Contains("support") || text.Contains("heal") || text.Contains("nova") || text.Contains("blocker") || text.Contains("shield")) { return DvergrCaste.SupportMage; } return null; } private static string ItemName(ItemData item) { if (item == null) { return null; } if ((Object)(object)item.m_dropPrefab != (Object)null) { return ((Object)item.m_dropPrefab).name; } if (item.m_shared == null) { return null; } return item.m_shared.m_name; } private static void LogDetectionMiss(Character target, Humanoid humanoid) { string text = "(none)"; if ((Object)(object)humanoid != (Object)null && humanoid.GetInventory() != null) { List list = new List(); foreach (ItemData allItem in humanoid.GetInventory().GetAllItems()) { list.Add(ItemName(allItem)); } if (list.Count > 0) { text = string.Join(", ", list); } } Plugin.Log.LogInfo((object)("[recruit] No staff detected -> Rogue. prefab='" + ((Object)target).name + "', slots[" + Slots() + "], inventory=[" + text + "].")); string Slots() { if ((Object)(object)humanoid == (Object)null) { return "(no humanoid)"; } return "cur='" + ItemName(humanoid.GetCurrentWeapon()) + "', right='" + ItemName(humanoid.m_rightItem) + "', hiddenRight='" + ItemName(humanoid.m_hiddenRightItem) + "', left='" + ItemName(humanoid.m_leftItem) + "', hiddenLeft='" + ItemName(humanoid.m_hiddenLeftItem) + "'"; } } public static GameObject SpawnRecruited(DvergrCaste caste, int level, Player owner, Vector3 pos, float xp = 0f) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNetScene.instance == (Object)null) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab(CastePrefab(caste)); if ((Object)(object)prefab == (Object)null && caste != DvergrCaste.Rogue) { prefab = ZNetScene.instance.GetPrefab("DvergerMage"); } if ((Object)(object)prefab == (Object)null) { prefab = ZNetScene.instance.GetPrefab("Dverger"); } if ((Object)(object)prefab == (Object)null) { return null; } GameObject val = Object.Instantiate(prefab, pos, Quaternion.identity); Character component = val.GetComponent(); ZNetView component2 = val.GetComponent(); if ((Object)(object)component2 != (Object)null && component2.IsValid()) { ZDO zDO = component2.GetZDO(); zDO.Set("DE_Recruited", true); zDO.Set("DE_Caste", (int)caste); zDO.Set("DE_Level", Mathf.Clamp(level, 1, 10)); zDO.Set("DE_Xp", Mathf.Max(0f, xp)); if ((Object)(object)owner != (Object)null) { zDO.Set("DE_Owner", owner.GetPlayerID()); zDO.Set("DE_OwnerName", owner.GetPlayerName()); } } if ((Object)(object)component != (Object)null) { ApplyFreedState(component, ((Object)(object)owner != (Object)null) ? ((Component)owner).gameObject : null); } if ((Object)(object)val.GetComponent() == (Object)null) { val.AddComponent(); } if ((Object)(object)val.GetComponent() == (Object)null) { val.AddComponent(); } if ((Object)(object)val.GetComponent() == (Object)null) { val.AddComponent(); } Plugin.Log.LogInfo((object)string.Format("[admin] Spawned recruited {0} (lv {1}) for {2}.", caste, level, ((Object)(object)owner != (Object)null) ? owner.GetPlayerName() : "?")); return val; } private static string CastePrefab(DvergrCaste caste) { return caste switch { DvergrCaste.FireMage => "DvergerMageFire", DvergrCaste.IceMage => "DvergerMageIce", DvergrCaste.SupportMage => "DvergerMageSupport", _ => "Dverger", }; } public static bool IsSubduedDvergr(Character target) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 if ((Object)(object)target == (Object)null) { return false; } if ((int)target.m_faction != 10) { return false; } if (target.IsDead()) { return false; } return target.GetHealthPercentage() <= 0.2f; } public static bool TryRecruit(Character target, Player recruiter, DvergrCaste caste) { if (!IsSubduedDvergr(target)) { return false; } ApplyFreedState(target, ((Object)(object)recruiter != (Object)null) ? ((Component)recruiter).gameObject : null); DvergrCompanion dvergrCompanion = ((Component)target).GetComponent(); if ((Object)(object)dvergrCompanion == (Object)null) { dvergrCompanion = ((Component)target).gameObject.AddComponent(); } if ((Object)(object)((Component)target).GetComponent() == (Object)null) { ((Component)target).gameObject.AddComponent(); } if ((Object)(object)((Component)target).GetComponent() == (Object)null) { ((Component)target).gameObject.AddComponent(); } dvergrCompanion.SetCaste(caste); if ((Object)(object)recruiter != (Object)null) { dvergrCompanion.SetOwner(recruiter.GetPlayerID()); dvergrCompanion.SetOwnerName(recruiter.GetPlayerName()); } ZNetView component = ((Component)target).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid()) { component.GetZDO().Set("DE_Recruited", true); } Plugin.Log.LogInfo((object)string.Format("Dvergr {0} recruited by {1}.", caste, ((Object)(object)recruiter != (Object)null) ? recruiter.GetPlayerName() : "unknown")); ServerGuideBridge.RaiseRecruited(caste); dvergrCompanion.AnnounceCapability(); return true; } public static void RestoreCompanion(Character target) { if ((Object)(object)target == (Object)null) { return; } ZNetView component = ((Component)target).GetComponent(); if (!((Object)(object)component == (Object)null) && component.IsValid() && component.GetZDO().GetBool("DE_Recruited", false)) { ApplyFreedState(target, null); if ((Object)(object)((Component)target).GetComponent() == (Object)null) { ((Component)target).gameObject.AddComponent(); } if ((Object)(object)((Component)target).GetComponent() == (Object)null) { ((Component)target).gameObject.AddComponent(); } if ((Object)(object)((Component)target).GetComponent() == (Object)null) { ((Component)target).gameObject.AddComponent(); } if (ChoreAI.HasPersistedChore(component) && (Object)(object)((Component)target).GetComponent() == (Object)null) { ((Component)target).gameObject.AddComponent(); } } } private static void ApplyFreedState(Character target, GameObject followTarget) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) target.m_faction = (Faction)0; NpcTalk component = ((Component)target).GetComponent(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } MonsterAI component2 = ((Component)target).GetComponent(); if (!((Object)(object)component2 == (Object)null)) { ((BaseAI)component2).SetAggravated(false, (AggravatedReason)0); ((BaseAI)component2).m_aggravatable = false; component2.m_attackPlayerObjects = false; component2.m_targetStatic = null; ((BaseAI)component2).m_avoidWater = true; component2.SetTarget((Character)null); ((BaseAI)component2).SetAlerted(false); ((BaseAI)component2).SetHuntPlayer(false); if ((Object)(object)followTarget != (Object)null) { component2.SetFollowTarget(followTarget); } } } } public enum ConsumableKind { None, Food, HealthMead, ResistMead } public static class CompanionConsumables { public static ConsumableKind Classify(ItemData item) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 if (item?.m_shared == null) { return ConsumableKind.None; } if ((int)item.m_shared.m_itemType != 2) { return ConsumableKind.None; } if (item.m_shared.m_food > 0f) { return ConsumableKind.Food; } StatusEffect consumeStatusEffect = item.m_shared.m_consumeStatusEffect; SE_Stats val = (SE_Stats)(object)((consumeStatusEffect is SE_Stats) ? consumeStatusEffect : null); if ((Object)(object)val == (Object)null) { return ConsumableKind.None; } if (val.m_healthUpFront + val.m_healthOverTime > 0f) { return ConsumableKind.HealthMead; } if (GrantsResistance(val)) { return ConsumableKind.ResistMead; } return ConsumableKind.None; } public static float HealAmount(ItemData item) { StatusEffect obj = item?.m_shared?.m_consumeStatusEffect; SE_Stats val = (SE_Stats)(object)((obj is SE_Stats) ? obj : null); if ((Object)(object)val == (Object)null) { return 0f; } return Mathf.Max(0f, val.m_healthUpFront + val.m_healthOverTime); } public static StatusEffect ConsumeEffect(ItemData item) { if (item?.m_shared == null) { return null; } return item.m_shared.m_consumeStatusEffect; } private static bool GrantsResistance(SE_Stats stats) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0041: Expected I4, but got Unknown if (stats.m_mods == null) { return false; } foreach (DamageModPair mod in stats.m_mods) { DamageModifier modifier = mod.m_modifier; switch (modifier - 1) { case 0: case 2: case 4: return true; } } return false; } public static List ActiveResistEffects(Character c) { List list = new List(); SEMan val = (((Object)(object)c != (Object)null) ? c.GetSEMan() : null); if (val == null) { return list; } foreach (StatusEffect statusEffect in val.GetStatusEffects()) { SE_Stats val2 = (SE_Stats)(object)((statusEffect is SE_Stats) ? statusEffect : null); if (val2 != null && GrantsResistance(val2)) { list.Add(statusEffect); } } return list; } } [RequireComponent(typeof(DvergrCompanion))] public class CompanionInventory : MonoBehaviour { public const int Columns = 4; public const int Rows = 2; public const float MaxWeight = 150f; private Container _container; private DvergrCompanion _companion; public Container Container => _container; public Inventory Inventory { get { if (!((Object)(object)_container != (Object)null)) { return null; } return _container.GetInventory(); } } public bool IsOverCapacity => TotalWeight() > 150f; public bool IsEmpty { get { if (Inventory != null) { return Inventory.NrOfItems() == 0; } return true; } } private void Awake() { _companion = ((Component)this).GetComponent(); EnsureContainer(); if ((Object)(object)((Component)this).GetComponent() == (Object)null) { ((Component)this).gameObject.AddComponent(); } } public void EnsureContainer() { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown if (!((Object)(object)_container != (Object)null) || _container.GetInventory() == null) { _container = ((Component)this).GetComponent() ?? ((Component)this).gameObject.AddComponent(); _container.m_name = InventoryTitle(); _container.m_width = 4; _container.m_height = 2; _container.m_checkGuardStone = false; _container.m_autoDestroyEmpty = false; ZNetView component = ((Component)this).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid()) { Inventory val = new Inventory(_container.m_name, (Sprite)null, 4, 2); val.m_onChanged = _container.OnContainerChanged; _container.m_inventory = val; _container.Load(); } } } public string InventoryTitle() { return (((Object)(object)_companion != (Object)null) ? _companion.DisplayName : "Dvergr") + "'s pack"; } public void RefreshTitle() { if ((Object)(object)_container != (Object)null) { _container.m_name = InventoryTitle(); } } public float TotalWeight() { if (Inventory == null) { return 0f; } return Inventory.GetTotalWeight(); } public bool Holds(ItemData item) { if (item?.m_shared == null || Inventory == null) { return false; } foreach (ItemData allItem in Inventory.GetAllItems()) { if (allItem?.m_shared != null && allItem.m_shared.m_name == item.m_shared.m_name) { return true; } } return false; } public ItemData FindItem(Func match) { if (Inventory == null) { return null; } foreach (ItemData allItem in Inventory.GetAllItems()) { if (allItem != null && match(allItem)) { return allItem; } } return null; } public List Items() { if (Inventory == null) { return new List(); } return Inventory.GetAllItems(); } public bool ConsumeOne(ItemData item) { if (item == null || Inventory == null) { return false; } return Inventory.RemoveItem(item, 1); } } [RequireComponent(typeof(DvergrCompanion))] [RequireComponent(typeof(CompanionInventory))] public class CompanionInventoryAI : MonoBehaviour { private const float TickInterval = 1f; private const float PickupRange = 8f; private const float MeadHealBelowFraction = 0.35f; private const float MeadHealUntilFraction = 0.9f; private DvergrCompanion _companion; private CompanionInventory _inv; private Character _character; private MonsterAI _ai; private ZNetView _znv; private float _tick; private float _foodPeak; private float _foodApplied; private float _foodDuration; private float _foodElapsed; private bool _healing; private void Awake() { _companion = ((Component)this).GetComponent(); _inv = ((Component)this).GetComponent(); _character = ((Component)this).GetComponent(); _ai = ((Component)this).GetComponent(); _znv = ((Component)this).GetComponent(); } private void Update() { if ((Object)(object)_character == (Object)null || _character.IsDead()) { return; } if (!((Object)(object)_znv == (Object)null) && _znv.IsValid() && !_znv.IsOwner()) { if ((Object)(object)_companion != (Object)null && (Object)(object)_inv != (Object)null) { _companion.IsEncumbered = _inv.IsOverCapacity; } return; } TickFoodBuff(Time.deltaTime); bool isOverCapacity = _inv.IsOverCapacity; if (_companion.DuelMode) { _companion.IsEncumbered = isOverCapacity; } else { _companion.ApplyEncumbrance(isOverCapacity); } _tick += Time.deltaTime; if (!(_tick < 1f)) { _tick = 0f; TickConsumption(); if (!isOverCapacity && (!((Object)(object)_ai != (Object)null) || (!((BaseAI)_ai).IsAlerted() && !((BaseAI)_ai).HaveTarget())) && !_companion.ChoreActive && !_companion.DuelMode && !_inv.IsEmpty) { TickPickup(); } } } private void TickPickup() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = _inv.Inventory; if (inventory == null) { return; } Collider[] array = Physics.OverlapSphere(((Component)this).transform.position, 8f); foreach (Collider val in array) { ItemDrop val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInParent() : null); if ((Object)(object)val2 == (Object)null || !val2.CanPickup(false)) { continue; } ZNetView component = ((Component)val2).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsValid()) { continue; } ItemData itemData = val2.m_itemData; if (itemData?.m_shared != null && _inv.Holds(itemData)) { if (inventory.CanAddItem(itemData, itemData.m_stack)) { inventory.AddItem(itemData.Clone()); component.ClaimOwnership(); ZNetScene.instance.Destroy(((Component)val2).gameObject); } break; } } } private void TickConsumption() { if (_inv.Inventory == null) { return; } ItemData val = _inv.FindItem((ItemData it) => CompanionConsumables.Classify(it) == ConsumableKind.ResistMead && !HasEffect(CompanionConsumables.ConsumeEffect(it))); if (val != null) { DrinkStatusMead(val); return; } float healthPercentage = _character.GetHealthPercentage(); if (healthPercentage < 0.35f) { _healing = true; } else if (healthPercentage > 0.9f) { _healing = false; } if (_healing) { ItemData val2 = _inv.FindItem((ItemData it) => CompanionConsumables.Classify(it) == ConsumableKind.HealthMead); if (val2 != null) { float num = CompanionConsumables.HealAmount(val2); if (_inv.ConsumeOne(val2)) { _character.Heal(num, true); PlayConsumeEffects(val2); } return; } _healing = false; } if (_foodApplied <= 0f && _foodElapsed >= _foodDuration) { ItemData val3 = _inv.FindItem((ItemData it) => CompanionConsumables.Classify(it) == ConsumableKind.Food); if (val3 != null) { EatFood(val3); } } } private void DrinkStatusMead(ItemData mead) { StatusEffect val = CompanionConsumables.ConsumeEffect(mead); if (!((Object)(object)val == (Object)null) && _inv.ConsumeOne(mead)) { _character.GetSEMan().AddStatusEffect(val, true, 0, 0f); PlayConsumeEffects(mead); } } private bool HasEffect(StatusEffect se) { if ((Object)(object)se == (Object)null || (Object)(object)_character == (Object)null) { return false; } return _character.GetSEMan().HaveStatusEffect(se.NameHash()); } private void PlayConsumeEffects(ItemData item) { //IL_0024: 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) StatusEffect val = CompanionConsumables.ConsumeEffect(item); EffectList val2 = (((Object)(object)val != (Object)null) ? val.m_startEffects : null); if (val2 != null) { val2.Create(((Component)this).transform.position, ((Component)this).transform.rotation, ((Component)this).transform, 1f, -1); } } private void EatFood(ItemData food) { if (_inv.ConsumeOne(food)) { _foodPeak = food.m_shared.m_food; _foodDuration = Mathf.Max(1f, food.m_shared.m_foodBurnTime); _foodElapsed = 0f; ApplyFoodBonus(_foodPeak); _companion.IsFed = true; _companion.FedIcon = ((food.m_shared.m_icons != null && food.m_shared.m_icons.Length != 0) ? food.m_shared.m_icons[0] : null); PlayConsumeEffects(food); } } private void TickFoodBuff(float dt) { if (!(_foodApplied <= 0f) || !(_foodElapsed >= _foodDuration)) { _foodElapsed += dt; if (_foodElapsed >= _foodDuration) { ExpireFood(); return; } float num = _foodElapsed / _foodDuration; float num2 = ((num <= 0.5f) ? 1f : Mathf.Clamp01(1f - (num - 0.5f) / 0.5f)); ApplyFoodBonus(_foodPeak * num2); } } private void ApplyFoodBonus(float target) { if ((Object)(object)_character == (Object)null) { return; } float num = target - _foodApplied; if (!(Mathf.Abs(num) < 0.01f)) { float num2 = Mathf.Max(1f, _character.GetMaxHealth() + num); _character.SetMaxHealth(num2); if (num > 0f) { _character.Heal(num, false); } else if (_character.GetHealth() > num2) { _character.SetHealth(num2); } _foodApplied = target; } } private void ExpireFood() { if (_foodApplied > 0f && (Object)(object)_character != (Object)null) { float num = Mathf.Max(1f, _character.GetMaxHealth() - _foodApplied); _character.SetMaxHealth(num); if (_character.GetHealth() > num) { _character.SetHealth(num); } } _foodApplied = 0f; _foodPeak = 0f; _foodElapsed = 0f; _foodDuration = 0f; if ((Object)(object)_companion != (Object)null) { _companion.IsFed = false; _companion.FedIcon = null; } } } public class CompanionInventoryGui : MonoBehaviour { private static CompanionInventoryGui _instance; private DvergrCompanion _openCompanion; private CompanionInventory _openInventory; private Character _openCharacter; private TMP_InputField _nameField; private TMP_Text _hpText; private bool _cloneFailed; private const int VanillaInventoryHeight = 4; private const float ContainerClearancePx = 22f; private static float _baseContainerY = float.NaN; public static bool IsTyping { get { if ((Object)(object)_instance != (Object)null && (Object)(object)_instance._nameField != (Object)null) { return _instance._nameField.isFocused; } return false; } } public static void Open(DvergrCompanion companion, CompanionInventory inventory) { if (!((Object)(object)_instance == (Object)null) && !((Object)(object)companion == (Object)null) && !((Object)(object)inventory == (Object)null) && !((Object)(object)InventoryGui.instance == (Object)null)) { inventory.EnsureContainer(); inventory.RefreshTitle(); if (!((Object)(object)inventory.Container == (Object)null)) { InventoryGui.instance.Show(inventory.Container, 0); _instance._openCompanion = companion; _instance._openInventory = inventory; _instance._openCharacter = ((Component)companion).GetComponent(); _instance.BuildWidgets(); _instance.ShowWidgets(show: true); _instance.Refresh(); } } } private void Awake() { _instance = this; } private void Update() { if (!((Object)(object)_openCompanion == (Object)null)) { InventoryGui instance = InventoryGui.instance; if (!((Object)(object)instance != (Object)null) || !InventoryGui.IsVisible() || !((Object)(object)instance.m_currentContainer != (Object)null) || !((Object)(object)_openInventory != (Object)null) || !((Object)(object)instance.m_currentContainer == (Object)(object)_openInventory.Container) || !((Object)(object)_openCharacter != (Object)null) || _openCharacter.IsDead()) { ShowWidgets(show: false); RestoreContainerShift(); _openCompanion = null; _openInventory = null; _openCharacter = null; } else { Refresh(); } } } private void Refresh() { ApplyContainerShift(); if ((Object)(object)_nameField != (Object)null && !_nameField.isFocused) { _nameField.SetTextWithoutNotify(_openCompanion.DisplayName); } if ((Object)(object)_hpText != (Object)null && (Object)(object)_openCharacter != (Object)null) { float num = Mathf.Ceil(_openCharacter.GetHealth()); float num2 = Mathf.Ceil(_openCharacter.GetMaxHealth()); string arg = (_openCompanion.IsFed ? "#FFD24A" : "#FFFFFF"); _hpText.text = $"HP {num:0} / {num2:0}"; } } private void ApplyContainerShift() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) InventoryGui instance = InventoryGui.instance; Player localPlayer = Player.m_localPlayer; if (!((Object)(object)instance == (Object)null) && !((Object)(object)localPlayer == (Object)null) && !((Object)(object)instance.m_container == (Object)null) && !((Object)(object)instance.m_playerGrid == (Object)null)) { RectTransform container = instance.m_container; if (float.IsNaN(_baseContainerY)) { _baseContainerY = container.anchoredPosition.y; } float num = (float)Mathf.Max(0, ((Humanoid)localPlayer).GetInventory().GetHeight() - 4) * instance.m_playerGrid.m_elementSpace; float num2 = ((num > 0f) ? 22f : 0f); Vector2 anchoredPosition = container.anchoredPosition; anchoredPosition.y = _baseContainerY - num - num2; container.anchoredPosition = anchoredPosition; } } private void RestoreContainerShift() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (!float.IsNaN(_baseContainerY)) { RectTransform val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_container : null); if (!((Object)(object)val == (Object)null)) { Vector2 anchoredPosition = val.anchoredPosition; anchoredPosition.y = _baseContainerY; val.anchoredPosition = anchoredPosition; } } } private void BuildWidgets() { if (_cloneFailed) { return; } RectTransform val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_container : null); if ((Object)(object)val == (Object)null) { return; } try { EnsureNameField(val); EnsureHpText(val); } catch (Exception ex) { _cloneFailed = true; Plugin.Log.LogWarning((object)("[inventory] Could not build companion panel widgets: " + ex.Message)); } } private void EnsureNameField(RectTransform panel) { //IL_0067: 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) if ((Object)(object)_nameField != (Object)null) { return; } GuiInputField val = (((Object)(object)TextInput.instance != (Object)null) ? TextInput.instance.m_inputField : null); if (!((Object)(object)val == (Object)null)) { GameObject val2 = Object.Instantiate(((Component)val).gameObject, (Transform)(object)panel); ((Object)val2).name = "CompanionNameField"; RectTransform component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { Park(component, new Vector2(18f, -44f), new Vector2(210f, 28f)); } _nameField = val2.GetComponent(); if ((Object)(object)_nameField == (Object)null) { Object.Destroy((Object)(object)val2); return; } ((UnityEventBase)_nameField.onEndEdit).RemoveAllListeners(); ((UnityEvent)(object)_nameField.onEndEdit).AddListener((UnityAction)OnNameSubmitted); _nameField.characterLimit = 24; } } private void EnsureHpText(RectTransform panel) { //IL_0067: 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) if ((Object)(object)_hpText != (Object)null) { return; } TMP_Text val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_containerWeight : null); if (!((Object)(object)val == (Object)null)) { GameObject val2 = Object.Instantiate(((Component)val).gameObject, (Transform)(object)panel); ((Object)val2).name = "CompanionHpText"; RectTransform component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { Park(component, new Vector2(236f, -44f), new Vector2(210f, 28f)); } _hpText = val2.GetComponent(); if ((Object)(object)_hpText != (Object)null) { _hpText.alignment = (TextAlignmentOptions)513; _hpText.enableAutoSizing = false; _hpText.fontSize = 18f; _hpText.richText = true; } } } private static void Park(RectTransform rt, Vector2 anchoredPos, Vector2 size) { //IL_000b: 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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) rt.anchorMin = new Vector2(0f, 1f); rt.anchorMax = new Vector2(0f, 1f); rt.pivot = new Vector2(0f, 1f); rt.anchoredPosition = anchoredPos; rt.sizeDelta = size; } private void ShowWidgets(bool show) { if ((Object)(object)_nameField != (Object)null) { ((Component)_nameField).gameObject.SetActive(show); } if ((Object)(object)_hpText != (Object)null) { ((Component)_hpText).gameObject.SetActive(show); } } private void OnNameSubmitted(string text) { if (!((Object)(object)_openCompanion == (Object)null) && !string.IsNullOrWhiteSpace(text) && !(text == _openCompanion.DisplayName)) { _openCompanion.SetName(text); if ((Object)(object)_openInventory != (Object)null) { _openInventory.RefreshTitle(); } } } } public class CompanionMapPins : MonoBehaviour { private const float UpdateInterval = 0.25f; private readonly Dictionary _pins = new Dictionary(); private readonly HashSet _seen = new HashSet(); private readonly List _stale = new List(); private Minimap _boundMap; private float _timer; private void Update() { //IL_00a8: 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_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0123: 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_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) Minimap instance = Minimap.instance; Player localPlayer = Player.m_localPlayer; if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null || !Plugin.ShowMapPins.Value) { if (_pins.Count > 0) { if ((Object)(object)instance == (Object)null) { _pins.Clear(); } else { RemoveAll(instance); } } _boundMap = instance; return; } if ((Object)(object)_boundMap != (Object)(object)instance) { _pins.Clear(); _boundMap = instance; } _timer += Time.deltaTime; if (_timer < 0.25f) { return; } _timer = 0f; PinType val = PinIcon(); _seen.Clear(); foreach (DvergrCompanion item in DvergrCompanion.All) { if ((Object)(object)item == (Object)null || item.OwnerId == 0L || !item.IsOwner(localPlayer)) { continue; } _seen.Add(item); if (_pins.TryGetValue(item, out var value) && value != null) { value.m_pos = ((Component)item).transform.position; if (value.m_name != item.DisplayName) { value.m_name = item.DisplayName; } if (value.m_type != val) { value.m_type = val; } } else { _pins[item] = instance.AddPin(((Component)item).transform.position, val, item.DisplayName, false, false, 0L, default(PlatformUserID)); } } _stale.Clear(); foreach (KeyValuePair pin in _pins) { if (!_seen.Contains(pin.Key)) { _stale.Add(pin.Key); } } foreach (DvergrCompanion item2 in _stale) { if (_pins.TryGetValue(item2, out var value2) && value2 != null) { instance.RemovePin(value2); } _pins.Remove(item2); } } private void RemoveAll(Minimap map) { foreach (KeyValuePair pin in _pins) { if (pin.Value != null) { map.RemovePin(pin.Value); } } _pins.Clear(); } private static PinType PinIcon() { return (PinType)(Mathf.Clamp(Plugin.MapPinIcon.Value, 0, 4) switch { 0 => 0, 1 => 1, 2 => 2, 4 => 6, _ => 3, }); } } [HarmonyPatch(typeof(TeleportWorld), "Teleport")] public static class CompanionPortalPatch { public static void Postfix(Player player, bool __runOriginal) { if (__runOriginal && !((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && ((Character)player).IsTeleporting()) { CompanionTeleport.FollowOwnerThroughPortal(player); } } } [HarmonyPatch(typeof(TeleportWorld), "Teleport")] public static class CompanionPortalBlockPatch { private static float _lastMsg = -999f; public static bool Prefix(TeleportWorld __instance, Player player) { if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return true; } if ((Object)(object)__instance == (Object)null || ((Object)__instance).name.IndexOf("portal_wood", StringComparison.OrdinalIgnoreCase) < 0) { return true; } if (CompanionTeleport.TryFindPortalBlocker(player, out var companionName, out var itemName)) { if (Time.time - _lastMsg > 2.5f) { _lastMsg = Time.time; ((Character)player).Message((MessageType)2, companionName + " is carrying " + itemName + " — you can't take it through the portal.", 0, (Sprite)null); } return false; } return true; } } internal static class CompanionTeleport { private const float GatherRange = 60f; public static bool TryFindPortalBlocker(Player owner, out string companionName, out string itemName) { //IL_001e: 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_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) companionName = null; itemName = null; if ((Object)(object)owner == (Object)null) { return false; } long playerID = owner.GetPlayerID(); Vector3 position = ((Component)owner).transform.position; foreach (DvergrCompanion item in DvergrCompanion.All) { if ((Object)(object)item == (Object)null || item.Stance != CompanionStance.Follow || item.OwnerId == 0L || item.OwnerId != playerID || item.ChoreActive || item.DuelMode || item.IsFeral) { continue; } Character component = ((Component)item).GetComponent(); if ((Object)(object)component == (Object)null || component.IsDead() || Vector3.Distance(((Component)component).transform.position, position) > 60f) { continue; } CompanionInventory component2 = ((Component)item).GetComponent(); Inventory val = (((Object)(object)component2 != (Object)null) ? component2.Inventory : null); if (val == null) { continue; } foreach (ItemData allItem in val.GetAllItems()) { if (allItem?.m_shared != null && !allItem.m_shared.m_teleportable) { companionName = item.DisplayName; itemName = ((Localization.instance != null) ? Localization.instance.Localize(allItem.m_shared.m_name) : allItem.m_shared.m_name); return true; } } } return false; } public static void FollowOwnerThroughPortal(Player owner) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) Vector3 teleportTargetPos = owner.m_teleportTargetPos; Quaternion teleportTargetRot = owner.m_teleportTargetRot; Vector3 teleportFromPos = owner.m_teleportFromPos; long playerID = owner.GetPlayerID(); int num = 0; foreach (DvergrCompanion item in DvergrCompanion.All) { if (!((Object)(object)item == (Object)null) && item.Stance == CompanionStance.Follow && item.OwnerId != 0L && item.OwnerId == playerID && !item.ChoreActive && !item.DuelMode && !item.IsFeral) { Character component = ((Component)item).GetComponent(); if (!((Object)(object)component == (Object)null) && !component.IsDead() && !(Vector3.Distance(((Component)component).transform.position, teleportFromPos) > 60f)) { TeleportCompanion(component, teleportTargetPos + SpreadOffset(num), teleportTargetRot); num++; } } } if (num > 0) { Plugin.Log.LogInfo((object)$"[portal] Brought {num} companion(s) through the portal for {owner.GetPlayerName()}."); } } private static void TeleportCompanion(Character ch, Vector3 pos, Quaternion rot) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) ZNetView component = ((Component)ch).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid()) { component.ClaimOwnership(); component.GetZDO().SetPosition(pos); } ((Component)ch).transform.SetPositionAndRotation(pos, rot); if ((Object)(object)ch.m_body != (Object)null) { ch.m_body.position = pos; ch.m_body.rotation = rot; } } private static Vector3 SpreadOffset(int index) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) float num = 2.399963f * (float)index; float num2 = 1.2f + 0.5f * (float)index; return new Vector3(Mathf.Cos(num) * num2, 0f, Mathf.Sin(num) * num2); } } [HarmonyPatch(typeof(MonsterAI), "Start")] public static class CompanionRestorePatch { public static void Postfix(MonsterAI __instance) { CommunionService.RestoreCompanion(((Component)__instance).GetComponent()); } } public enum DvergrCaste { Rogue, FireMage, IceMage, SupportMage } public static class DvergrCasteExtensions { public static string Display(this DvergrCaste caste) { return caste switch { DvergrCaste.FireMage => "Fire Mage", DvergrCaste.IceMage => "Ice Mage", DvergrCaste.SupportMage => "Support Mage", _ => "Rogue", }; } } public class DvergrCompanion : MonoBehaviour, TextReceiver { public enum DuelExitReason { Defeated, NoOpponents, OwnerGone, OwnerStopped } public const string ZdoKeyRecruited = "DE_Recruited"; public const string ZdoKeyCaste = "DE_Caste"; public const string ZdoKeyLevel = "DE_Level"; public const string ZdoKeyXp = "DE_Xp"; public const string ZdoKeyOwner = "DE_Owner"; public const string ZdoKeyOwnerName = "DE_OwnerName"; public const string ZdoKeyName = "DE_Name"; public const string ZdoKeyDuel = "DE_Duel"; public const float SubdueFloorHealthFraction = 0.05f; private const float DuelWinXpBonus = 50f; private const float DuelDetectRange = 30f; private const float OwnerVisionRange = 40f; private const float DuelWaitTimeout = 60f; private const float DuelScanInterval = 1f; private const float StandbyAlertRange = 0f; private const float HostileDuration = 30f; public const int MaxLevel = 10; private static readonly int[] XpToNextByLevel = new int[9] { 100, 250, 450, 700, 1000, 1400, 1900, 2500, 3200 }; private const float GuardAlertRangeMultiplier = 2.5f; private const float SpeedBonusPerLevel = 0.03f; private const float HealthBonusPerLevel = 0.04f; private Character _character; private ZNetView _znv; private MonsterAI _ai; private float _baseAlertRange; private bool _baseAlertRangeCaptured; private float _baseRunSpeed; private float _baseSpeed; private float _baseWalkSpeed; private bool _baseSpeedsCaptured; private string _customName; private long _ownerId; private string _ownerName; private readonly Dictionary _hostileUntil = new Dictionary(); private float _pruneTimer; private bool _duelEngaged; private float _duelWaitTimer; private float _duelScanTimer; private bool _feral; private bool _duelModeLocal; private static readonly HashSet s_all = new HashSet(); private bool _encumbranceActive; public DvergrCaste Caste { get; private set; } public int Level { get; private set; } = 1; public float Xp { get; private set; } public CompanionStance Stance { get; private set; } public float XpPercentToNextLevel { get { if (!IsMaxLevel) { return Mathf.Clamp(Xp / XpRequiredForNextLevel() * 100f, 0f, 100f); } return 100f; } } public bool IsMaxLevel => Level >= 10; public long OwnerId => _ownerId; public bool DuelMode { get { if ((Object)(object)_znv != (Object)null && _znv.IsValid()) { return _znv.GetZDO().GetBool("DE_Duel", false); } return _duelModeLocal; } private set { _duelModeLocal = value; if ((Object)(object)_znv != (Object)null && _znv.IsValid()) { _znv.ClaimOwnership(); _znv.GetZDO().Set("DE_Duel", value); } } } public string OwnerName { get { if (!string.IsNullOrEmpty(_ownerName)) { return _ownerName; } if (_ownerId != 0L) { Player player = Player.GetPlayer(_ownerId); if ((Object)(object)player != (Object)null) { return player.GetPlayerName(); } } return null; } } public static IEnumerable All => s_all; public bool ChoreActive { get; set; } public bool IsEncumbered { get; set; } public bool IsFed { get; set; } public Sprite FedIcon { get; set; } public bool HasCustomName => !string.IsNullOrEmpty(_customName); public string DisplayName { get { if (!HasCustomName) { return DefaultName(); } return _customName; } } public bool IsFeral => _feral; private float XpRequiredForNextLevel() { int num = Level - 1; if (num < 0 || num >= XpToNextByLevel.Length) { return float.MaxValue; } return XpToNextByLevel[num]; } private void OnEnable() { s_all.Add(this); } private void OnDisable() { s_all.Remove(this); } public static bool PlayerHasCompanion(Player player) { if ((Object)(object)player == (Object)null) { return false; } foreach (DvergrCompanion item in s_all) { if ((Object)(object)item != (Object)null && item.IsOwner(player)) { return true; } } return false; } private void Awake() { _character = ((Component)this).GetComponent(); _znv = ((Component)this).GetComponent(); _ai = ((Component)this).GetComponent(); if ((Object)(object)_znv != (Object)null && _znv.IsValid() && _znv.GetZDO().GetBool("DE_Recruited", false)) { Caste = (DvergrCaste)_znv.GetZDO().GetInt("DE_Caste", 0); Level = _znv.GetZDO().GetInt("DE_Level", 1); Xp = _znv.GetZDO().GetFloat("DE_Xp", 0f); _ownerId = _znv.GetZDO().GetLong("DE_Owner", 0L); _ownerName = _znv.GetZDO().GetString("DE_OwnerName", (string)null); _customName = _znv.GetZDO().GetString("DE_Name", (string)null); ApplyLevelToCharacter(); if (_znv.IsOwner()) { _znv.GetZDO().Set("DE_Duel", false); } } if ((Object)(object)_ai != (Object)null) { _baseAlertRange = _ai.m_alertRange; _baseAlertRangeCaptured = true; } ApplyMovementTweaks(); } private void ApplyMovementTweaks() { if ((Object)(object)_ai != (Object)null && ((BaseAI)_ai).m_jumpInterval <= 0f) { ((BaseAI)_ai).m_jumpInterval = 8f; } if ((Object)(object)_character != (Object)null) { Character character = _character; character.m_jumpForce *= 0.5f; } } public void SetPassive(bool passive) { if (!((Object)(object)_ai == (Object)null)) { _ai.m_alertRange = (passive ? 0f : (_baseAlertRangeCaptured ? _baseAlertRange : _ai.m_alertRange)); } } public void ApplyEncumbrance(bool encumbered) { IsEncumbered = encumbered; if ((Object)(object)_ai == (Object)null) { return; } if (encumbered) { if (!_baseAlertRangeCaptured) { _baseAlertRange = _ai.m_alertRange; _baseAlertRangeCaptured = true; } _ai.SetTarget((Character)null); ((BaseAI)_ai).SetAlerted(false); _ai.m_alertRange = 0f; _encumbranceActive = true; } else if (_encumbranceActive) { _encumbranceActive = false; if (ChoreActive) { SetPassive(passive: true); } else { RestoreAlertRangeForStance(); } } } private string DefaultName() { if ((Object)(object)_character != (Object)null && !string.IsNullOrEmpty(_character.m_name) && Localization.instance != null) { return Localization.instance.Localize(_character.m_name); } return "Dvergr"; } public void SetName(string name) { _customName = (string.IsNullOrWhiteSpace(name) ? null : name.Trim()); if ((Object)(object)_znv != (Object)null && _znv.IsValid()) { _znv.GetZDO().Set("DE_Name", _customName ?? string.Empty); } } public string GetText() { return DisplayName; } public void SetText(string text) { //IL_001f: 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) SetName(text); if ((Object)(object)Chat.instance != (Object)null) { Chat.instance.SetNpcText(((Component)this).gameObject, Vector3.up * 2.2f, 20f, 4f, string.Empty, "I'll answer to " + DisplayName + " now.", false); } } public void SetOwner(long ownerId) { _ownerId = ownerId; if ((Object)(object)_znv != (Object)null && _znv.IsValid()) { _znv.GetZDO().Set("DE_Owner", ownerId); } } public void SetOwnerName(string ownerName) { _ownerName = (string.IsNullOrWhiteSpace(ownerName) ? null : ownerName); if ((Object)(object)_znv != (Object)null && _znv.IsValid()) { _znv.GetZDO().Set("DE_OwnerName", _ownerName ?? string.Empty); } } public bool IsOwner(Player player) { if ((Object)(object)player == (Object)null) { return false; } if (_ownerId == 0L) { return true; } return player.GetPlayerID() == _ownerId; } public Player OwnerPlayer() { if (_ownerId == 0L) { return Player.m_localPlayer; } return Player.GetPlayer(_ownerId); } public bool IsOwnerNear(float range) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) Player val = OwnerPlayer(); if ((Object)(object)val != (Object)null) { return Vector3.Distance(((Component)val).transform.position, ((Component)this).transform.position) <= range; } return false; } public bool IsHostileTo(Character other) { if ((Object)(object)other == (Object)null) { return false; } Player val = (Player)(object)((other is Player) ? other : null); if (val != null) { if (_feral) { return true; } if (IsOwner(val)) { return false; } if (Stance == CompanionStance.Guard && _ownerId != 0L) { return true; } } if (_hostileUntil.TryGetValue(other, out var value)) { return Time.time < value; } return false; } public void GoFeral(Character attacker) { if (_feral) { return; } _feral = true; if (DuelMode) { ExitDuelMode(DuelExitReason.OwnerStopped); } if ((Object)(object)_ai != (Object)null) { if (!_baseAlertRangeCaptured) { _baseAlertRange = _ai.m_alertRange; _baseAlertRangeCaptured = true; } _ai.m_alertRange = _baseAlertRange * 2.5f; if ((Object)(object)attacker != (Object)null) { _ai.SetTarget(attacker); } ((BaseAI)_ai).SetAlerted(true); } Announce(DisplayName + " turns on you, eyes wild with the old corruption!"); } public void MarkHostile(Character player) { if (!((Object)(object)player == (Object)null)) { _hostileUntil[player] = Time.time + 30f; } } public void Retaliate(Character attacker) { MarkHostile(attacker); if ((Object)(object)_ai != (Object)null) { _ai.SetTarget(attacker); ((BaseAI)_ai).SetAlerted(true); } } public void MarkOwnersCompanionsHostile(long ownerId) { if (ownerId == 0L) { return; } foreach (DvergrCompanion item in s_all) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item == (Object)(object)this) && item._ownerId == ownerId) { if ((Object)(object)item._character != (Object)null) { MarkHostile(item._character); } if ((Object)(object)_character != (Object)null) { item.MarkHostile(_character); } } } } public bool HasPotentialDuelRivalNearby() { //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) if (_ownerId == 0L) { return false; } foreach (DvergrCompanion item in s_all) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item == (Object)(object)this) && item._ownerId != 0L && item._ownerId != _ownerId) { Character character = item._character; if (!((Object)(object)character == (Object)null) && !character.IsDead() && Vector3.Distance(((Component)this).transform.position, ((Component)item).transform.position) <= 30f) { return true; } } } return false; } private void Update() { if (!DuelMode && (Stance == CompanionStance.Standby || ChoreActive) && (Object)(object)_ai != (Object)null) { Character targetCreature = ((BaseAI)_ai).GetTargetCreature(); if ((Object)(object)targetCreature != (Object)null) { Player val = (Player)(object)((targetCreature is Player) ? targetCreature : null); if (val == null || !IsHostileTo((Character)(object)val)) { _ai.SetTarget((Character)null); } } } if (DuelMode && ((Object)(object)_znv == (Object)null || !_znv.IsValid() || _znv.IsOwner())) { TickDuel(); } _pruneTimer += Time.deltaTime; if (!(_pruneTimer >= 5f)) { return; } _pruneTimer = 0f; if (_hostileUntil.Count <= 0) { return; } float time = Time.time; List list = new List(); foreach (KeyValuePair item in _hostileUntil) { if (item.Value <= time) { list.Add(item.Key); } } foreach (Character item2 in list) { _hostileUntil.Remove(item2); } } public void SetCaste(DvergrCaste caste) { Caste = caste; if ((Object)(object)_znv != (Object)null && _znv.IsValid()) { _znv.GetZDO().Set("DE_Caste", (int)caste); } } public void AddXp(float amount) { if (!(amount <= 0f) && Level < 10) { Xp += amount; while (Level < 10 && Xp >= XpRequiredForNextLevel()) { Xp -= XpRequiredForNextLevel(); Level++; ApplyLevelToCharacter(); Plugin.Log.LogInfo((object)$"Dvergr {Caste} reached level {Level}."); ServerGuideBridge.RaiseLevelUp(Caste, Level); } if (Level >= 10) { Xp = 0f; } Persist(); } } private void ApplyLevelToCharacter() { if (!((Object)(object)_character == (Object)null)) { _character.SetLevel(Level); ApplyCasteBonus(); } } private void ApplyCasteBonus() { int num = Level - 1; switch (Caste) { case DvergrCaste.Rogue: case DvergrCaste.FireMage: { if (!_baseSpeedsCaptured) { _baseRunSpeed = _character.m_runSpeed; _baseSpeed = _character.m_speed; _baseWalkSpeed = _character.m_walkSpeed; _baseSpeedsCaptured = true; } float num3 = 1f + 0.03f * (float)num; _character.m_runSpeed = _baseRunSpeed * num3; _character.m_speed = _baseSpeed * num3; _character.m_walkSpeed = _baseWalkSpeed * num3; break; } case DvergrCaste.IceMage: case DvergrCaste.SupportMage: { float maxHealth = _character.GetMaxHealth(); float num2 = maxHealth * (1f + 0.04f * (float)num); _character.SetMaxHealth(num2); _character.SetHealth(Mathf.Min(_character.GetHealth() + (num2 - maxHealth), num2)); break; } } } private void Persist() { if ((Object)(object)_znv != (Object)null && _znv.IsValid()) { _znv.GetZDO().Set("DE_Level", Level); _znv.GetZDO().Set("DE_Xp", Xp); } } public void SetStance(CompanionStance stance, GameObject followTarget) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) Stance = stance; AnnounceCapability(); if (!((Object)(object)_ai == (Object)null)) { if (!_baseAlertRangeCaptured) { _baseAlertRange = _ai.m_alertRange; _baseAlertRangeCaptured = true; } switch (stance) { case CompanionStance.Follow: _ai.m_alertRange = _baseAlertRange; _ai.SetFollowTarget(followTarget); break; case CompanionStance.Guard: _ai.m_alertRange = _baseAlertRange * 2.5f; _ai.SetFollowTarget((GameObject)null); ((BaseAI)_ai).SetPatrolPoint(((Component)this).transform.position); break; case CompanionStance.Standby: _ai.m_alertRange = 0f; _ai.SetFollowTarget((GameObject)null); ((BaseAI)_ai).SetPatrolPoint(((Component)this).transform.position); _ai.SetTarget((Character)null); break; } } } public void AnnounceCapability() { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Chat.instance == (Object)null) && !((Object)(object)_character == (Object)null)) { string text = Caste switch { DvergrCaste.FireMage => "tend smelters, kilns and forges", DvergrCaste.IceMage => "run the refineries", DvergrCaste.SupportMage => "farm, cook, brew and tend the beasts", _ => "haul loose drops to a chest", }; string text2 = Stance switch { CompanionStance.Guard => "I'll hold this ground. Or set me to " + text + ".", CompanionStance.Standby => "I'll wait here, quietly. Set me to " + text + " when you're ready.", _ => "I'll follow and fight at your side. Set me to " + text + ".", }; Chat.instance.SetNpcText(((Component)this).gameObject, Vector3.up * 2.2f, 20f, 6f, string.Empty, text2, false); } } public void AnnounceBoarded() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Chat.instance == (Object)null) && !((Object)(object)_character == (Object)null)) { Chat.instance.SetNpcText(((Component)this).gameObject, Vector3.up * 2.2f, 20f, 5f, string.Empty, "Aboard — I'll take a seat and hold fast.", false); } } public bool EnterDuelMode() { if (DuelMode) { return true; } if (ChoreActive || _feral) { return false; } DuelMode = true; _duelEngaged = false; _duelWaitTimer = 0f; _duelScanTimer = 0f; if ((Object)(object)_ai != (Object)null) { if (!_baseAlertRangeCaptured) { _baseAlertRange = _ai.m_alertRange; _baseAlertRangeCaptured = true; } _ai.m_alertRange = 30f; ((BaseAI)_ai).SetAlerted(true); } Announce(DisplayName + " squares up — challenging rival companions to a duel."); return true; } public void ExitDuelMode(DuelExitReason reason) { if (DuelMode) { DuelMode = false; _duelEngaged = false; if ((Object)(object)_ai != (Object)null) { _ai.SetTarget((Character)null); ((BaseAI)_ai).SetAlerted(false); RestoreAlertRangeForStance(); } switch (reason) { case DuelExitReason.Defeated: Announce(DisplayName + " is subdued and yields the duel."); break; case DuelExitReason.NoOpponents: Announce(DisplayName + " finds no more challengers and stands down."); break; case DuelExitReason.OwnerGone: Announce(DisplayName + " loses sight of its owner and breaks off the duel."); break; case DuelExitReason.OwnerStopped: Announce(DisplayName + " lowers its guard and leaves the sparring circle."); break; } } } public void AwardDuelWin(DvergrCompanion loser) { AddXp(50f); ServerGuideBridge.RaiseDuelWon(Caste, ((Object)(object)loser != (Object)null) ? loser.Caste : Caste); Announce($"{DisplayName} wins the bout! (+{50f:0} XP)"); if ((Object)(object)Chat.instance != (Object)null) { string ownerName = OwnerName; string text = (((Object)(object)loser != (Object)null) ? loser.DisplayName : "its rival"); string text2 = (string.IsNullOrEmpty(ownerName) ? DisplayName : (DisplayName + " (" + ownerName + ")")); Chat.instance.SendText((Type)2, text2 + " wins the duel against " + text + "!"); } } private void TickDuel() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) Player val = OwnerPlayer(); if ((Object)(object)val == (Object)null || Vector3.Distance(((Component)val).transform.position, ((Component)this).transform.position) > 40f) { ExitDuelMode(DuelExitReason.OwnerGone); return; } _duelScanTimer += Time.deltaTime; if (_duelScanTimer < 1f) { return; } _duelScanTimer = 0f; DvergrCompanion dvergrCompanion = FindNearestDuelRival(); if ((Object)(object)dvergrCompanion != (Object)null) { _duelEngaged = true; _duelWaitTimer = 0f; if ((Object)(object)_ai != (Object)null) { Character targetCreature = ((BaseAI)_ai).GetTargetCreature(); if ((Object)(object)targetCreature == (Object)null || targetCreature.IsDead() || (Object)(object)((Component)targetCreature).GetComponent() != (Object)(object)dvergrCompanion) { _ai.SetTarget(((Component)dvergrCompanion).GetComponent()); ((BaseAI)_ai).SetAlerted(true); } } } else if (_duelEngaged) { ExitDuelMode(DuelExitReason.NoOpponents); } else { _duelWaitTimer += 1f; if (_duelWaitTimer >= 60f) { ExitDuelMode(DuelExitReason.NoOpponents); } } } private DvergrCompanion FindNearestDuelRival() { //IL_0079: 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) DvergrCompanion result = null; float num = float.MaxValue; foreach (DvergrCompanion item in s_all) { if ((Object)(object)item == (Object)null || (Object)(object)item == (Object)(object)this || !item.DuelMode || item._ownerId == _ownerId || _ownerId == 0L || item._ownerId == 0L) { continue; } Character character = item._character; if (!((Object)(object)character == (Object)null) && !character.IsDead()) { float num2 = Vector3.Distance(((Component)this).transform.position, ((Component)item).transform.position); if (num2 <= 30f && num2 < num) { num = num2; result = item; } } } return result; } private void RestoreAlertRangeForStance() { if (!((Object)(object)_ai == (Object)null)) { float num = (_baseAlertRangeCaptured ? _baseAlertRange : _ai.m_alertRange); switch (Stance) { case CompanionStance.Guard: _ai.m_alertRange = num * 2.5f; break; case CompanionStance.Standby: _ai.m_alertRange = 0f; break; default: _ai.m_alertRange = num; break; } } } private void Announce(string line) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_character == (Object)null)) { if ((Object)(object)Chat.instance != (Object)null) { Chat.instance.SetNpcText(((Component)this).gameObject, Vector3.up * 2.2f, 20f, 5f, string.Empty, line, false); } if ((Object)(object)MessageHud.instance != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null && IsOwner(Player.m_localPlayer)) { MessageHud.instance.ShowMessage((MessageType)2, line, 0, (Sprite)null, false); } } } } public enum CompanionStance { Follow, Guard, Standby } public static class MeadFeedingService { public static bool TryFeed(Character target, Player player) { //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0195: 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_0094: Invalid comparison between Unknown and I4 Inventory inventory = ((Humanoid)player).GetInventory(); ItemData val = null; float num = 0f; foreach (ItemData allItem in inventory.GetAllItems()) { float num2 = HealthMeadRestoreAmount(allItem); if (num2 > 0f) { val = allItem; num = num2; break; } } if (val == null) { foreach (ItemData allItem2 in inventory.GetAllItems()) { if (allItem2.m_shared != null && (int)allItem2.m_shared.m_itemType == 2) { string text = (((Object)(object)allItem2.m_dropPrefab != (Object)null) ? ((Object)allItem2.m_dropPrefab).name : allItem2.m_shared.m_name); StatusEffect consumeStatusEffect = allItem2.m_shared.m_consumeStatusEffect; Plugin.Log.LogInfo((object)("[feed] Consumable not matched as health mead: '" + text + "' " + string.Format("(m_food={0}, consumeEffect={1}).", allItem2.m_shared.m_food, ((Object)(object)consumeStatusEffect != (Object)null) ? ((object)consumeStatusEffect).GetType().Name : "null"))); } } return false; } inventory.RemoveItem(val, 1); float num3 = num; target.Heal(num3, true); EffectList val2 = (((Object)(object)val.m_shared.m_consumeStatusEffect != (Object)null) ? val.m_shared.m_consumeStatusEffect.m_startEffects : null); if (val2 != null) { val2.Create(((Component)target).transform.position, ((Component)target).transform.rotation, ((Component)target).transform, 1f, -1); } Plugin.Log.LogInfo((object)($"[feed] Fed companion: potion restores {num} HP " + "to the companion (flat, same as it heals the player).")); return true; } private static float HealthMeadRestoreAmount(ItemData item) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 if (item?.m_shared == null) { return 0f; } if ((int)item.m_shared.m_itemType != 2) { return 0f; } StatusEffect consumeStatusEffect = item.m_shared.m_consumeStatusEffect; SE_Stats val = (SE_Stats)(object)((consumeStatusEffect is SE_Stats) ? consumeStatusEffect : null); if ((Object)(object)val == (Object)null) { return 0f; } float num = val.m_healthUpFront + val.m_healthOverTime; if (!(num > 0f)) { return 0f; } return num; } } public static class PlantingCatalog { private static Dictionary s_map; private static void EnsureBuilt() { if (s_map != null || (Object)(object)ZNetScene.instance == (Object)null) { return; } Dictionary dictionary = new Dictionary(); foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } Plant component = prefab.GetComponent(); Piece component2 = prefab.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || component2.m_resources == null) { continue; } Requirement[] resources = component2.m_resources; foreach (Requirement val in resources) { if (val != null && !((Object)(object)val.m_resItem == (Object)null)) { string name = ((Object)val.m_resItem).name; if (!string.IsNullOrEmpty(name) && !dictionary.ContainsKey(name)) { dictionary[name] = prefab; } } } } s_map = dictionary; Plugin.Log.LogInfo((object)$"[farm] planting catalog built: {s_map.Count} seed→sapling entries."); } public static bool TryGetSapling(string seedPrefabName, out GameObject sapling) { sapling = null; if (string.IsNullOrEmpty(seedPrefabName)) { return false; } EnsureBuilt(); if (s_map != null) { return s_map.TryGetValue(seedPrefabName, out sapling); } return false; } } public class ShipRideAI : MonoBehaviour { private const float LadderBoardRange = 3.5f; private const float HullBoardRange = 5f; private const float BoardCooldown = 1.5f; private const float ScanInterval = 0.4f; private Character _character; private MonsterAI _ai; private ZNetView _znv; private DvergrCompanion _companion; private Rigidbody _body; private float _scanTimer; private float _lastBoardTime = -999f; private bool _wasAboard; private bool IsDriver { get { if (!((Object)(object)_znv == (Object)null) && _znv.IsValid()) { return _znv.IsOwner(); } return true; } } private bool Eligible { get { if ((Object)(object)_companion != (Object)null && _companion.Stance == CompanionStance.Follow && !_companion.ChoreActive && !_companion.DuelMode) { return !_companion.IsFeral; } return false; } } private void Awake() { _character = ((Component)this).GetComponent(); _ai = ((Component)this).GetComponent(); _znv = ((Component)this).GetComponent(); _companion = ((Component)this).GetComponent(); _body = (((Object)(object)_character != (Object)null) ? _character.m_body : null); } private void Update() { if (!IsDriver || (Object)(object)_character == (Object)null || (Object)(object)_ai == (Object)null) { return; } _scanTimer += Time.deltaTime; if (_scanTimer < 0.4f) { return; } _scanTimer = 0f; if (!Eligible) { _wasAboard = false; SetAvoidWater(avoid: true); return; } Player val = _companion.OwnerPlayer(); Ship val2 = (((Object)(object)val != (Object)null) ? FindShipCarrying(val) : null); SetAvoidWater((Object)(object)val2 == (Object)null); if ((Object)(object)val2 == (Object)null) { _wasAboard = false; } else if ((Object)(object)_character.GetStandingOnShip() == (Object)(object)val2) { _wasAboard = true; } else if (!(Time.time - _lastBoardTime < 1.5f)) { TryBoard(val2); } } private void TryBoard(Ship ship) { //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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)this).transform.position; Ladder val = null; float num = float.MaxValue; Ladder[] componentsInChildren = ((Component)ship).GetComponentsInChildren(); foreach (Ladder val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null)) { float num2 = HorizontalDistance(position, ((Component)val2).transform.position); if (num2 < num) { num = num2; val = val2; } } } if ((Object)(object)val != (Object)null && num <= 3.5f) { Vector3 deckPoint = (((Object)(object)val.m_targetPos != (Object)null) ? val.m_targetPos.position : (((Component)val).transform.position + Vector3.up * 1.5f)); Board(deckPoint); } else if ((Object)(object)val == (Object)null && HorizontalDistance(position, ((Component)ship).transform.position) <= 5f) { Player val3 = _companion.OwnerPlayer(); Vector3 val4 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).transform.position : ((Component)ship).transform.position); Board(val4 + ((Component)ship).transform.right * 0.8f); } } private void Board(Vector3 deckPoint) { //IL_0036: 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_0026: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_body != (Object)null) { _body.position = deckPoint; _body.rotation = ((Component)this).transform.rotation; } ((Component)this).transform.position = deckPoint; _lastBoardTime = Time.time; if (!_wasAboard) { _companion.AnnounceBoarded(); } _wasAboard = true; } private void SetAvoidWater(bool avoid) { if ((Object)(object)_ai != (Object)null && ((BaseAI)_ai).m_avoidWater != avoid) { ((BaseAI)_ai).m_avoidWater = avoid; } } private static Ship FindShipCarrying(Player owner) { if ((Object)(object)owner == (Object)null || Ship.s_currentShips == null) { return null; } foreach (Ship s_currentShip in Ship.s_currentShips) { if ((Object)(object)s_currentShip != (Object)null && s_currentShip.IsPlayerInBoat(owner)) { return s_currentShip; } } return null; } private static float HorizontalDistance(Vector3 a, Vector3 b) { //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) a.y = 0f; b.y = 0f; return Vector3.Distance(a, b); } } public static class TotemConversionService { public const string TotemPrefab = "GoblinTotem"; public const string WispPrefab = "Wisp"; public const string ItemName = "Communion Totem"; public const string ItemDescription = "A vessel of the Communion Rite. Within slumbers a freed Dvergr, bound to the totem until you call it forth again."; private static readonly string[] SealFx = new string[3] { "vfx_ghost_death", "fx_spawn_smoke", "vfx_spawn" }; private static readonly string[] SummonFx = new string[3] { "vfx_spawn", "fx_spawn_smoke", "vfx_ghost_death" }; private static readonly MethodInfo MemberwiseCloneMethod = typeof(object).GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic); private const string KeyMarker = "DE_Totem"; private const string KeyCaste = "DE_TotemCaste"; private const string KeyLevel = "DE_TotemLevel"; private const string KeyXp = "DE_TotemXp"; private const string KeyName = "DE_TotemName"; private const string KeyOwner = "DE_TotemOwner"; private const string KeyOwnerName = "DE_TotemOwnerName"; private const string KeyInv = "DE_TotemInv"; private static string _wispSharedName; public static string WispSharedName() { if (!string.IsNullOrEmpty(_wispSharedName)) { return _wispSharedName; } GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab("Wisp") : null); SharedData val2 = ((!((Object)(object)val != (Object)null)) ? null : val.GetComponent()?.m_itemData?.m_shared); if (val2 != null) { _wispSharedName = val2.m_name; } return _wispSharedName; } public static bool IsCompanionTotem(ItemData item) { if (item?.m_customData != null) { return item.m_customData.ContainsKey("DE_Totem"); } return false; } public static ItemData CreateTotem(DvergrCompanion companion) { //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Expected O, but got Unknown if ((Object)(object)companion == (Object)null) { return null; } GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab("GoblinTotem") : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogWarning((object)"[totem] 'GoblinTotem' prefab not found in ObjectDB — cannot seal companion."); return null; } ItemData obj = val2.m_itemData.Clone(); obj.m_dropPrefab = val; obj.m_stack = 1; obj.m_quality = 1; obj.m_worldLevel = (byte)Game.m_worldLevel; obj.m_crafterName = companion.DisplayName; obj.m_crafterID = companion.OwnerId; Dictionary customData = obj.m_customData; customData["DE_Totem"] = "1"; customData["DE_TotemCaste"] = ((int)companion.Caste).ToString(CultureInfo.InvariantCulture); customData["DE_TotemLevel"] = companion.Level.ToString(CultureInfo.InvariantCulture); customData["DE_TotemXp"] = companion.Xp.ToString("R", CultureInfo.InvariantCulture); customData["DE_TotemName"] = (companion.HasCustomName ? companion.DisplayName : string.Empty); customData["DE_TotemOwner"] = companion.OwnerId.ToString(CultureInfo.InvariantCulture); customData["DE_TotemOwnerName"] = companion.OwnerName ?? string.Empty; CompanionInventory component = ((Component)companion).GetComponent(); if ((Object)(object)component != (Object)null && component.Inventory != null && component.Inventory.NrOfItems() > 0) { ZPackage val3 = new ZPackage(); component.Inventory.Save(val3); customData["DE_TotemInv"] = val3.GetBase64(); } ApplyTotemShared(obj); return obj; } public static void ApplyTotemShared(ItemData item) { if (item?.m_shared != null && !(MemberwiseCloneMethod == null)) { object? obj = MemberwiseCloneMethod.Invoke(item.m_shared, null); SharedData val = (SharedData)((obj is SharedData) ? obj : null); if (val != null) { val.m_name = "Communion Totem"; val.m_description = "A vessel of the Communion Rite. Within slumbers a freed Dvergr, bound to the totem until you call it forth again."; val.m_maxStackSize = 1; item.m_shared = val; } } } public static void ReapplyTotemShared(ItemData item) { if (IsCompanionTotem(item) && (item.m_shared == null || !(item.m_shared.m_name == "Communion Totem"))) { ApplyTotemShared(item); } } public static bool TrySummon(Player player, ItemData totem) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || !IsCompanionTotem(totem)) { return false; } if (!TryGetAimPoint(player, out var point)) { return false; } DvergrCaste dvergrCaste = (DvergrCaste)GetInt(totem, "DE_TotemCaste", 0); int num = Mathf.Clamp(GetInt(totem, "DE_TotemLevel", 1), 1, 10); float xp = GetFloat(totem, "DE_TotemXp", 0f); string str = GetStr(totem, "DE_TotemName"); GameObject val = CommunionService.SpawnRecruited(dvergrCaste, num, player, point + Vector3.up * 0.5f, xp); if ((Object)(object)val == (Object)null) { return false; } if (!string.IsNullOrEmpty(str)) { val.GetComponent()?.SetName(str); } RestoreInventory(totem, val); PlaySummonVfx(val.transform.position); Plugin.Log.LogInfo((object)$"[totem] Summoned {dvergrCaste} (lv {num}) from totem for {player.GetPlayerName()}."); return true; } private static void RestoreInventory(ItemData totem, GameObject go) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown string str = GetStr(totem, "DE_TotemInv"); if (string.IsNullOrEmpty(str)) { return; } CompanionInventory companionInventory = go.GetComponent(); if ((Object)(object)companionInventory == (Object)null) { companionInventory = go.AddComponent(); } companionInventory.EnsureContainer(); if (companionInventory.Inventory == null) { return; } try { companionInventory.Inventory.Load(new ZPackage(str)); Container container = companionInventory.Container; if (container != null) { container.Save(); } Plugin.Log.LogInfo((object)$"[totem] Restored {companionInventory.Inventory.NrOfItems()} pack item(s) to the summoned companion."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[totem] Failed to restore sealed pack: " + ex.Message)); } } public static void PlaySealVfx(Vector3 pos) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) PlayEffect(SealFx, pos); } public static void PlaySummonVfx(Vector3 pos) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) PlayEffect(SummonFx, pos); } private static void PlayEffect(string[] candidates, Vector3 pos) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_0052: Expected O, but got Unknown //IL_0059: 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) if ((Object)(object)ZNetScene.instance == (Object)null) { return; } foreach (string text in candidates) { GameObject prefab = ZNetScene.instance.GetPrefab(text); if (!((Object)(object)prefab == (Object)null)) { EffectList val = new EffectList(); val.m_effectPrefabs = (EffectData[])(object)new EffectData[1] { new EffectData { m_prefab = prefab, m_enabled = true } }; val.Create(pos, Quaternion.identity, (Transform)null, 1f, -1); return; } } Plugin.Log.LogWarning((object)("[totem] No VFX prefab resolved (tried: " + string.Join(", ", candidates) + ").")); } private static bool TryGetAimPoint(Player player, out Vector3 point) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) point = Vector3.zero; GameCamera instance = GameCamera.instance; if ((Object)(object)instance != (Object)null) { int mask = LayerMask.GetMask(new string[6] { "Default", "static_solid", "Default_small", "piece", "terrain", "vehicle" }); RaycastHit val = default(RaycastHit); if (Physics.Raycast(((Component)instance).transform.position, ((Component)instance).transform.forward, ref val, 50f, mask)) { point = ((RaycastHit)(ref val)).point; return true; } } if ((Object)(object)player == (Object)null) { return false; } point = ((Component)player).transform.position + ((Component)player).transform.forward * 4f; return true; } public static string BuildTooltipBlock(ItemData item) { DvergrCaste caste = (DvergrCaste)GetInt(item, "DE_TotemCaste", 0); int num = GetInt(item, "DE_TotemLevel", 1); string value = GetStr(item, "DE_TotemName"); string str = GetStr(item, "DE_TotemOwnerName"); if (string.IsNullOrEmpty(value)) { value = caste.Display(); } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("\n\n⚔ ").Append(value).Append(""); stringBuilder.Append("\n").Append(caste.Display()).Append(" — Level ") .Append(num) .Append(""); if (num >= 10) { stringBuilder.Append(" (max)"); } if (!string.IsNullOrEmpty(str)) { stringBuilder.Append("\nBound to ").Append(str).Append(""); } stringBuilder.Append("\n\nUse to summon this companion where you are looking."); return stringBuilder.ToString(); } private static int GetInt(ItemData item, string key, int fallback) { if (item?.m_customData != null && item.m_customData.TryGetValue(key, out var value) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return result; } return fallback; } private static float GetFloat(ItemData item, string key, float fallback) { if (item?.m_customData != null && item.m_customData.TryGetValue(key, out var value) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } return fallback; } private static string GetStr(ItemData item, string key) { if (item?.m_customData != null && item.m_customData.TryGetValue(key, out var value)) { return value; } return string.Empty; } } } namespace LostScrollsII.Patches { [HarmonyPatch(typeof(Terminal), "InitTerminal")] public static class AdminCommands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__1_0; public static ConsoleOptionsFetcher <>9__1_1; internal void b__1_0(ConsoleEventArgs args) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { args.Context.AddString("No local player."); return; } if (args.Length < 2) { args.Context.AddString("Usage: de_spawn [level]"); return; } if (!TryParseCaste(args[1], out var caste)) { args.Context.AddString("Unknown caste '" + args[1] + "'. Use rogue|fire|ice|support."); return; } int result = 1; if (args.Length >= 3) { int.TryParse(args[2], out result); } result = Mathf.Clamp(result, 1, 10); Vector3 pos = ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward * 2f + Vector3.up * 0.5f; GameObject val = CommunionService.SpawnRecruited(caste, result, localPlayer, pos); args.Context.AddString(((Object)(object)val != (Object)null) ? $"Spawned {caste} companion (level {result})." : "Spawn failed — creature prefab not found."); } internal List b__1_1() { return new List { "rogue", "fire", "ice", "support" }; } } private static bool _registered; public static void Postfix() { //IL_0022: 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_002d: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown if (_registered) { return; } _registered = true; object obj = <>c.<>9__1_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; DvergrCaste caste; if ((Object)(object)localPlayer == (Object)null) { args.Context.AddString("No local player."); } else if (args.Length < 2) { args.Context.AddString("Usage: de_spawn [level]"); } else if (!TryParseCaste(args[1], out caste)) { args.Context.AddString("Unknown caste '" + args[1] + "'. Use rogue|fire|ice|support."); } else { int result = 1; if (args.Length >= 3) { int.TryParse(args[2], out result); } result = Mathf.Clamp(result, 1, 10); Vector3 pos = ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward * 2f + Vector3.up * 0.5f; GameObject val4 = CommunionService.SpawnRecruited(caste, result, localPlayer, pos); args.Context.AddString(((Object)(object)val4 != (Object)null) ? $"Spawned {caste} companion (level {result})." : "Spawn failed — creature prefab not found."); } }; <>c.<>9__1_0 = val; obj = (object)val; } ConsoleEvent val2 = (ConsoleEvent)obj; object obj2 = <>c.<>9__1_1; if (obj2 == null) { ConsoleOptionsFetcher val3 = () => new List { "rogue", "fire", "ice", "support" }; <>c.<>9__1_1 = val3; obj2 = (object)val3; } new ConsoleCommand("de_spawn", " [level] - spawn a recruited Dvergr companion", val2, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false); } private static bool TryParseCaste(string s, out DvergrCaste caste) { switch (s.ToLowerInvariant()) { case "rogue": caste = DvergrCaste.Rogue; return true; case "fire": case "firemage": caste = DvergrCaste.FireMage; return true; case "icemage": case "ice": caste = DvergrCaste.IceMage; return true; case "support": case "supportmage": caste = DvergrCaste.SupportMage; return true; default: caste = DvergrCaste.Rogue; return false; } } } internal static class ChoreHint { public static string Line(Object anchorObj, string verb) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) GameObject val = (GameObject)(object)((anchorObj is GameObject) ? anchorObj : null); if ((Object)(object)val == (Object)null) { Component val2 = (Component)(object)((anchorObj is Component) ? anchorObj : null); if (val2 != null) { val = val2.gameObject; } } ChoreAI choreAI = (((Object)(object)val != (Object)null) ? ChoreAI.ClaimantOf(val) : null); if ((Object)(object)choreAI != (Object)null) { return "\n" + choreAI.WorkerName + " is already working here."; } if (!DvergrCompanion.PlayerHasCompanion(Player.m_localPlayer)) { return null; } return $"\n[{Plugin.ChoreAssignKey.Value}] {verb}"; } public static string FeedLine(Character ch) { //IL_0029: 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) if ((Object)(object)ch == (Object)null || !ch.IsTamed()) { return null; } if ((Object)(object)((Component)ch).GetComponent() != (Object)null) { return null; } ChoreAI choreAI = ChoreAI.FeederCovering(((Component)ch).transform.position); if ((Object)(object)choreAI != (Object)null) { return "\n" + choreAI.WorkerName + " is already working here."; } if (!DvergrCompanion.PlayerHasCompanion(Player.m_localPlayer)) { return null; } return $"\n[{Plugin.ChoreAssignKey.Value}] Set companion to feed"; } public static void AppendOnce(ref string result, string line) { if (!string.IsNullOrEmpty(line) && (result == null || !result.Contains(line))) { result += line; } } } [HarmonyPatch(typeof(Switch), "GetHoverText")] public static class SmelterSwitchChoreHintPatch { public static void Postfix(Switch __instance, ref string __result) { if ((Object)(object)__instance == (Object)null) { return; } Smelter componentInParent = ((Component)__instance).GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null)) { string text = ChoreHint.Line((Object)(object)((Component)componentInParent).gameObject, "Set companion to work"); if (text != null) { __result += text; } } } } [HarmonyPatch(typeof(CookingStation), "GetHoverText")] public static class CookingChoreHintPatch { public static void Postfix(CookingStation __instance, ref string __result) { string text = ChoreHint.Line((Object)(object)__instance, "Set companion to cook"); if (text != null) { __result += text; } } } [HarmonyPatch(typeof(Fermenter), "GetHoverText")] public static class FermenterChoreHintPatch { public static void Postfix(Fermenter __instance, ref string __result) { string text = ChoreHint.Line((Object)(object)__instance, "Set companion to brew"); if (text != null) { __result += text; } } } [HarmonyPatch(typeof(Container), "GetHoverText")] public static class ContainerChoreHintPatch { public static void Postfix(Container __instance, ref string __result) { string text = ChoreHint.Line((Object)(object)__instance, "Set companion to haul here"); if (text != null) { __result += text; } } } [HarmonyPatch(typeof(Tameable), "GetHoverText")] public static class TameableChoreHintPatch { public static void Postfix(Tameable __instance, ref string __result) { if (!((Object)(object)__instance == (Object)null)) { string line = ChoreHint.FeedLine(((Component)__instance).GetComponent()); ChoreHint.AppendOnce(ref __result, line); } } } [HarmonyPatch(typeof(Character), "GetHoverText")] public static class CharacterFeedChoreHintPatch { public static void Postfix(Character __instance, ref string __result) { string line = ChoreHint.FeedLine(__instance); ChoreHint.AppendOnce(ref __result, line); } } [HarmonyPatch(typeof(Pickable), "GetHoverText")] public static class PickableChoreHintPatch { public static void Postfix(Pickable __instance, ref string __result) { //IL_0010: 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_0016: 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) if ((Object)(object)__instance == (Object)null) { return; } Vector3 position = ((Component)__instance).transform.position; Heightmap val = Heightmap.FindHeightmap(position); if (!((Object)(object)val == (Object)null) && val.IsCultivated(position)) { string text = ChoreHint.Line((Object)(object)((Component)__instance).gameObject, "Set companion to farm here"); if (text != null) { __result += text; } } } } [HarmonyPatch(typeof(ItemStand), "GetHoverText")] public static class ItemStandFarmHintPatch { public static void Postfix(ItemStand __instance, ref string __result) { if (!((Object)(object)__instance == (Object)null) && __instance.HaveAttachment() && !(__instance.GetAttachedItem() != "Cultivator")) { string text = ChoreHint.Line((Object)(object)((Component)__instance).gameObject, "Set companion to farm this field"); if (text != null) { __result += text; } } } } [HarmonyPatch(typeof(Container), "GetHoverText")] public static class CompanionContainerHoverTextPatch { public static bool Prefix(Container __instance, ref string __result) { if ((Object)(object)((Component)__instance).GetComponent() == (Object)null) { return true; } __result = string.Empty; return false; } } [HarmonyPatch(typeof(Container), "GetHoverName")] public static class CompanionContainerHoverNamePatch { public static bool Prefix(Container __instance, ref string __result) { if ((Object)(object)((Component)__instance).GetComponent() == (Object)null) { return true; } __result = string.Empty; return false; } } [HarmonyPatch(typeof(Container), "Interact")] public static class CompanionContainerInteractPatch { public static bool Prefix(Container __instance, ref bool __result) { if ((Object)(object)((Component)__instance).GetComponent() == (Object)null) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Character), "OnDeath")] public static class CompanionDeathDropPatch { public static void Prefix(Character __instance) { //IL_0066: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)((Component)__instance).GetComponent() == (Object)null) { return; } CompanionInventory component = ((Component)__instance).GetComponent(); Inventory val = (((Object)(object)component != (Object)null) ? component.Inventory : null); if (val == null || val.NrOfItems() == 0) { return; } ZNetView component2 = ((Component)__instance).GetComponent(); if ((Object)(object)component2 != (Object)null && component2.IsValid() && !component2.IsOwner()) { return; } Vector3 val2 = ((Component)__instance).transform.position + Vector3.up * 0.5f; Vector3 val3 = default(Vector3); foreach (ItemData item in new List(val.GetAllItems())) { if (item != null) { ((Vector3)(ref val3))..ctor(Random.Range(-0.6f, 0.6f), 0f, Random.Range(-0.6f, 0.6f)); ItemDrop.DropItem(item, item.m_stack, val2 + val3, Quaternion.identity); } } val.RemoveAll(); Plugin.Log.LogInfo((object)"[inventory] Companion died — spilled its pack to the ground."); } } [HarmonyPatch(typeof(Character), "GetHoverText")] public static class CompanionHoverTextPatch { public static void Postfix(Character __instance, ref string __result) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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_0119: Unknown result type (might be due to invalid IL or missing references) DvergrCompanion component = ((Component)__instance).GetComponent(); if (!((Object)(object)component == (Object)null) && component.IsOwner(Player.m_localPlayer)) { string text = ((component.Stance == CompanionStance.Guard) ? "Guard" : ((component.Stance == CompanionStance.Standby) ? "Standby" : "Follow")); __result = __result + "\nStance: " + text + "" + $"\n[{Plugin.StanceCycleKey.Value}] Cycle stance" + $"\n[{Plugin.InventoryKey.Value}] Inventory / rename"; ChoreAI component2 = ((Component)__instance).GetComponent(); if (component2 != null && component2.IsAssigned) { __result += $"\n[{Plugin.ChoreAssignKey.Value}] Recall from chore"; } if (component.DuelMode) { __result += $"\n[{Plugin.DuelSelectKey.Value}] Stand down from duel"; } else if (component.HasPotentialDuelRivalNearby()) { __result += $"\n[{Plugin.DuelSelectKey.Value}] Duel a rival companion nearby"; } } } } [HarmonyPatch(typeof(Character), "GetHoverName")] public static class CompanionNameBadgePatch { public static void Postfix(Character __instance, ref string __result) { DvergrCompanion component = ((Component)__instance).GetComponent(); if (!((Object)(object)component == (Object)null)) { if (component.HasCustomName) { __result = component.DisplayName; } string ownerName = component.OwnerName; if (!string.IsNullOrEmpty(ownerName)) { __result = __result + " (" + ownerName + ")"; } __result += $" ★{component.Level}"; } } } [HarmonyPatch(typeof(EnemyHud), "UpdateHuds")] public static class CompanionHideVanillaStarsPatch { public static void Postfix(EnemyHud __instance) { if ((Object)(object)__instance == (Object)null || __instance.m_huds == null) { return; } foreach (KeyValuePair hud in __instance.m_huds) { Character key = hud.Key; if (!((Object)(object)key == (Object)null) && !((Object)(object)((Component)key).GetComponent() == (Object)null)) { HudData value = hud.Value; if ((Object)(object)value.m_level2 != (Object)null) { ((Component)value.m_level2).gameObject.SetActive(false); } if ((Object)(object)value.m_level3 != (Object)null) { ((Component)value.m_level3).gameObject.SetActive(false); } } } } } [HarmonyPatch(typeof(EnemyHud), "UpdateHuds")] public static class CompanionStatusIconPatch { public static void Postfix(EnemyHud __instance) { if ((Object)(object)__instance == (Object)null || __instance.m_huds == null) { return; } foreach (KeyValuePair hud in __instance.m_huds) { Character key = hud.Key; if ((Object)(object)key == (Object)null) { continue; } DvergrCompanion component = ((Component)key).GetComponent(); if ((Object)(object)component == (Object)null) { continue; } HudData value = hud.Value; if (value != null && !((Object)(object)value.m_gui == (Object)null)) { CompanionHudStatusIcons companionHudStatusIcons = value.m_gui.GetComponent(); if ((Object)(object)companionHudStatusIcons == (Object)null) { companionHudStatusIcons = value.m_gui.AddComponent(); } companionHudStatusIcons.Refresh(component, value); } } } } public class CompanionHudStatusIcons : MonoBehaviour { private readonly List _pool = new List(); private readonly List _sprites = new List(); private static Sprite _encumberedSprite; private static bool _encumberedResolved; private const float IconSize = 22f; private const float Gap = 2f; public void Refresh(DvergrCompanion companion, HudData hud) { //IL_013e: Unknown result type (might be due to invalid IL or missing references) Transform anchor = (((Object)(object)hud.m_name != (Object)null) ? ((TMP_Text)hud.m_name).transform : ((Component)this).transform); _sprites.Clear(); if (companion.IsFed && (Object)(object)companion.FedIcon != (Object)null) { _sprites.Add(companion.FedIcon); } if (companion.IsEncumbered && (Object)(object)EncumberedSprite() != (Object)null) { _sprites.Add(EncumberedSprite()); } foreach (StatusEffect item in CompanionConsumables.ActiveResistEffects(hud.m_character)) { if ((Object)(object)item.m_icon != (Object)null) { _sprites.Add(item.m_icon); } } float num = (0f - ((float)_sprites.Count * 22f + (float)Mathf.Max(0, _sprites.Count - 1) * 2f)) * 0.5f + 11f; for (int i = 0; i < _sprites.Count; i++) { Image icon = GetIcon(i, anchor); icon.sprite = _sprites[i]; ((Graphic)icon).rectTransform.anchoredPosition = new Vector2(num + (float)i * 24f, 6f); ((Component)icon).gameObject.SetActive(true); } for (int j = _sprites.Count; j < _pool.Count; j++) { ((Component)_pool[j]).gameObject.SetActive(false); } } private Image GetIcon(int index, Transform anchor) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) if (index < _pool.Count) { return _pool[index]; } GameObject val = new GameObject($"LS_StatusIcon{index}", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform component = val.GetComponent(); ((Transform)component).SetParent(anchor, false); component.anchorMin = new Vector2(0.5f, 1f); component.anchorMax = new Vector2(0.5f, 1f); component.pivot = new Vector2(0.5f, 0f); component.sizeDelta = new Vector2(22f, 22f); Image component2 = val.GetComponent(); ((Graphic)component2).raycastTarget = false; component2.preserveAspect = true; _pool.Add(component2); return component2; } private static Sprite EncumberedSprite() { if (_encumberedResolved) { return _encumberedSprite; } _encumberedResolved = true; if ((Object)(object)ObjectDB.instance != (Object)null) { StatusEffect statusEffect = ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("Encumbered")); if ((Object)(object)statusEffect != (Object)null) { _encumberedSprite = statusEffect.m_icon; } } return _encumberedSprite; } } [HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[] { typeof(Character), typeof(Character) })] public static class CompanionIsEnemyPatch { public static void Postfix(Character a, Character b, ref bool __result) { if (!((Object)(object)a == (Object)null) && !((Object)(object)b == (Object)null)) { DvergrCompanion component = ((Component)a).GetComponent(); DvergrCompanion component2 = ((Component)b).GetComponent(); if (((Object)(object)component != (Object)null && component.DuelMode) || ((Object)(object)component2 != (Object)null && component2.DuelMode)) { __result = (Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null && component.DuelMode && component2.DuelMode && component.OwnerId != 0L && component2.OwnerId != 0L && component.OwnerId != component2.OwnerId; } else if (!__result && (Hostile(a, b) || Hostile(b, a))) { __result = true; } } } private static bool Hostile(Character maybeCompanion, Character maybeTarget) { if ((Object)(object)maybeTarget == (Object)null) { return false; } DvergrCompanion component = ((Component)maybeCompanion).GetComponent(); if ((Object)(object)component != (Object)null) { return component.IsHostileTo(maybeTarget); } return false; } } [HarmonyPatch(typeof(Character), "Damage")] public static class CompanionDamagePatch { public static bool Prefix(Character __instance, HitData hit) { if ((Object)(object)__instance == (Object)null || hit == null) { return true; } Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); DvergrCompanion component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { if (component.DuelMode) { if ((Object)(object)val != (Object)null) { return false; } float num = __instance.GetMaxHealth() * 0.05f; if (__instance.GetHealth() - hit.GetTotalDamage() <= num) { __instance.SetHealth(num); (((Object)(object)attacker != (Object)null) ? ((Component)attacker).GetComponent() : null)?.AwardDuelWin(component); component.ExitDuelMode(DvergrCompanion.DuelExitReason.Defeated); return false; } return true; } if ((Object)(object)val != (Object)null) { if (IsButcherKnife(attacker)) { component.GoFeral((Character)(object)val); } else if (!component.IsOwner(val)) { component.Retaliate((Character)(object)val); component.MarkOwnersCompanionsHostile(val.GetPlayerID()); } } return true; } Player val2 = (Player)(object)((__instance is Player) ? __instance : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val != (Object)null && (Object)(object)val2 != (Object)(object)val) { long playerID = val.GetPlayerID(); long playerID2 = val2.GetPlayerID(); foreach (DvergrCompanion item in DvergrCompanion.All) { if (!((Object)(object)item == (Object)null)) { if (item.OwnerId == playerID && item.Stance == CompanionStance.Follow) { item.MarkHostile((Character)(object)val2); } if (item.OwnerId == playerID2 && ((Character)val2).IsPVPEnabled() && ((Character)val).IsPVPEnabled()) { item.MarkHostile((Character)(object)val); item.MarkOwnersCompanionsHostile(playerID); } } } } return true; } private static bool IsButcherKnife(Character attacker) { Humanoid val = (Humanoid)(object)((attacker is Humanoid) ? attacker : null); ItemData val2 = (((Object)(object)val != (Object)null) ? val.GetCurrentWeapon() : null); if (val2 == null) { return false; } string text = (((Object)(object)val2.m_dropPrefab != (Object)null) ? ((Object)val2.m_dropPrefab).name : null); string text2 = ((val2.m_shared != null) ? val2.m_shared.m_name : null); if (text == null || text.IndexOf("Butcher", StringComparison.OrdinalIgnoreCase) < 0) { if (text2 != null) { return text2.IndexOf("butcher", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } return true; } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int) })] public static class CompanionTotemTooltipPatch { public static void Postfix(ItemData item, ref string __result) { if (TotemConversionService.IsCompanionTotem(item)) { __result += TotemConversionService.BuildTooltipBlock(item); } } } [HarmonyPatch(typeof(ItemDrop), "LoadFromZDO", new Type[] { typeof(ItemData), typeof(ZDO) })] public static class CompanionTotemLoadZdoPatch { public static void Postfix(ItemData itemData) { TotemConversionService.ReapplyTotemShared(itemData); } } [HarmonyPatch(typeof(ItemDrop), "LoadFromZDO", new Type[] { typeof(int), typeof(ItemData), typeof(ZDO) })] public static class CompanionTotemLoadIndexedPatch { public static void Postfix(ItemData itemData) { TotemConversionService.ReapplyTotemShared(itemData); } } [HarmonyPatch(typeof(Humanoid), "UseItem")] public static class CompanionTotemUsePatch { public static bool Prefix(Humanoid __instance, Inventory inventory, ItemData item) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } if (!TotemConversionService.IsCompanionTotem(item)) { return true; } Player val = (Player)(object)((__instance is Player) ? __instance : null); Inventory val2 = inventory ?? __instance.m_inventory; if ((Object)(object)val == (Object)null || val2 == null || !val2.ContainsItem(item)) { return true; } if (TotemConversionService.TrySummon(val, item)) { val2.RemoveItem(item, 1); ((Character)val).Message((MessageType)2, "The totem cracks — your companion returns.", 0, (Sprite)null); } else { ((Character)val).Message((MessageType)2, "There is no room here to summon your companion.", 0, (Sprite)null); } return false; } } [HarmonyPatch(typeof(ZInput), "GetButtonDown")] public static class CompanionTypingButtonDownPatch { public static bool Prefix(ref bool __result) { if (!CompanionInventoryGui.IsTyping) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(BaseAI), "SetAggravated")] public static class CorruptionAwakensPatch { private const float Cooldown = 6f; private const float NearRange = 40f; private static float _lastShown = -999f; private static int _nextLine; private static readonly string[] Lines = new string[3] { "Something old stirs in it — the corruption was never truly gone. Roused, it turns on you.", "You have woken the shadow sleeping in its blood. This is why they rage.", "The old corruption rises in it, provoked. It knows only wrath now." }; public static void Prefix(BaseAI __instance, bool aggro) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_005a: 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) if (!aggro || __instance.m_aggravated) { return; } Character component = ((Component)__instance).GetComponent(); if (!((Object)(object)component == (Object)null) && (int)component.m_faction == 10 && !((Object)(object)((Component)component).GetComponent() != (Object)null)) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)MessageHud.instance == (Object)null) && !(Vector3.Distance(((Component)localPlayer).transform.position, ((Component)component).transform.position) > 40f) && !(Time.time - _lastShown < 6f)) { _lastShown = Time.time; string text = Lines[_nextLine % Lines.Length]; _nextLine++; MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false); } } } } [HarmonyPatch(typeof(Character), "GetHoverText")] public static class HoverTextPatch { public static void Postfix(Character __instance, ref string __result) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) DvergrCompanion component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { string arg = (component.IsMaxLevel ? "max" : $"{component.XpPercentToNextLevel:F0}% to next"); __result = __result + $"\n{component.Caste.Display()} · Lv {component.Level} ({arg})" + $"\n[{Plugin.CommunionKey.Value}] Feed"; } else if (CommunionService.IsSubduedDvergr(__instance)) { __result += $"\n[{Plugin.CommunionKey.Value}] Communion"; } } } [HarmonyPatch(typeof(Incinerator), "OnIncinerate")] public static class IncineratorConversionPatch { private const float GatherRange = 15f; public static bool Prefix(Incinerator __instance, Humanoid user, ref bool __result) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) Player val = (Player)(object)((user is Player) ? user : null); if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer) { return true; } if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.m_nview == (Object)null || !__instance.m_nview.IsValid()) { return true; } if (!PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, true, false)) { return true; } if ((Object)(object)__instance.m_container == (Object)null) { return true; } Inventory inventory = __instance.m_container.GetInventory(); if (inventory == null) { return true; } string text = TotemConversionService.WispSharedName(); if (((!string.IsNullOrEmpty(text) && inventory.CountItems(text, -1, true) != 0) ? 1 : 0) <= (false ? 1 : 0)) { return true; } if (GatherFollowers(val, ((Component)__instance).transform.position).Count == 0) { return true; } __result = true; if ((Object)(object)Plugin.Instance != (Object)null) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(SealRitual(__instance, val, text)); } return false; } private static List GatherFollowers(Player player, Vector3 origin) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) List list = new List(); foreach (DvergrCompanion item in DvergrCompanion.All) { if (!((Object)(object)item == (Object)null) && item.IsOwner(player) && item.Stance == CompanionStance.Follow && !item.ChoreActive && !item.DuelMode) { Character component = ((Component)item).GetComponent(); if (!((Object)(object)component == (Object)null) && !component.IsDead() && !(Vector3.Distance(origin, ((Component)item).transform.position) > 15f)) { list.Add(item); } } } list.Sort((DvergrCompanion a, DvergrCompanion b) => Vector3.Distance(origin, ((Component)a).transform.position).CompareTo(Vector3.Distance(origin, ((Component)b).transform.position))); return list; } private static IEnumerator SealRitual(Incinerator inc, Player player, string wispName) { ZNetView nview = inc.m_nview; nview.ClaimOwnership(); ZNetView component = ((Component)inc.m_container).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid()) { component.ClaimOwnership(); } nview.InvokeRPC(ZNetView.Everybody, "RPC_AnimateLever", Array.Empty()); inc.m_leverEffects.Create(((Component)inc).transform.position, ((Component)inc).transform.rotation, (Transform)null, 1f, -1); yield return (object)new WaitForSeconds(Random.Range(inc.m_effectDelayMin, inc.m_effectDelayMax)); nview.InvokeRPC(ZNetView.Everybody, "RPC_AnimateLeverReturn", Array.Empty()); if ((Object)(object)inc.m_lightingAOEs != (Object)null) { Object.Instantiate(inc.m_lightingAOEs, ((Component)inc).transform.position, ((Component)inc).transform.rotation); } Inventory inventory = inc.m_container.GetInventory(); if (inventory == null) { yield break; } int num = ((!string.IsNullOrEmpty(wispName)) ? inventory.CountItems(wispName, -1, true) : 0); List list = GatherFollowers(player, ((Component)inc).transform.position); int num2 = Mathf.Min(num, list.Count); int num3 = 0; for (int i = 0; i < num2; i++) { DvergrCompanion dvergrCompanion = list[i]; if (!((Object)(object)dvergrCompanion == (Object)null)) { ItemData val = TotemConversionService.CreateTotem(dvergrCompanion); if (val == null) { break; } if (!inventory.AddItem(val)) { ((Character)player).Message((MessageType)2, "The incinerator has no room for more totems.", 0, (Sprite)null); break; } inventory.RemoveItem(wispName, 1, -1, true); TotemConversionService.PlaySealVfx(((Component)dvergrCompanion).transform.position + Vector3.up); DissolveCompanion(dvergrCompanion); num3++; } } if (num3 > 0) { ((Character)player).Message((MessageType)2, (num3 == 1) ? "A companion is sealed within a totem." : $"{num3} companions are sealed within totems.", 0, (Sprite)null); } } private static void DissolveCompanion(DvergrCompanion companion) { GameObject gameObject = ((Component)companion).gameObject; ZNetView component = gameObject.GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid()) { component.ClaimOwnership(); ZNetScene.instance.Destroy(gameObject); } else { Object.Destroy((Object)(object)gameObject); } } } [HarmonyPatch(typeof(Character), "OnDeath")] public static class KillXpPatch { private const float XpShareRadius = 20f; private const float PlayerKillXp = 50f; private const float MinXpFraction = 0.25f; public static void Postfix(Character __instance) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)((Component)__instance).GetComponent() != (Object)null) { return; } float num; if (__instance.IsPlayer()) { num = 50f; } else { if ((int)__instance.m_faction == 0) { return; } num = BiomeScaledXp(__instance); } if (num <= 0f) { return; } List list = new List(); Character.GetCharactersInRange(((Component)__instance).transform.position, 20f, list); foreach (Character item in list) { DvergrCompanion component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null) { component.AddXp(num); } } } private static float BiomeScaledXp(Character dead) { //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_005b: 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) GetBiomeTier(Heightmap.FindBiome(((Component)dead).transform.position), out var cap, out var referenceHp); float num = ((referenceHp > 0f) ? (dead.GetMaxHealth() / referenceHp) : 1f); num = Mathf.Clamp(num, 0.25f, 1f); float num2 = Mathf.Round(cap * num); Plugin.Log.LogInfo((object)($"[xp] Kill in {Heightmap.FindBiome(((Component)dead).transform.position)}: " + $"'{((Object)dead).name}' maxHP={dead.GetMaxHealth():F0} / ref {referenceHp:F0} " + $"-> {num2} XP (cap {cap:F0}).")); return num2; } private static void GetBiomeTier(Biome biome, out float cap, out float referenceHp) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected I4, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 if ((int)biome <= 8) { switch (biome - 1) { default: if ((int)biome != 8) { break; } cap = 12f; referenceHp = 150f; return; case 0: cap = 5f; referenceHp = 25f; return; case 1: cap = 22f; referenceHp = 150f; return; case 3: cap = 35f; referenceHp = 100f; return; case 2: break; } } else { if ((int)biome == 16) { cap = 50f; referenceHp = 80f; return; } if ((int)biome == 32) { cap = 100f; referenceHp = 200f; return; } if ((int)biome == 512) { cap = 75f; referenceHp = 200f; return; } } cap = 35f; referenceHp = 100f; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }