Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ValheimNet Villagers v0.6.3
plugins/ValheimNet_Villagers/Villagers.Core.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Splatform; using TMPro; using UnityEngine; using UnityEngine.AI; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Villagers.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Villagers.Core")] [assembly: AssemblyTitle("Villagers.Core")] [assembly: AssemblyVersion("1.0.0.0")] [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 Villagers { [HarmonyPatch(typeof(MonsterAI), "UpdateAI")] internal static class MonsterAiPatch { private static bool Prefix(MonsterAI __instance, ref bool __result) { if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<VillagerBrain>())) { return true; } __result = true; return false; } } public class AutoDoor : MonoBehaviour { [HarmonyPatch(typeof(Door), "Awake")] private static class DoorAwake { private static void Postfix(Door __instance) { Doors.Add(__instance); } } public static bool Enabled = true; public static float Delay = 5f; public static float Clearance = 1.2f; private static readonly HashSet<Door> Doors = new HashSet<Door>(); private readonly Dictionary<Door, float> _openSince = new Dictionary<Door, float>(); private float _t; private void Update() { //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || Time.time < _t) { return; } _t = Time.time + 1f; Doors.RemoveWhere((Door d) => !Object.op_Implicit((Object)(object)d)); foreach (Door item in Doors.ToList()) { ZNetView component = ((Component)item).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner()) { _openSince.Remove(item); } else { if (item.m_canNotBeClosed || Object.op_Implicit((Object)(object)item.m_keyItem)) { continue; } ZDO zDO = component.GetZDO(); float value; if (zDO.GetInt(ZDOVars.s_state, 0) == 0) { _openSince.Remove(item); } else if (!_openSince.TryGetValue(item, out value)) { _openSince[item] = Time.time; } else if (!(Time.time - value < Delay)) { Vector3 p = ((Component)item).transform.position; if (!Character.GetAllCharacters().Any((Character c) => Object.op_Implicit((Object)(object)c) && !c.IsDead() && Vector3.Distance(Flat(((Component)c).transform.position), Flat(p)) < Clearance && Mathf.Abs(((Component)c).transform.position.y - p.y) < 2.5f)) { zDO.Set(ZDOVars.s_state, 0, false); _openSince.Remove(item); ZLog.Log((object)$"[Villagers][door] auto-closed {Utils.GetPrefabName(((Component)item).gameObject)} @{p:F0} after {Time.time - value:0} s"); } } } } } private static Vector3 Flat(Vector3 v) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) v.y = 0f; return v; } } public class BpPiece { public string prefab; public Vector3 pos; public float rotY; } public class BuildStep { public string prefab; public Vector3 worldPos; public Quaternion rot; } public struct Target { public int max; public int min; public Target(int mx, int mn) { max = mx; min = mn; } } public class TargetDef { public string item; public int max; public int min; public TargetDef(string i, int mx, int mn) { item = i; max = mx; min = mn; } } public class UiRow { public string item; public string max; public string min; } public class Blueprint { public string Name; public List<BpPiece> Pieces = new List<BpPiece>(); public static Blueprint Parse(string name, string text) { //IL_0095: 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) Blueprint blueprint = new Blueprint { Name = name }; string[] array = text.Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length != 0 && !text2.StartsWith("#")) { string[] array2 = text2.Split(new char[1] { ';' }); CultureInfo invariantCulture = CultureInfo.InvariantCulture; blueprint.Pieces.Add(new BpPiece { prefab = array2[0], pos = new Vector3(float.Parse(array2[1], invariantCulture), float.Parse(array2[2], invariantCulture), float.Parse(array2[3], invariantCulture)), rotY = float.Parse(array2[4], invariantCulture) }); } } blueprint.Pieces.Sort((BpPiece a, BpPiece b) => a.pos.y.CompareTo(b.pos.y)); return blueprint; } } public static class Census { public static float Interval = 180f; private static float _next = -1f; private static readonly List<ZDO> _buf = new List<ZDO>(); public static List<ZDO> AllVillagerZdos() { _buf.Clear(); int num = 0; if (ZDOMan.instance == null) { return new List<ZDO>(); } while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative("vv_villager", _buf, ref num)) { } return _buf.Where((ZDO z) => z != null && z.IsValid()).ToList(); } public static VillageStone Keeper() { return (from s in VillageStone.All where Object.op_Implicit((Object)(object)s) && (Object)(object)s.Nv != (Object)null && s.Nv.IsValid() orderby ((ZDOID)(ref s.Nv.GetZDO().m_uid)).ID select s).FirstOrDefault(); } public static Dictionary<string, Death.Grave> Known(VillageStone s) { Dictionary<string, Death.Grave> dictionary = new Dictionary<string, Death.Grave>(); object obj; if (s == null) { obj = null; } else { ZNetView nv = s.Nv; if (nv == null) { obj = null; } else { ZDO zDO = nv.GetZDO(); obj = ((zDO != null) ? zDO.GetString("vv_known", "") : null); } } if (obj == null) { obj = ""; } foreach (string item in from x in ((string)obj).Split(new char[1] { ';' }) where x.Length > 0 select x) { try { Death.Grave value = Death.Grave.De(item); if (!string.IsNullOrEmpty(value.name)) { dictionary[value.name] = value; } } catch { } } return dictionary; } private static void SaveKnown(VillageStone s, Dictionary<string, Death.Grave> d) { if (Object.op_Implicit((Object)(object)s) && Object.op_Implicit((Object)(object)s.Nv) && s.Nv.IsValid()) { s.Nv.GetZDO().Set("vv_known", string.Join(";", d.Values.Select((Death.Grave g) => g.Ser()).ToArray())); } } public static void Remember(Death.Grave g) { VillageStone villageStone = Keeper(); if (Object.op_Implicit((Object)(object)villageStone) && !string.IsNullOrEmpty(g.name)) { Dictionary<string, Death.Grave> dictionary = Known(villageStone); g.due = 0.0; dictionary[g.name] = g; SaveKnown(villageStone, dictionary); } } public static void ForgetAll() { foreach (VillageStone item in VillageStone.All.Where((VillageStone x) => Object.op_Implicit((Object)(object)x) && Object.op_Implicit((Object)(object)x.Nv) && x.Nv.IsValid())) { item.Nv.GetZDO().Set("vv_known", ""); } } public static void Tick() { if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || ZDOMan.instance == null) { return; } VillageStone villageStone = Keeper(); if (Object.op_Implicit((Object)(object)villageStone) && villageStone.Nv.IsOwner()) { if (_next < 0f) { _next = Time.time + 60f; } else if (!(Time.time < _next)) { _next = Time.time + Interval; Reconcile(villageStone, verbose: false); } } } public static string Reconcile(VillageStone s, bool verbose) { //IL_0240: Unknown result type (might be due to invalid IL or missing references) List<ZDO> list = AllVillagerZdos(); Dictionary<string, Death.Grave> dictionary = Known(s); List<string> list2 = new List<string>(); bool flag = false; Dictionary<string, int> dictionary2 = new Dictionary<string, int>(); foreach (ZDO item in list) { string text = item.GetString("vv_name", ""); if (string.IsNullOrEmpty(text)) { continue; } dictionary2[text] = ((!dictionary2.TryGetValue(text, out var value)) ? 1 : (value + 1)); Death.Grave value2 = Death.Grave.FromZdo(item, 0.0); if (!dictionary.TryGetValue(text, out var value3) || value3.Ser() != value2.Ser()) { if (!dictionary.ContainsKey(text)) { list2.Add("learned " + text); } dictionary[text] = value2; flag = true; } } foreach (KeyValuePair<string, int> item2 in dictionary2.Where((KeyValuePair<string, int> k) => k.Value > 1)) { list2.Add($"WARNING: {item2.Value} bodies named {item2.Key}"); } HashSet<string> hashSet = new HashSet<string>(from grave in VillageStone.All.Where((VillageStone x) => Object.op_Implicit((Object)(object)x)).SelectMany((VillageStone x) => x.Graves()) select grave.name); foreach (Death.Grave item3 in dictionary.Values.ToList()) { if (!dictionary2.ContainsKey(item3.name) && !hashSet.Contains(item3.name)) { Death.Grave g = item3; g.due = 0.0; g.starter = 1; s.AddGrave(g); list2.Add($"{item3.name} has NO body anywhere in the world and no pending respawn - re-queued for an immediate respawn at the Village Stone @{((Component)s).transform.position:F0}"); } } if (flag) { SaveKnown(s, dictionary); } string text2 = $"census: {list.Count} villager ZDO(s) world-wide, {dictionary.Count} known, {hashSet.Count} pending" + ((list2.Count > 0) ? ("; " + string.Join("; ", list2.ToArray())) : ""); if (verbose || list2.Count > 0) { ZLog.Log((object)("[Villagers][census] " + text2)); } return text2; } public static string Requeue(string nm, bool fem) { //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) VillageStone villageStone = Keeper(); if (!Object.op_Implicit((Object)(object)villageStone)) { return "no Village Stone"; } if (string.IsNullOrEmpty(nm)) { return "usage: <Name> [f|m]"; } if (AllVillagerZdos().Any((ZDO z) => z.GetString("vv_name", "") == nm)) { return nm + " already has a body"; } if (VillageStone.All.Where((VillageStone x) => Object.op_Implicit((Object)(object)x)).Any((VillageStone x) => x.Graves().Any((Death.Grave grave) => grave.name == nm))) { return nm + " already pending"; } Dictionary<string, Death.Grave> dictionary = Known(villageStone); Death.Grave value; Death.Grave g = (dictionary.TryGetValue(nm, out value) ? value : new Death.Grave { name = nm, female = (fem ? 1 : 0), hair = (fem ? "Hair10" : "Hair3"), beard = (fem ? "BeardNone" : "Beard5"), haircol = new Vector3(0.6f, 0.45f, 0.3f), skin = Vector3.one * 0.85f, tier = 1, trait = 0 }); g.due = 0.0; g.starter = 1; Remember(g); villageStone.AddGrave(g); return string.Format("{0} queued for an immediate respawn at the Village Stone @{1:F0} ({2}, starter gear)", nm, ((Component)villageStone).transform.position, dictionary.ContainsKey(nm) ? "remembered look" : "default look"); } public static string Dump() { //IL_0045: 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_008b: 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_0097: 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) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) List<ZDO> list = AllVillagerZdos(); StringBuilder stringBuilder = new StringBuilder(); Vector3? anchor = VillageAnchor.Anchor; stringBuilder.Append($"ALL villager ZDOs world-wide: {list.Count}"); foreach (ZDO item in list) { Vector3 position = item.GetPosition(); ZNetView val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(item) : null); stringBuilder.Append(string.Format("\n {0,-10} pos {1:F0} zone {2} {3} owner {4} persistent {5}", item.GetString("vv_name", ""), position, ZoneSystem.GetZone(position), Object.op_Implicit((Object)(object)val) ? "LOADED" : "not loaded", (item.GetOwner() == ZDOMan.GetSessionID()) ? "server" : ((item.GetOwner() == 0L) ? "none" : item.GetOwner().ToString()), item.Persistent) + string.Format(" job {0} exp {1} hp {2:0}", (Job)item.GetInt("vv_job", 0), item.GetInt("vv_exp_state", 0), item.GetFloat(ZDOVars.s_health, -1f)) + (anchor.HasValue ? $" {Vector3.Distance(anchor.Value, position):0} m from stone" : "") + $" id {item.m_uid}"); } foreach (VillageStone item2 in VillageStone.All.Where((VillageStone x) => Object.op_Implicit((Object)(object)x))) { stringBuilder.Append($"\nStone @{((Component)item2).transform.position:F0}: pending " + string.Join(", ", (from g in item2.Graves() select g.name).ToArray())); if ((Object)(object)item2 == (Object)(object)Keeper()) { stringBuilder.Append("; known " + string.Join(", ", Known(item2).Keys.ToArray())); } } return stringBuilder.ToString(); } } [BepInPlugin("valheimnet.villagers.core", "Villagers.Core", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class CorePlugin : BaseUnityPlugin { private class ClearCmd : ConsoleCommand { public override string Name => "vv_clear"; public override string Help => "remove ALL villagers (server admin)"; public override void Run(string[] a) { Send("clear", 0, 0); } } private class GiveCmd : ConsoleCommand { public override string Name => "vv_give"; public override string Help => "<PrefabName> [count=1] spawn an item at your feet (server admin)"; public override void Run(string[] args) { if (args.Length < 1) { Console.instance.Print("usage: vv_give <PrefabName> [count]"); } else { Send("give:" + args[0], (args.Length <= 1 || !int.TryParse(args[1], out var result)) ? 1 : result, 0); } } } private class ResetCmd : ConsoleCommand { public override string Name => "vv_reset"; public override string Help => "<Name> move a stuck villager to the Village Stone and clear its job (keeps gear/bed) (server admin)"; public override void Run(string[] a) { if (a.Length < 1) { Console.instance.Print("usage: vv_reset <Name>"); } else { Send("reset:" + string.Join(" ", a), 0, 0); } } } private class MobCmd : ConsoleCommand { public override string Name => "vv_spawncreature"; public override string Help => "<Prefab> [count=1, max 10] [level=1] spawn a creature a few m in front of you (server admin)"; public override void Run(string[] a) { if (a.Length < 1) { Console.instance.Print("usage: vv_spawncreature <Prefab> [count] [level]"); } else { Send("mob:" + a[0], (a.Length <= 1 || !int.TryParse(a[1], out var result)) ? 1 : result, (a.Length <= 2 || !int.TryParse(a[2], out var result2)) ? 1 : result2); } } } private class MobAlias : MobCmd { public override string Name => "vv_mob"; } private class GraveCmd : ConsoleCommand { public override string Name => "vv_grave"; public override string Help => "<Name> [minutes=30] [f|m] re-create a lost pending respawn at the nearest Village Stone (server admin)"; public override void Run(string[] a) { if (a.Length < 1) { Console.instance.Print("usage: vv_grave <Name> [minutes=30] [f|m]"); } else { Send("grave:" + a[0], (a.Length > 1 && int.TryParse(a[1], out var result)) ? result : 30, (a.Length > 2 && a[2].StartsWith("f")) ? 1 : 0); } } } private class WakeCmd : ConsoleCommand { public override string Name => "vv_wake"; public override string Help => "[seconds=120] force villagers awake (test, server admin)"; public override void Run(string[] a) { Send("wake", (a.Length != 0 && int.TryParse(a[0], out var result)) ? result : 120, 0); } } private class SleepCmd : ConsoleCommand { public override string Name => "vv_sleep"; public override string Help => "[seconds=120] force villagers to bed (test, server admin)"; public override void Run(string[] a) { Send("sleep", (a.Length != 0 && int.TryParse(a[0], out var result)) ? result : 120, 0); } } private class CensusCmd : ConsoleCommand { public override string Name => "vv_census"; public override string Help => "list ALL villager ZDOs world-wide and re-queue any known villager with no body (server admin)"; public override void Run(string[] a) { Send("census", 0, 0); } } private class BedsCmd : ConsoleCommand { public override string Name => "vv_beds"; public override string Help => "list which villager owns which bed (server admin)"; public override void Run(string[] a) { Send("beds", 0, 0); } } private class StatusCmd : ConsoleCommand { public override string Name => "vv_status"; public override string Help => "print each villager's state/target/gear (server admin)"; public override void Run(string[] a) { Send("status", 0, 0); } } private class ExpCmd : ConsoleCommand { public override string Name => "vv_expedition"; public override string Help => "send <Name> [seconds] | recall <Name> - force an expedition trip (test, server admin)"; public override void Run(string[] a) { if (a.Length < 2 || (a[0] != "send" && a[0] != "recall")) { Console.instance.Print("usage: vv_expedition send <Name> [seconds] | recall <Name>"); } else { Send((a[0] == "send") ? "exp_send" : "exp_recall", StringExtensionMethods.GetStableHashCode(a[1].ToLowerInvariant()), (a.Length > 2 && int.TryParse(a[2], out var result)) ? result : 0); } } } private class SpawnCmd : ConsoleCommand { public override string Name => "vv_spawn"; public override string Help => "[count=3] [tier=1] spawn villagers at nearest Village Stone (server admin)"; public override void Run(string[] args) { int result; int a = ((args.Length != 0 && int.TryParse(args[0], out result)) ? Mathf.Clamp(result, 1, 10) : 3); int result2; int b = ((args.Length <= 1 || !int.TryParse(args[1], out result2)) ? 1 : result2); Send("spawn", a, b); } } private class StoneCmd : ConsoleCommand { public override string Name => "vv_stone"; public override string Help => "[tier 1-7] give a Seidr Stone (server admin)"; public override void Run(string[] args) { Send("stone", (args.Length == 0 || !int.TryParse(args[0], out var result)) ? 1 : result, 0); } } [CompilerGenerated] private static class <>O { public static Action <0>__AddContent; public static CoroutineHandler <1>__OnServerAdmin; public static CoroutineHandler <2>__OnClientAdmin; } public static Blueprint Hut; private static readonly string[] Names; private bool _purged; private float _flagT; private bool _quitting; private static readonly HashSet<string> _registered; private static readonly string[] OurPrefabs; private static CustomRPC _admin; static CorePlugin() { Names = new string[6] { "Astrid", "Bjorn", "Sigrun", "Leif", "Ingrid", "Ulf" }; _registered = new HashSet<string>(); OurPrefabs = new string[5] { "vv_villager", "vv_village_stone", "vv_job_board", "vv_summon_altar", "vv_storage_box" }; try { string configPath = Paths.ConfigPath; string text = Path.Combine(configPath, "jake.villagers.core.cfg"); string text2 = Path.Combine(configPath, "valheimnet.villagers.core.cfg"); if (File.Exists(text) && !File.Exists(text2)) { File.Copy(text, text2); } } catch (Exception) { } } private void Awake() { //IL_0421: Unknown result type (might be due to invalid IL or missing references) //IL_042b: Expected O, but got Unknown //IL_06bf: Unknown result type (might be due to invalid IL or missing references) string path = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Blueprints", "hut4x4.vvbp"); if (File.Exists(path)) { Hut = Blueprint.Parse("hut4x4", File.ReadAllText(path)); } Death.Permadeath = ((BaseUnityPlugin)this).Config.Bind<bool>("Death", "Permadeath", false, "true = dead villagers are gone for good").Value; Death.RespawnDays = ((BaseUnityPlugin)this).Config.Bind<float>("Death", "RespawnDays", 1f, "in-game days until a dead villager returns at the Village Stone").Value; Death.WeakDays = ((BaseUnityPlugin)this).Config.Bind<float>("Death", "WeakenedDays", 1f, "in-game days of Weakened after respawn").Value; Death.WeakHealth = ((BaseUnityPlugin)this).Config.Bind<float>("Death", "WeakenedHealthMult", 0.6f, (ConfigDescription)null).Value; Death.WeakWork = ((BaseUnityPlugin)this).Config.Bind<float>("Death", "WeakenedWorkMult", 0.7f, (ConfigDescription)null).Value; VillagerBrain.DepositReach = ((BaseUnityPlugin)this).Config.Bind<float>("Storage", "DepositReach", 10f, "metres: a villager within this distance of the right storage box deposits directly instead of walking up to it").Value; StorageBox.AutoPickup = ((BaseUnityPlugin)this).Config.Bind<bool>("Storage", "GroundPickup", true, "mod storage boxes pull matching loose items off the ground").Value; StorageBox.PickupRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Storage", "GroundPickupRadius", 25f, "metres around a box to pick up matching drops").Value; StorageBox.PickupInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Storage", "GroundPickupInterval", 2f, "seconds between ground-pickup scans").Value; StorageBox.PickupDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Storage", "GroundPickupDelay", 5f, "seconds a drop must lie on the ground first (so a player's just-dropped item isn't grabbed)").Value; AutoDoor.Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Doors", "AutoClose", true, "close doors left open").Value; AutoDoor.Delay = ((BaseUnityPlugin)this).Config.Bind<float>("Doors", "AutoCloseDelay", 5f, "seconds a door stays open before closing").Value; AutoDoor.Clearance = ((BaseUnityPlugin)this).Config.Bind<float>("Doors", "AutoCloseClearance", 1.2f, "don't close while a player/villager is within this many metres").Value; ((Component)this).gameObject.AddComponent<AutoDoor>(); ExpeditionPortal.Cost = ((BaseUnityPlugin)this).Config.Bind<string>("Expedition", "Cost", "Wood:10,Stone:5", "TEMPORARY cheap test cost (workbench + wood/stone). Vanilla-portal parity: FineWood:20,GreydwarfEye:10,SurtlingCore:2").Value; ExpeditionPortal.TripSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Expedition", "TripSeconds", 120f, "how long a villager is away").Value; ExpeditionPortal.CooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Expedition", "VillagerCooldownSeconds", 120f, "per-villager rest after a trip, real seconds (saved on the villager)").Value; ExpeditionPortal.MaxAway = ((BaseUnityPlugin)this).Config.Bind<int>("Expedition", "MaxAway", 1, "max villagers away at once (default 1); 0 = half the villagers, min 1").Value; ExpeditionPortal.AutoTrips = ((BaseUnityPlugin)this).Config.Bind<bool>("Expedition", "AutoTrips", true, "send an idle villager automatically when a Job Board item is below its min").Value; ExpeditionPortal.InjuryChance = ((BaseUnityPlugin)this).Config.Bind<float>("Expedition", "InjuryChance", 0.2f, (ConfigDescription)null).Value; ExpeditionPortal.GearLossChance = ((BaseUnityPlugin)this).Config.Bind<float>("Expedition", "GearLossChance", 0.1f, (ConfigDescription)null).Value; ((Component)this).gameObject.AddComponent<ExpeditionTicker>(); Crafting.Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Crafting", "Enabled", false, "villagers craft tools/gear sets from village stock at workbenches (Job Board gear rows). Off: crafting is left to the player; villagers still equip better gear found in chests").Value; VillagerBrain.RoamRadius = Mathf.Clamp(((BaseUnityPlugin)this).Config.Bind<float>("Idle", "RoamRadius", 35f, "idle stroll radius around the Village Stone (m), clamped to the village radius").Value, 5f, 200f); Farming.Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Farming", "Enabled", true, "villagers harvest ripe crops whose pickable is removed when picked (carrot, turnip, onion, barley, flax, magecap, jotun puffs...) inside the village and replant the same seed at the same spot from storage. Regrowing plants (berries, mushrooms, thistle, dandelion, PlantEverything bushes) are never touched").Value; Farming.FreeSeed = ((BaseUnityPlugin)this).Config.Bind<string>("Farming", "ReplantMode", "FreeSeed", new ConfigDescription("FreeSeed: a villager who harvests a crop gets one free replant item (CarrotSeeds for a carrot plant, a Carrot for a seed carrot, Barley/Flax for barley/flax) and replants the same crop on the spot; the whole harvest goes to storage. Players are unaffected. SeedStock: the seed-stock loop (uses SeedStock below)", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "FreeSeed", "SeedStock" }), Array.Empty<object>())).Value != "SeedStock"; Farming.SpareSeeds = Mathf.Clamp(((BaseUnityPlugin)this).Config.Bind<int>("Farming", "SpareSeeds", 1, "FreeSeed mode: extra replant items (the plot's own planting item) a villager gets per harvest, on top of the one used to replant. They go to the villager's pack and then to storage").Value, 0, 100); Farming.SeedStock = Mathf.Clamp(((BaseUnityPlugin)this).Config.Bind<int>("Farming", "SeedStock", 10, "SeedStock mode only: when a crop's seed stock (pack + village storage) is below this, the plot is replanted with that crop's seed plant (e.g. a Carrot planted to grow CarrotSeeds) instead of food, and seeds are never spent on food. A Job Board Min for the seed item overrides this").Value, 0, 1000); Firekeeping.Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Firekeeping", "Enabled", true, "villagers top up campfires, hearths, torches, sconces and braziers inside the village that are below Threshold, using each fire's own fuel item (Wood, Resin, Guck...) from village storage. Also needs the Job Board 'Refuel fires' toggle on (default on). Fires a player switched off are left alone").Value; Firekeeping.Threshold = Mathf.Clamp01(((BaseUnityPlugin)this).Config.Bind<float>("Firekeeping", "Threshold", 0.5f, "refuel fires below this fraction of their max fuel").Value); VillagerBrain.Caps["Firekeeping"] = Mathf.Clamp(((BaseUnityPlugin)this).Config.Bind<int>("Firekeeping", "MaxFirekeepers", 1, "how many villagers may refuel fires at once").Value, 0, 5); VillagerBrain.Caps["Farming"] = Mathf.Clamp(((BaseUnityPlugin)this).Config.Bind<int>("Farming", "MaxFarmers", 1, "how many villagers may farm at once").Value, 0, 10); VillagerBrain.IdleFurniture = ((BaseUnityPlugin)this).Config.Bind<bool>("Idle", "UseFurniture", true, "idle villagers sit on free chairs/benches/thrones and warm up by lit fires (never inside them)").Value; VillageStone.BuildingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Building", "Enabled", false, "villagers place/build structures (huts, blueprints). Off by default").Value; VillagerBrain.SleepAt = ((BaseUnityPlugin)this).Config.Bind<float>("Sleep", "SleepAtDayFraction", 0.78f, "Day fraction when villagers go to bed (0=midnight, 0.25~dawn, 0.5=noon, 0.75~dusk)").Value; VillagerBrain.WakeAt = ((BaseUnityPlugin)this).Config.Bind<float>("Sleep", "WakeAtDayFraction", 0.24f, "Day fraction when villagers wake").Value; foreach (string item in VillagerBrain.Caps.Keys.ToList()) { VillagerBrain.Caps[item] = ((BaseUnityPlugin)this).Config.Bind<int>("JobCaps", item, 2, "Max villagers per village doing " + item + " at once (Defense/Sleep/Rest exempt; a full-load haul is always allowed)").Value; } VillagerBrain.DefaultStoneTarget = ((BaseUnityPlugin)this).Config.Bind<int>("Mining", "DefaultStoneTarget", 50, "Mine when village Stone is below this (Job Board Stone min overrides when higher)").Value; PrefabManager.OnVanillaPrefabsAvailable += AddContent; PrefabManager.OnVanillaPrefabsAvailable += ExpeditionPortal.AddContent; new Harmony("valheimnet.villagers.core").PatchAll(); InitAdmin(); RosterBoard.InitRpc(); MapPins.Init(((Component)this).gameObject); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SpawnCmd()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ExpCmd()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new CensusCmd()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new StoneCmd()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ClearCmd()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new StatusCmd()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new BedsCmd()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new GiveCmd()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new GraveCmd()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ResetCmd()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new MobCmd()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new MobAlias()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new WakeCmd()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SleepCmd()); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Villagers.Core loaded; hut pieces={Hut?.Pieces.Count ?? 0}"); } private void CheckTestFlags() { string[] array = new string[2] { "vv_wake.flag", "vv_sleep.flag" }; foreach (string text in array) { string path = Path.Combine(Paths.ConfigPath, text); if (File.Exists(path)) { float result = 120f; float.TryParse(File.ReadAllText(path).Trim(), out result); if (result <= 0f) { result = 120f; } File.Delete(path); if (text == "vv_wake.flag") { VillagerBrain.ForceWakeUntil = Time.time + result; VillagerBrain.ForceSleepUntil = 0f; } else { VillagerBrain.ForceSleepUntil = Time.time + result; VillagerBrain.ForceWakeUntil = 0f; VillagerBrain.SleepEpoch++; } ZLog.Log((object)string.Format("[Villagers] test flag {0}: forcing {1} for {2:0} s", text, (text == "vv_wake.flag") ? "wake" : "sleep", result)); } } } private void CheckShutdownFlag() { if (_quitting || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated() || (_flagT -= Time.deltaTime) > 0f) { return; } _flagT = 2f; CheckTestFlags(); string path = Path.Combine(Paths.ConfigPath, "vv_shutdown.flag"); if (File.Exists(path)) { _quitting = true; File.Delete(path); ZLog.Log((object)"[Villagers] shutdown flag found: saving world, then quitting"); MethodInfo methodInfo = AccessTools.Method(typeof(ZNet), "SaveWorld", (Type[])null, (Type[])null); try { ParameterInfo[] parameters = methodInfo.GetParameters(); methodInfo.Invoke(ZNet.instance, (parameters.Length != 1) ? new object[parameters.Length] : new object[1] { true }); ZLog.Log((object)"[Villagers] world saved (sync) for shutdown"); } catch (Exception ex) { ZLog.LogError((object)("[Villagers] shutdown save failed: " + ex)); } Application.Quit(); } } private void Update() { CheckShutdownFlag(); if (!_purged && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZDOMan.instance != null && !((Object)(object)ZNetScene.instance == (Object)null)) { _purged = true; string path = Path.Combine(Paths.ConfigPath, "vv_purged_v2.flag"); if (!File.Exists(path)) { int num = ClearAll(); File.WriteAllText(path, DateTime.Now.ToString() + " removed " + num); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"One-time purge of legacy villagers: {num}"); } } } public static void Registered(string name) { _registered.Add(name); } private void AddContent() { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown PrefabManager.OnVanillaPrefabsAvailable -= AddContent; try { AddContentInner(); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"FATAL: Villagers content registration failed: {arg}"); } string[] ourPrefabs = OurPrefabs; foreach (string text in ourPrefabs) { if (_registered.Contains(text)) { continue; } try { GameObject val = PrefabManager.Instance.CreateEmptyPrefab(text, true); ZNetView component = val.GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component)) { component.m_persistent = true; component.m_type = (ObjectType)0; } Collider[] componentsInChildren = val.GetComponentsInChildren<Collider>(true); for (int j = 0; j < componentsInChildren.Length; j++) { Object.DestroyImmediate((Object)(object)componentsInChildren[j]); } PrefabManager.Instance.AddPrefab(new CustomPrefab(val, false)); ((BaseUnityPlugin)this).Logger.LogError((object)("FATAL: prefab " + text + " did not register - inert PLACEHOLDER registered so saved " + text + " objects are NOT destroyed. Do not play; deploy a fixed build.")); ZLog.LogError((object)("[Villagers] FATAL: placeholder prefab for " + text + " (saved objects preserved)")); } catch (Exception arg2) { ((BaseUnityPlugin)this).Logger.LogError((object)$"FATAL: could not even register placeholder for {text}: {arg2}"); } } } private void AddContentInner() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Expected O, but got Unknown //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Expected O, but got Unknown //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Expected O, but got Unknown //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Expected O, but got Unknown //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Expected O, but got Unknown //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Expected O, but got Unknown //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Expected O, but got Unknown //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Expected O, but got Unknown //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Expected O, but got Unknown //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Expected O, but got Unknown //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Expected O, but got Unknown GameObject val = PrefabManager.Instance.CreateClonedPrefab("vv_village_stone", "guard_stone"); Object.DestroyImmediate((Object)(object)val.GetComponent<PrivateArea>()); val.AddComponent<VillageStone>(); Piece component = val.GetComponent<Piece>(); if (Object.op_Implicit((Object)(object)component)) { component.m_craftingStation = null; component.m_resources = (Requirement[])(object)new Requirement[0]; } Registered("vv_village_stone"); PieceManager instance = PieceManager.Instance; PieceConfig val2 = new PieceConfig(); val2.Name = "Village Stone"; val2.PieceTable = "Hammer"; val2.Category = "Village"; val2.CraftingStation = "piece_workbench"; val2.Requirements = (RequirementConfig[])(object)new RequirementConfig[2] { new RequirementConfig("Stone", 20, 0, true), new RequirementConfig("Wood", 10, 0, true) }; instance.AddPiece(new CustomPiece(val, true, val2)); GameObject val3 = PrefabManager.Instance.CreateClonedPrefab("vv_job_board", "sign"); Object.DestroyImmediate((Object)(object)val3.GetComponent<Sign>()); val3.AddComponent<JobBoard>(); Piece component2 = val3.GetComponent<Piece>(); if (Object.op_Implicit((Object)(object)component2)) { component2.m_craftingStation = null; component2.m_resources = (Requirement[])(object)new Requirement[0]; } Registered("vv_job_board"); PieceManager instance2 = PieceManager.Instance; val2 = new PieceConfig(); val2.Name = "Village Job Board"; val2.Description = "Set village stock targets"; val2.PieceTable = "Hammer"; val2.Category = "Village"; val2.CraftingStation = "piece_workbench"; val2.Requirements = (RequirementConfig[])(object)new RequirementConfig[1] { new RequirementConfig("Wood", 6, 0, true) }; instance2.AddPiece(new CustomPiece(val3, true, val2)); GameObject val4 = PrefabManager.Instance.CreateClonedPrefab("vv_roster_board", "sign"); Object.DestroyImmediate((Object)(object)val4.GetComponent<Sign>()); val4.AddComponent<RosterBoard>(); Piece component3 = val4.GetComponent<Piece>(); if (Object.op_Implicit((Object)(object)component3)) { component3.m_craftingStation = null; component3.m_resources = (Requirement[])(object)new Requirement[0]; } Registered("vv_roster_board"); PieceManager instance3 = PieceManager.Instance; val2 = new PieceConfig(); val2.Name = "Village Roster"; val2.Description = "See every villager: task, health, gear and what they carry"; val2.PieceTable = "Hammer"; val2.Category = "Village"; val2.CraftingStation = "piece_workbench"; val2.Requirements = (RequirementConfig[])(object)new RequirementConfig[3] { new RequirementConfig("Wood", 6, 0, true), new RequirementConfig("Stone", 2, 0, true), new RequirementConfig("Resin", 1, 0, true) }; instance3.AddPiece(new CustomPiece(val4, true, val2)); try { Recruitment.AddContent(); Registered("vv_summon_altar"); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"FATAL: altar: {arg}"); } GameObject val5 = HumanBody.Build(PrefabManager.Instance.CreateClonedPrefab("vv_villager", "Player")); val5.AddComponent<VillagerBrain>(); CreatureManager.Instance.AddCreature(new CustomCreature(val5, true, new CreatureConfig { Name = "Villager", Faction = (Faction)0 })); Registered("vv_villager"); try { StorageBox.AddContent(); Registered("vv_storage_box"); } catch (Exception arg2) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Storage box failed to load (villagers unaffected): {arg2}"); } try { CraftyBoxesCompat.Init(new Harmony("valheimnet.villagers.core.crafty")); } catch (Exception arg3) { ((BaseUnityPlugin)this).Logger.LogError((object)$"CraftyBoxes compat failed: {arg3}"); } } internal static void InitAdmin() { //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_0025: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown NetworkManager instance = NetworkManager.Instance; object obj = <>O.<1>__OnServerAdmin; if (obj == null) { CoroutineHandler val = OnServerAdmin; <>O.<1>__OnServerAdmin = val; obj = (object)val; } object obj2 = <>O.<2>__OnClientAdmin; if (obj2 == null) { CoroutineHandler val2 = OnClientAdmin; <>O.<2>__OnClientAdmin = val2; obj2 = (object)val2; } _admin = instance.AddRPC("vv_admin", (CoroutineHandler)obj, (CoroutineHandler)obj2); } private static void Send(string cmd, int a, int b) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)localPlayer)) { Console.instance.Print("[Villagers] join a world first"); return; } ZPackage val = new ZPackage(); val.Write(cmd); val.Write(a); val.Write(b); val.Write(((Component)localPlayer).transform.position); long num = VillagersApi.ServerPeerId(); if (num == 0L) { Console.instance.Print("[Villagers] not connected to a server"); return; } Console.instance.Print("[Villagers] " + cmd + " sent to server..."); _admin.SendPackage(num, val); } private static IEnumerator OnServerAdmin(long sender, ZPackage pkg) { string text; try { text = HandleAdmin(sender, pkg); } catch (Exception ex) { text = "ERROR on server: " + ex.Message; Debug.LogError((object)ex); } ZLog.Log((object)("[Villagers] admin cmd from " + sender + ": " + text)); ZPackage val = new ZPackage(); val.Write(text); _admin.SendPackage(sender, val); yield break; } private static string HandleAdmin(long sender, ZPackage pkg) { //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_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_0509: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_059d: Unknown result type (might be due to invalid IL or missing references) //IL_069c: Unknown result type (might be due to invalid IL or missing references) //IL_06a1: Unknown result type (might be due to invalid IL or missing references) //IL_06a8: Unknown result type (might be due to invalid IL or missing references) //IL_06b2: Unknown result type (might be due to invalid IL or missing references) //IL_06b7: Unknown result type (might be due to invalid IL or missing references) //IL_0a3a: Unknown result type (might be due to invalid IL or missing references) //IL_0a3f: Unknown result type (might be due to invalid IL or missing references) //IL_08e3: Unknown result type (might be due to invalid IL or missing references) //IL_08e4: Unknown result type (might be due to invalid IL or missing references) //IL_08ee: Unknown result type (might be due to invalid IL or missing references) //IL_08f3: Unknown result type (might be due to invalid IL or missing references) //IL_08f8: Unknown result type (might be due to invalid IL or missing references) //IL_0902: Unknown result type (might be due to invalid IL or missing references) //IL_0907: Unknown result type (might be due to invalid IL or missing references) //IL_090c: Unknown result type (might be due to invalid IL or missing references) //IL_0a4f: Unknown result type (might be due to invalid IL or missing references) //IL_0a6b: Unknown result type (might be due to invalid IL or missing references) //IL_0a70: Unknown result type (might be due to invalid IL or missing references) //IL_0a8c: Unknown result type (might be due to invalid IL or missing references) //IL_0a91: Unknown result type (might be due to invalid IL or missing references) //IL_0aa2: Unknown result type (might be due to invalid IL or missing references) //IL_0aa3: Unknown result type (might be due to invalid IL or missing references) //IL_0aaa: Unknown result type (might be due to invalid IL or missing references) //IL_0aaf: Unknown result type (might be due to invalid IL or missing references) //IL_0ad7: Unknown result type (might be due to invalid IL or missing references) //IL_0adc: Unknown result type (might be due to invalid IL or missing references) //IL_0ae1: Unknown result type (might be due to invalid IL or missing references) //IL_0b15: Unknown result type (might be due to invalid IL or missing references) //IL_0b17: Unknown result type (might be due to invalid IL or missing references) //IL_0b19: Unknown result type (might be due to invalid IL or missing references) //IL_0b1e: Unknown result type (might be due to invalid IL or missing references) //IL_0af4: Unknown result type (might be due to invalid IL or missing references) //IL_0bad: Unknown result type (might be due to invalid IL or missing references) string text = pkg.ReadString(); int a = pkg.ReadInt(); int num = pkg.ReadInt(); Vector3 val = pkg.ReadVector3(); ZNetPeer peer = ZNet.instance.GetPeer(sender); string text2; if (ZNet.instance.IsDedicated() && (peer == null || !ZNet.instance.IsAdmin(peer.m_socket.GetHostName()))) { text2 = "DENIED: you are not on the server adminlist (id " + ((peer != null) ? peer.m_socket.GetHostName() : null) + ")"; } else { switch (text) { case "exp_send": case "exp_recall": { VillagerBrain villagerBrain2 = VillagersApi.AllVillagers().FirstOrDefault((VillagerBrain x) => Object.op_Implicit((Object)(object)x) && StringExtensionMethods.GetStableHashCode(x.Npc.ToLowerInvariant()) == a); text2 = ((!Object.op_Implicit((Object)(object)villagerBrain2)) ? "no villager with that name on the server" : ExpeditionTicker.Command((text == "exp_send") ? "send" : "recall", villagerBrain2.Npc, num)); break; } case "spawn": { VillageStone villageStone3 = VillageStone.Nearest(val); if (!Object.op_Implicit((Object)(object)villageStone3)) { Vector3? anchor = VillageAnchor.Anchor; text2 = "No Village Stone loaded within ~60 m on the server (server anchor: " + ((anchor.HasValue ? ((object)anchor.GetValueOrDefault()/*cast due to .constrained prefix*/).ToString() : null) ?? "none") + "). Build one, or wait 10 s and retry."; break; } List<string> list = new List<string>(); for (int num11 = 0; num11 < a; num11++) { VillagerBrain villagerBrain = Recruitment.Spawn(villageStone3, ((Component)villageStone3).transform.position + new Vector3((float)(num11 * 2 - 2), 0f, 3f), Mathf.Clamp(num, 1, 7)); list.Add(villagerBrain.Npc + ((villagerBrain.Trait > 0) ? (" (" + villagerBrain.TraitName + ")") : "")); } text2 = string.Format("Spawned {0} tier-{1} villager(s): {2}", a, num, string.Join(", ", list)); break; } case "clear": text2 = $"Removed {ClearAll()} villager(s)"; break; case "status": { VillagerBrain[] array = VillagersApi.AllVillagers(); string text4; if (array.Length != 0) { text4 = string.Join("\n", array.Select((VillagerBrain v) => v.Status())); } else { Vector3? anchor = VillageAnchor.Anchor; text4 = "No villagers loaded on server (anchor " + ((anchor.HasValue ? ((object)anchor.GetValueOrDefault()/*cast due to .constrained prefix*/).ToString() : null) ?? "none") + ")"; } text2 = text4; VillageStone st = VillageStone.Nearest(val); if (Object.op_Implicit((Object)(object)st)) { string[] obj = new string[9] { text2, $"\nVillage: beds {Recruitment.Beds(st)}, chests {st.Chests().Count()}, stock ", string.Join(" ", new string[6] { "Wood", "Stone", "Flint", "CopperOre", "TinOre", "Resin" }.Select((string i) => $"{i}={JobBoard.StockOf(st, i)}")), ", jobs ", string.Join(" ", (from k in VillagerBrain.JobCounts(st) select $"{k.Key}={k.Value}/{VillagerBrain.Caps[k.Key]}").ToArray()), null, null, null, null }; string arg = st.BuildProgress(); EnvMan instance = EnvMan.instance; obj[5] = string.Format(", hut {0}, day {1:0.00} ({2})", arg, (instance != null) ? new float?(instance.GetDayFraction()) : ((float?)null), VillagerBrain.IsSleepTime() ? "night: sleeping" : "day: working"); obj[6] = Crafting.StatusLine(st); obj[7] = Firekeeping.Status(st); obj[8] = string.Join("", from g in st.Graves() select $"\n grave: {g.name} returns in {Math.Max(0.0, (g.due - ZNet.instance.GetTimeSeconds()) / 60.0):0} min"); text2 = string.Concat(obj); } break; } case "census": { VillageStone villageStone2 = Census.Keeper(); text2 = Census.Dump() + (Object.op_Implicit((Object)(object)villageStone2) ? ("\n" + Census.Reconcile(villageStone2, verbose: true)) : ""); ZLog.Log((object)("[Villagers][census] vv_census:\n" + text2)); break; } case "beds": { VillageStone villageStone4 = VillageStone.Nearest(val); text2 = (Object.op_Implicit((Object)(object)villageStone4) ? villageStone4.BedsReport() : "No Village Stone near you"); ZLog.Log((object)("[Villagers][beds] vv_beds:\n" + text2)); break; } default: if (text.StartsWith("reset:")) { string text3 = ZNet.instance.GetPeer(sender)?.m_playerName ?? sender.ToString(); text2 = VillagerBrain.ResetByName(text.Substring(6), text3); } else if (text.StartsWith("grave:")) { VillageStone villageStone = VillageStone.Nearest(val); string nm = text.Substring(6).Trim(); if (!Object.op_Implicit((Object)(object)villageStone)) { text2 = "No Village Stone near you"; break; } if (nm.Length == 0) { text2 = "usage: vv_grave <Name> [minutes=30] [f|m]"; break; } if (villageStone.Graves().Any((Death.Grave g) => g.name == nm) || VillagersApi.AllVillagers().Any((VillagerBrain v) => v.Npc == nm)) { text2 = nm + " is already alive or pending"; break; } bool flag = num == 1; villageStone.AddGrave(new Death.Grave { name = nm, female = (flag ? 1 : 0), hair = (flag ? "Hair10" : "Hair3"), beard = (flag ? "BeardNone" : "Beard5"), haircol = new Vector3(0.6f, 0.45f, 0.3f), skin = Vector3.one * 0.85f, tier = 1, trait = 0, starter = 1, due = ZNet.instance.GetTimeSeconds() + (double)Mathf.Max(1, a) * 60.0 }); Census.Remember(villageStone.Graves().Last()); text2 = string.Format("Re-created pending respawn: {0} ({1}, tier 1, default look) back in {2} min", nm, flag ? "female" : "male", Mathf.Max(1, a)); ZLog.Log((object)("[Villagers] " + text2)); } else if (text == "wake" || text == "sleep") { float num2 = Mathf.Clamp((a <= 0) ? 120 : a, 10, 3600); if (text == "wake") { VillagerBrain.ForceWakeUntil = Time.time + num2; VillagerBrain.ForceSleepUntil = 0f; } else { VillagerBrain.ForceSleepUntil = Time.time + num2; VillagerBrain.ForceWakeUntil = 0f; VillagerBrain.SleepEpoch++; } text2 = $"Forcing villagers to {text} for {num2:0} s"; ZLog.Log((object)("[Villagers] " + text2)); } else if (text.StartsWith("give:")) { string name = text.Substring(5); GameObject val2 = ObjectDB.instance.GetItemPrefab(name) ?? ((IEnumerable<GameObject>)ObjectDB.instance.m_items).FirstOrDefault((Func<GameObject, bool>)((GameObject g) => Object.op_Implicit((Object)(object)g) && string.Equals(((Object)g).name, name, StringComparison.OrdinalIgnoreCase))); ItemDrop val3 = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent<ItemDrop>() : null); if (!Object.op_Implicit((Object)(object)val3)) { text2 = "No item prefab named '" + name + "' (use the internal name, e.g. Wood, AxeBronze, PickaxeAntler)"; break; } int num3 = Mathf.Clamp(a, 1, 999); int num4 = num3; int num5 = Mathf.Max(1, val3.m_itemData.m_shared.m_maxStackSize); while (num4 > 0) { int num6 = Mathf.Min(num4, num5); Object.Instantiate<GameObject>(val2, val + Vector3.up * 0.5f + Random.insideUnitSphere * 0.3f, Quaternion.identity).GetComponent<ItemDrop>().SetStack(num6); num4 -= num6; } text2 = $"Spawned {num3}x {((Object)val2).name} at your feet"; } else if (text.StartsWith("mob:")) { string name2 = text.Substring(4); GameObject val4 = ZNetScene.instance.GetPrefab(name2); if (!Object.op_Implicit((Object)(object)val4)) { GameObject val5 = ((IEnumerable<GameObject>)ZNetScene.instance.m_prefabs).FirstOrDefault((Func<GameObject, bool>)((GameObject g) => Object.op_Implicit((Object)(object)g) && string.Equals(((Object)g).name, name2, StringComparison.OrdinalIgnoreCase))); if (Object.op_Implicit((Object)(object)val5)) { val4 = val5; } } if (!Object.op_Implicit((Object)(object)val4) || !Object.op_Implicit((Object)(object)val4.GetComponent<Character>())) { text2 = "No creature prefab named '" + name2 + "' (internal name, e.g. Boar, Greydwarf, Troll, Skeleton)"; break; } if (((Object)val4).name == "vv_villager" || ((Object)val4).name == "Player") { text2 = "Not allowed: use the Summoning Altar for villagers"; break; } int num7 = Mathf.Clamp(a, 1, 10); int num8 = Mathf.Clamp(num, 1, 10); Vector3 forward = Vector3.forward; GameObject val6 = ((peer != null) ? ZNetScene.instance.FindInstance(peer.m_characterID) : null); if (Object.op_Implicit((Object)(object)val6)) { forward = val6.transform.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.01f) { forward = Vector3.forward; } ((Vector3)(ref forward)).Normalize(); } float num10 = default(float); for (int num9 = 0; num9 < num7; num9++) { Vector3 val7 = val + forward * 4f + new Vector3(Random.Range(-1.5f, 1.5f), 0.5f, Random.Range(-1.5f, 1.5f)); if (Object.op_Implicit((Object)(object)ZoneSystem.instance) && ZoneSystem.instance.GetSolidHeight(val7, ref num10, 1000)) { val7.y = num10 + 0.3f; } Character component = Object.Instantiate<GameObject>(val4, val7, Quaternion.LookRotation(-forward)).GetComponent<Character>(); if (Object.op_Implicit((Object)(object)component) && num8 > 1) { component.SetLevel(num8); } } text2 = string.Format("Spawned {0}x {1}{2} in front of you", num7, ((Object)val4).name, (num8 > 1) ? string.Format(" level {0} ({1} star{2})", num8, num8 - 1, (num8 == 2) ? "" : "s") : ""); ZLog.Log((object)$"[Villagers] vv_mob: {text2} @{val:F0}"); } else { text2 = ((!(text == "stone")) ? "unknown command" : ("OKSTONE:" + Mathf.Clamp(a, 1, 7))); } break; } } return text2; } private static IEnumerator OnClientAdmin(long sender, ZPackage pkg) { string text = pkg.ReadString(); if (text.StartsWith("OKSTONE:")) { int num = int.Parse(text.Substring(8)); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Humanoid)localPlayer).GetInventory().AddItem(ObjectDB.instance.GetItemPrefab(Recruitment.StoneIds[num]), 1); } text = $"Added 1 Seidr Stone (tier {num}) to your inventory"; } Console instance = Console.instance; if (instance != null) { instance.Print("[Villagers] " + text); } Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)1, text, 0, (Sprite)null, false); } yield break; } public static int ClearAll() { List<ZDO> list = new List<ZDO>(); int num = 0; while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative("vv_villager", list, ref num)) { } foreach (ZDO item in list) { ZNetView val = ZNetScene.instance.FindInstance(item); if (Object.op_Implicit((Object)(object)val)) { ZNetView component = ((Component)val).GetComponent<ZNetView>(); component.ClaimOwnership(); component.Destroy(); } else { item.SetOwner(ZDOMan.GetSessionID()); ZDOMan.instance.DestroyZDO(item); } } Census.ForgetAll(); ZLog.Log((object)$"[Villagers] vv_clear removed {list.Count} (village census forgotten too)"); return list.Count; } } public static class Crafting { public class Order { public string prefab; public string why; public string blocker; public Recipe recipe; public CraftingStation station; public bool Ready => blocker == null; } private static readonly HashSet<string> _claimed = new HashSet<string>(); public static bool Enabled = false; private static VillageStone _cs; private static string Key(VillageStone s, string p) { return ((Object)s).GetInstanceID() + "|" + p; } public static bool Claim(VillageStone s, Order o) { return _claimed.Add(Key(s, o.prefab)); } public static void Release(VillageStone s, string prefab) { _claimed.Remove(Key(s, prefab)); } public static bool Claimed(VillageStone s, string prefab) { return _claimed.Contains(Key(s, prefab)); } private static IEnumerable<Recipe> RecipesFor(string prefab) { return ObjectDB.instance.m_recipes.Where((Recipe r) => Object.op_Implicit((Object)(object)r) && r.m_enabled && Object.op_Implicit((Object)(object)r.m_item) && ((Object)r.m_item).name == prefab); } private static int Have(List<Container> ch, ItemDrop it) { return ch.Sum((Container c) => c.GetInventory().CountItems(it.m_itemData.m_shared.m_name, -1, true)) + (Object.op_Implicit((Object)(object)_cs) ? StorageBox.Count(_cs, it.m_itemData.m_shared.m_name) : 0); } public static CraftingStation Station(VillageStone s, Recipe r) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)r.m_craftingStation)) { return null; } return (from c in Physics.OverlapSphere(((Component)s).transform.position, s.Radius) select ((Component)c).GetComponentInParent<CraftingStation>()).FirstOrDefault((Func<CraftingStation, bool>)((CraftingStation cs) => Object.op_Implicit((Object)(object)cs) && cs.m_name == r.m_craftingStation.m_name && cs.GetLevel(true) >= Mathf.Max(1, r.m_minStationLevel))); } private static string Missing(VillageStone s, Recipe r) { _cs = s; List<Container> ch = s.Chests().ToList(); List<string> list = (from q in r.m_resources where Object.op_Implicit((Object)(object)q.m_resItem) && q.m_amount > 0 && Have(ch, q.m_resItem) < q.m_amount select $"{((Object)q.m_resItem).name} {Have(ch, q.m_resItem)}/{q.m_amount}").ToList(); if (list.Count != 0) { return "needs " + string.Join(", ", list.ToArray()); } return null; } private static Order Evaluate(VillageStone s, string prefab, string why) { Order order = null; foreach (Recipe item in RecipesFor(prefab)) { Order order2 = new Order { prefab = prefab, why = why, recipe = item }; if (Object.op_Implicit((Object)(object)item.m_craftingStation)) { order2.station = Station(s, item); if (!Object.op_Implicit((Object)(object)order2.station)) { order2.blocker = $"no {Localization.instance.Localize(item.m_craftingStation.m_name)} lvl {Mathf.Max(1, item.m_minStationLevel)} in village"; } } if (order2.blocker == null) { order2.blocker = Missing(s, item); } if (order == null || (order2.Ready && !order.Ready)) { order = order2; } } return order; } private static bool Usable(ItemData i, Job j) { if (HumanBody.IsTool(i, j)) { if (i.m_shared.m_useDurability) { return i.m_durability > 0f; } return true; } return false; } public static List<Order> Queue(VillageStone s) { List<Order> list = new List<Order>(); List<Container> source = s.Chests().ToList(); List<VillagerBrain> source2 = (from v in VillagersApi.AllVillagers() where (Object)(object)VillageStone.Nearest(((Component)v).transform.position) == (Object)(object)s select v).ToList(); Job[] array = new Job[2] { Job.Woodcutter, Job.Miner }; foreach (Job job in array) { if (source.Any((Container c) => c.GetInventory().GetAllItems().Any((ItemData i) => Usable(i, job))) || source2.Any((VillagerBrain v) => v.Body.GetInventory().GetAllItems().Any((ItemData i) => Usable(i, job)))) { continue; } bool broken = source2.Any((VillagerBrain v) => v.Body.GetInventory().GetAllItems().Any((ItemData i) => HumanBody.IsTool(i, job))); List<Order> list2 = (from p in (from r in ObjectDB.instance.m_recipes where Object.op_Implicit((Object)(object)r) && r.m_enabled && Object.op_Implicit((Object)(object)r.m_item) && HumanBody.IsTool(r.m_item.m_itemData, job) select ((Object)r.m_item).name).Distinct() select Evaluate(s, p, broken ? $"{job} tool broken" : $"no {job} tool") into order where order != null orderby order.Ready descending, HumanBody.Score(order.recipe.m_item.m_itemData) descending select order).ToList(); if (list2.Count > 0) { list.Add(list2[0]); } } JobBoard jobBoard = JobBoard.For(s); if (Object.op_Implicit((Object)(object)jobBoard)) { foreach (KeyValuePair<string, Target> item in from k in jobBoard.Targets() orderby JobBoard.Need(s, k.Key) descending select k) { int num2 = JobBoard.Need(s, item.Key); if (num2 != 0 && item.Value.max > 0) { Order o = Evaluate(s, item.Key, string.Format("Job Board {0}/{1}", JobBoard.StockOf(s, item.Key), (num2 == 2) ? (item.Value.min + " min") : (item.Value.max + " max"))); if (o != null && !list.Any((Order x) => x.prefab == o.prefab)) { list.Add(o); } } } } return list; } public static Order NextReady(VillageStone s) { return Queue(s).FirstOrDefault((Order o) => o.Ready && !Claimed(s, o.prefab)); } public static bool Craft(VillageStone s, Order o, string who) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_03a2: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Unknown result type (might be due to invalid IL or missing references) if (Missing(s, o.recipe) != null) { ZLog.Log((object)("[Villagers] " + who + ": craft " + o.prefab + " cancelled, materials gone")); return false; } ItemData val = o.recipe.m_item.m_itemData.Clone(); val.m_stack = Mathf.Max(1, o.recipe.m_amount); string why; StorageBox storageBox = StorageBox.Accepting(s, ((Object)((Component)o.recipe.m_item).gameObject).name, ((Component)s).transform.position, null, out why); Container val2 = (Object.op_Implicit((Object)(object)storageBox) ? null : s.ChestFor(val)); if (!Object.op_Implicit((Object)(object)storageBox) && !Object.op_Implicit((Object)(object)val2)) { ZLog.Log((object)("[Villagers] " + who + ": craft " + o.prefab + " skipped - nowhere to store the output (" + why + ", no vanilla chest with space)")); return false; } Requirement[] resources = o.recipe.m_resources; foreach (Requirement val3 in resources) { if (!Object.op_Implicit((Object)(object)val3.m_resItem) || val3.m_amount <= 0) { continue; } int num = val3.m_amount; string name = val3.m_resItem.m_itemData.m_shared.m_name; foreach (Container item in s.WritableChests()) { Inventory inventory = item.GetInventory(); int num2 = Mathf.Min(num, inventory.CountItems(name, -1, true)); if (num2 > 0) { inventory.RemoveItem(name, num2, -1, true); num -= num2; ItemLog.TookName(who, name, num2, item); } if (num <= 0) { break; } } if (num > 0) { num -= StorageBox.Take(s, name, num, who + " (craft)"); } } ItemData val4 = o.recipe.m_item.m_itemData.Clone(); val4.m_dropPrefab = ((Component)o.recipe.m_item).gameObject; val4.m_stack = Mathf.Max(1, o.recipe.m_amount); val4.m_quality = 1; val4.m_durability = val4.GetMaxDurability(); val4.m_crafterName = who; string text = (Object.op_Implicit((Object)(object)o.station) ? Localization.instance.Localize(o.station.m_name) : "by hand"); Container val5 = null; int num3 = 0; if (Object.op_Implicit((Object)(object)storageBox)) { num3 = storageBox.ServerAdd(((Object)((Component)o.recipe.m_item).gameObject).name, val4.m_stack, 1, who + " (craft)"); } if (num3 < val4.m_stack) { val4.m_stack -= num3; val5 = s.ChestFor(val4); if (Object.op_Implicit((Object)(object)val5)) { val5.GetInventory().AddItem(val4); ItemLog.Put(who, val4, val4.m_stack, val5); } else { ItemDrop.DropItem(val4, val4.m_stack, (Object.op_Implicit((Object)(object)o.station) ? ((Component)o.station).transform.position : ((Component)s).transform.position) + Vector3.up, Quaternion.identity); } } CraftingStation station = o.station; if (station != null) { EffectList craftItemDoneEffects = station.m_craftItemDoneEffects; if (craftItemDoneEffects != null) { craftItemDoneEffects.Create(((Component)o.station).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } } ZLog.Log((object)string.Format("[Villagers] {0} crafted {1}x {2} at {3} ({4}) -> {5}", who, val4.m_stack, o.prefab, text, o.why, (num3 > 0) ? ("mod box #" + storageBox.Id + " (" + why + ")" + (Object.op_Implicit((Object)(object)val5) ? " + chest" : "")) : (Object.op_Implicit((Object)(object)val5) ? "chest" : "dropped, nowhere to store"))); return true; } public static string StatusLine(VillageStone s) { List<Order> list = Queue(s); if (list.Count == 0) { return "\nCraft queue: empty"; } return "\nCraft queue:" + string.Join("", list.Select((Order o) => "\n " + o.prefab + " [" + o.why + "] " + (Claimed(s, o.prefab) ? "CRAFTING" : (o.Ready ? "ready" : ("blocked: " + o.blocker)))).ToArray()); } } public static class CraftyBoxesCompat { private static Type _api; private static Type _drawer; private static ConstructorInfo _ctor; private static FieldInfo _znvField; public static void Init(Harmony h) { //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Expected O, but got Unknown //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown try { _api = AccessTools.TypeByName("AzuCraftyBoxes.APIs.ItemDrawers_API"); _drawer = AccessTools.TypeByName("AzuCraftyBoxes.APIs.ItemDrawers_API+Drawer"); if (_api == null || _drawer == null) { ZLog.Log((object)"[Villagers][box] AzuCraftyBoxes not installed - box crafting support off"); return; } _ctor = _drawer.GetConstructor(new Type[1] { typeof(ZNetView) }); _znvField = AccessTools.GetDeclaredFields(_drawer).FirstOrDefault((FieldInfo f) => f.FieldType == typeof(ZNetView)); MethodInfo methodInfo = AccessTools.Method(_api, "AllDrawersInRange", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(_drawer, "Remove", (Type[])null, (Type[])null); if (_ctor == null || _znvField == null || methodInfo == null || methodInfo2 == null) { ZLog.LogWarning((object)"[Villagers][box] AzuCraftyBoxes API shape changed - box crafting support off"); return; } h.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(CraftyBoxesCompat), "InRangePost", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); h.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(CraftyBoxesCompat), "RemovePre", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ZLog.Log((object)"[Villagers][box] AzuCraftyBoxes found - storage boxes usable for crafting"); } catch (Exception ex) { ZLog.LogWarning((object)("[Villagers][box] AzuCraftyBoxes hook failed: " + ex.Message)); } } private static void InRangePost(Vector3 pos, float range, object __result) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) try { if (!(__result is IList list)) { return; } foreach (StorageBox item in StorageBox.InRange(pos, range)) { if (item.Amount > 0 && Object.op_Implicit((Object)(object)item.ItemPrefab)) { list.Add(_ctor.Invoke(new object[1] { item.NView })); } } } catch (Exception ex) { ZLog.LogWarning((object)("[Villagers][box] crafty list: " + ex.Message)); } } private static bool RemovePre(object __instance, int amount) { object? value = _znvField.GetValue(__instance); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if (!Object.op_Implicit((Object)(object)(Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<StorageBox>() : null))) { return true; } if (amount > 0) { long num = (ZNet.instance.IsServer() ? ZDOMan.GetSessionID() : (ZNet.instance.GetServerPeer()?.m_uid ?? 0)); val.InvokeRPC(num, "ForceRemove", new object[1] { amount }); } return false; } } public static class Death { [HarmonyPatch(typeof(Character), "ApplyDamage")] private static class LastHit { private static void Prefix(Character __instance, HitData hit) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Invalid comparison between Unknown and I4 //IL_0060: Unknown result type (might be due to invalid IL or missing references) VillagerBrain component = ((Component)__instance).GetComponent<VillagerBrain>(); if (Object.op_Implicit((Object)(object)component)) { if (component.NView.IsOwner()) { component.WearArmor(hit); } Character attacker = hit.GetAttacker(); _cause[((Object)__instance).GetInstanceID()] = (Object.op_Implicit((Object)(object)attacker) ? ("killed by " + Localization.instance.Localize(attacker.m_name)) : (((int)hit.m_hitType == 13) ? "crushed by a tree" : (((int)hit.m_hitType == 3) ? "fell to their death" : (((int)hit.m_hitType == 4) ? "drowned" : $"died ({hit.m_hitType})")))); } } } [HarmonyPatch(typeof(Character), "OnDeath")] private static class OnDeath { private static void Prefix(Character __instance) { //IL_004d: 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_00c3: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Unknown result type (might be due to invalid IL or missing references) //IL_0356: Unknown result type (might be due to invalid IL or missing references) //IL_035b: Unknown result type (might be due to invalid IL or missing references) VillagerBrain component = ((Component)__instance).GetComponent<VillagerBrain>(); if (!Object.op_Implicit((Object)(object)component) || !component.NView.IsOwner()) { return; } _cause.TryGetValue(((Object)__instance).GetInstanceID(), out var value); value = value ?? "died"; ZDO zDO = component.NView.GetZDO(); Vector3 position = ((Component)component).transform.position; List<ItemData> list = component.Body.GetInventory().GetAllItems().ToList(); int num = 0; ZLog.Log((object)("[Villagers][items] " + component.Npc + " died carrying: " + ItemLog.Dump(component.Body.GetInventory()))); GameObject prefab = ZNetScene.instance.GetPrefab("CargoCrate"); if (Object.op_Implicit((Object)(object)prefab) && list.Count > 0) { Container component2 = Object.Instantiate<GameObject>(prefab, position + Vector3.up * 0.3f, Quaternion.identity).GetComponent<Container>(); try { ZNetView component3 = ((Component)component2).GetComponent<ZNetView>(); if (component3 != null) { ZDO zDO2 = component3.GetZDO(); if (zDO2 != null) { zDO2.Set("vv_deathcrate", component.Npc); } } } catch { } foreach (ItemData item in list) { ItemData val = item.Clone(); val.m_equipped = false; if (component2.GetInventory().AddItem(val)) { num++; } } ZLog.Log((object)("[Villagers][items] death crate " + ItemLog.Cn(component2) + " holds: " + ItemLog.Dump(component2.GetInventory()))); } if (!Object.op_Implicit((Object)(object)prefab) && list.Count > 0) { foreach (ItemData item2 in list) { ItemData obj2 = item2.Clone(); obj2.m_equipped = false; ItemDrop.DropItem(obj2, obj2.m_stack, position + Vector3.up * 0.5f + Random.insideUnitSphere * 0.4f, Quaternion.identity); num++; } ZLog.Log((object)$"[Villagers][items] no crate prefab; dropped {num} stacks on the ground at {position:F0}"); } component.Body.GetInventory().RemoveAll(); string text = component.Npc + " died: " + value; MessageHud instance = MessageHud.instance; if (instance != null) { instance.MessageAll((MessageType)2, text); } ZLog.Log((object)string.Format("[Villagers] {0} at {1:F0}; {2} items left in a crate. {3}", text, position, num, Permadeath ? "Permadeath." : $"Respawns in {RespawnDays} day(s).")); if (!Permadeath) { double due = ZNet.instance.GetTimeSeconds() + (double)(RespawnDays * DayLen()); Grave g = Grave.FromZdo(zDO, due); VillageStone villageStone = VillageStone.Home(position) ?? Census.Keeper(); if (!Object.op_Implicit((Object)(object)villageStone)) { ZLog.Log((object)("[Villagers] " + g.name + ": no Village Stone anywhere - cannot queue a respawn (the census will re-queue her once a stone exists)")); return; } Census.Remember(g); villageStone.AddGrave(g); ZLog.Log((object)$"[Villagers] {g.name}: respawn queued at the Village Stone @{((Component)villageStone).transform.position:F0} ({Vector3.Distance(((Component)villageStone).transform.position, position):0} m from the body)"); } } } public struct Grave { public string name; public string hair; public string beard; public int female; public int tier; public int trait; public int starter; public Vector3 haircol; public Vector3 skin; public double due; public string Ser() { return string.Join("|", name, female, hair, beard, haircol.x, haircol.y, haircol.z, skin.x, skin.y, skin.z, tier, trait, due.ToString(CultureInfo.InvariantCulture), starter); } public static Grave De(string s) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) string[] array = s.Split(new char[1] { '|' }); CultureInfo invariantCulture = CultureInfo.InvariantCulture; return new Grave { name = array[0], female = int.Parse(array[1]), hair = array[2], beard = array[3], haircol = new Vector3(float.Parse(array[4], invariantCulture), float.Parse(array[5], invariantCulture), float.Parse(array[6], invariantCulture)), skin = new Vector3(float.Parse(array[7], invariantCulture), float.Parse(array[8], invariantCulture), float.Parse(array[9], invariantCulture)), tier = int.Parse(array[10]), trait = int.Parse(array[11]), due = double.Parse(array[12], invariantCulture), starter = ((array.Length > 13 && array[13] == "1") ? 1 : 0) }; } public static Grave FromZdo(ZDO z, double due) { //IL_0068: 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_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0089: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) return new Grave { name = z.GetString("vv_name", ""), female = z.GetInt("vv_female", 0), hair = z.GetString("vv_hair", ""), beard = z.GetString("vv_beard", ""), haircol = z.GetVec3("vv_haircol", Vector3.one * 0.5f), skin = z.GetVec3("vv_skin", Vector3.one * 0.8f), tier = z.GetInt("vv_tier", 1), trait = z.GetInt("vv_trait", 0), due = due }; } } public static bool Permadeath = false; public static float RespawnDays = 1f; public static float WeakDays = 1f; public static float WeakHealth = 0.6f; public static float WeakWork = 0.7f; private static readonly Dictionary<int, string> _cause = new Dictionary<int, string>(); public static float DayLen() { if (!Object.op_Implicit((Object)(object)EnvMan.instance)) { return 1800f; } return EnvMan.instance.m_dayLengthSec; } public static bool Respawn(VillageStone s, Grave g) { //IL_0040: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: 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_0079: 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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) try { GameObject prefab = PrefabManager.Instance.GetPrefab("vv_villager"); if (!Object.op_Implicit((Object)(object)prefab)) { ZLog.Log((object)("[Villagers] respawn " + g.name + ": vv_villager prefab missing")); return false; } Vector3 val = ((Component)s).transform.position + ((Component)s).transform.forward * 2f; string text = "the Village Stone"; if (Object.op_Implicit((Object)(object)Pathfinding.instance) && Nav.FindValid(Pathfinding.instance, out var point, val, 3f, Nav.Agent)) { val = point; } GameObject val2 = Object.Instantiate<GameObject>(prefab, val + Vector3.up * 0.2f, Quaternion.identity); ZNetView component = val2.GetComponent<ZNetView>(); ZDO val3 = (Object.op_Implicit((Object)(object)component) ? component.GetZDO() : null); if (val3 == null) { ZLog.Log((object)("[Villagers] respawn " + g.name + ": spawned object has no ZDO - destroying, will retry")); Object.Destroy((Object)(object)val2); return false; } val3.Set("vv_name", g.name); val3.Set("vv_female", g.female); val3.Set("vv_hair", g.hair); val3.Set("vv_beard", g.beard); val3.Set("vv_haircol", g.haircol); val3.Set("vv_skin", g.skin); val3.Set("vv_tier", g.tier); val3.Set("vv_trait", g.trait); val3.Set("vv_job", 5); val3.Set("vv_weak_until", (float)(ZNet.instance.GetTimeSeconds() + (double)(WeakDays * DayLen()))); VillagerBrain component2 = val2.GetComponent<VillagerBrain>(); component2.Npc = g.name; component2.Tier = g.tier; component2.Trait = g.trait; component2.ApplyStats(); component2.Refresh(); if (g.starter == 1 && Object.op_Implicit((Object)(object)component2.Body) && component2.Body.GetInventory().NrOfItems() == 0) { ItemData val4 = HumanBody.Starter(Job.Idle); if (val4 != null) { component2.Body.GetInventory().AddItem(val4); ItemLog.Created(g.name, val4, "starter gear after census respawn"); } } MessageHud instance = MessageHud.instance; if (instance != null) { instance.MessageAll((MessageType)2, g.name + " has returned to the village (weakened)"); } ZLog.Log((object)$"[Villagers] {g.name} respawned at {text} {val:F0} (ZDO {val3.m_uid}, persistent {val3.Persistent}), weakened for {WeakDays} day(s); waiting for confirmation"); return true; } catch (Exception ex) { ZLog.LogWarning((object)("[Villagers] respawn " + g.name + " failed: " + ex.Message + " - will retry")); return false; } } } public class ExpeditionPortal : MonoBehaviour { public static readonly List<ExpeditionPortal> All = new List<ExpeditionPortal>(); public static int MaxAway = 1; public static float TripSeconds = 120f; public static float CooldownSeconds = 120f; public static float InjuryChance = 0.2f; public static float GearLossChance = 0.1f; public static bool AutoTrips = true; public static string Cost = "Wood:10,Stone:5"; public const string KState = "vv_exp_state"; public const string KUntil = "vv_exp_until"; public const string KTarget = "vv_exp_target"; public const string KPortal = "vv_exp_portal"; public const string KLeft = "vv_exp_left"; public const string KCd = "vv_exp_cd"; private static float _autoT; private void Awake() { All.Add(this); } private void OnDestroy() { All.Remove(this); } public static void AddContent() { //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Expected O, but got Unknown //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Expected O, but got Unknown //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown PrefabManager.OnVanillaPrefabsAvailable -= AddContent; GameObject val = PrefabManager.Instance.CreateClonedPrefab("vv_expedition_portal", "portal_wood"); if (!Object.op_Implicit((Object)(object)val)) { ZLog.LogWarning((object)"[Villagers][expedition] portal_wood not found - no Expedition Portal piece"); return; } TeleportWorld[] componentsInChildren = val.GetComponentsInChildren<TeleportWorld>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren[i]); } TeleportWorldTrigger[] componentsInChildren2 = val.GetComponentsInChildren<TeleportWorldTrigger>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[i]); } val.AddComponent<ExpeditionPortal>(); List<RequirementConfig> list = new List<RequirementConfig>(); string[] array = Cost.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }); if (array2.Length == 2 && int.TryParse(array2[1].Trim(), out var result) && result > 0) { list.Add(new RequirementConfig(array2[0].Trim(), result, 0, true)); } } PieceManager.Instance.AddPiece(new CustomPiece(val, false, new PieceConfig { Name = "Expedition Portal", Description = "Villagers step through to gather far away and come back with a haul.", PieceTable = "Hammer", Category = "Village", CraftingStation = "piece_workbench", Requirements = list.ToArray() })); ZLog.Log((object)("[Villagers][expedition] portal piece registered, cost " + Cost)); } private static string[] Pool() { List<string> list = new List<string> { "Wood", "Stone", "Resin", "Flint", "LeatherScraps", "DeerHide", "Feathers" }; if (K("defeated_eikthyr")) { list.AddRange(new string[5] { "CopperOre", "TinOre", "FineWood", "GreydwarfEye", "SurtlingCore" }); } if (K("defeated_gdking")) { list.AddRange(new string[4] { "IronScrap", "Guck", "Chain", "ElderBark" }); } if (K("defeated_bonemass")) { list.AddRange(new string[5] { "SilverOre", "Obsidian", "WolfPelt", "Crystal", "FreezeGland" }); } if (K("defeated_dragon")) { list.AddRange(new string[5] { "BlackMetalScrap", "Flax", "Barley", "LoxPelt", "Needle" }); } if (K("defeated_goblinking")) { list.AddRange(new string[5] { "BlackMarble", "YggdrasilWood", "Softtissue", "Carapace", "Sap" }); } if (K("defeated_queen")) { list.AddRange(new string[5] { "FlametalOreNew", "Blackwood", "Grausten", "AskHide", "Charredbone" }); } if (!Object.op_Implicit((Object)(object)ObjectDB.instance)) { return list.ToArray(); } return list.Where((string i) => Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(i))).ToArray(); static bool K(string k) { if (Object.op_Implicit((Object)(object)ZoneSystem.instance)) { return ZoneSystem.instance.GetGlobalKey(k); } return false; } } public static string PickTarget(VillageStone s) { JobBoard jobBoard = JobBoard.For(s); Dictionary<string, Target> dictionary = (Object.op_Implicit((Object)(object)jobBoard) ? jobBoard.Targets() : null); if (dictionary == null) { return "General"; } HashSet<string> hashSet = new HashSet<string>(Pool()); string text = null; float num = 0f; foreach (KeyValuePair<string, Target> item in dictionary) { if (hashSet.Contains(item.Key) && item.Value.min > 0 && VillagerBrain.HasHome(s, item.Key)) { float num2 = (float)(item.Value.min - JobBoard.StockOf(s, item.Key)) / (float)item.Value.min; if (num2 > num) { num = num2; text = item.Key; } } } if (text != null) { return text; } foreach (KeyValuePair<string, Target> item2 in dictionary) { if (hashSet.Contains(item2.Key) && item2.Value.max > 0 && VillagerBrain.HasHome(s, item2.Key)) { float num3 = (float)(item2.Value.max - JobBoard.StockOf(s, item2.Key)) / (float)item2.Value.max; if (num3 > num) { num = num3; text = item2.Key; } } } return text ?? "General"; } private static string Kind(string item) { if (!(item == "General")) { if (!item.EndsWith("Ore") && !item.EndsWith("OreNew")) { switch (item) { default: if (!item.EndsWith("Scrap")) { if (!item.Contains("Hide") && !item.Contains("Pelt")) { switch (item) { default: return "forest"; case "Feathers": case "LeatherScraps": case "Needle": case "FreezeGland": break; } } return "hunting"; } return "scavenging"; case "IronScrap": case "Guck": case "Chain": return "swamp crypt"; case "Stone": case "Flint": case "Obsidian": case "Crystal": case "BlackMarble": case "Grausten": break; } } return "mining"; } return "general"; } public static string PoolText() { return string.Join(", ", Pool()); } public static string Send(VillagerBrain v, float secs, bool force) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) ZNetView nView = v.NView; ZDO val = ((nView != null) ? nView.GetZDO() : null); if (val == null) { return "no ZDO"; } if (val.GetInt("vv_exp_state", 0) != 0) { return v.Npc + " is already on an expedition"; } VillageStone s = VillageStone.Home(((Component)v).transform.position); if (!Object.op_Implicit((Object)(object)s)) { return "no Village Stone"; } ExpeditionPortal expeditionPortal = (from p in All where Object.op_Implicit((Object)(object)p) && Vector3.Distance(((Component)p).transform.position, ((Component)s).transform.position) < s.Radius + 20f orderby Vector3.Distance(((Component)p).transform.position, ((Component)v).transform.position) select p).FirstOrDefault(); if (!Object.op_Implicit((Object)(object)expeditionPortal) && !force) { return "no Expedition Portal in the village"; } string text = PickTarget(s); val.Set("vv_exp_target", text); val.Set("vv_exp_portal", Object.op_Implicit((Object)(object)expeditionPortal) ? ((Component)expeditionPortal).transform.position : ((Component)v).transform.position); val.Set("vv_exp_until", (long)((secs > 0f) ? secs : TripSeconds)); val.Set("vv_exp_state", 1); ZLog.Log((object)string.Format("[Villagers][expedition] {0}: heading to the {1} - {2} trip for {3} (Job Board pick), {4:0} s; trip pool: {5}", v.Npc, Object.op_Implicit((Object)(object)expeditionPortal) ? $"portal @{((Component)expeditionPortal).transform.position:F0}" : "edge of the village (forced, no portal)", Kind(text), text, (secs > 0f) ? secs : TripSeconds, PoolText())); return v.Npc + " sent for " + text; } public static string Recall(VillagerBrain v) { ZNetView nView = v.NView; ZDO val = ((nView != null) ? nView.GetZDO() : null); if (val == null) { return "no ZDO"; } switch (val.GetInt("vv_exp_state", 0)) { case 0: return v.Npc + " is not on an expedition"; case 1: val.Set("vv_exp_state", 0); ZLog.Log((object)("[Villagers][expedition] " + v.Npc + ": trip cancelled before departure")); return v.Npc + " cancelled"; default: val.Set("vv_exp_left", 0f); v.ExpLeft = 0f; return v.Npc + " recalled (returns next tick)"; } } private static int Cap(int n) { if (MaxAway <= 0) { return Mathf.Max(1, n / 2); } return MaxAway; } public static void CooldownTick(VillagerBrain v, float dt) { ZDO zDO = v.NView.GetZDO(); float num = Mathf.Min(v.ExpCd ?? zDO.GetFloat("vv_exp_cd", 0f), CooldownSeconds); if (num <= 0f) { v.ExpCd = 0f; return; } num -= dt; v.ExpCd = num; if (Time.time > v.ExpCdSaveT || num <= 0f) { v.ExpCdSaveT = Time.time + 5f; zDO.Set("vv_exp_cd", Mathf.Max(0f, num)); } } public static void AutoTick() { //IL_0100: Unknown result type (might be due to invalid IL or missing references) if (!AutoTrips || !Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || Time.time < _autoT) { return; } _autoT = Time.time + 30f; if (All.Count == 0) { return; } VillagerBrain[] array = (from v in VillagersApi.AllVillagers() where Object.op_Implicit((Object)(object)v) && Object.op_Implicit((Object)(object)v.NView) && v.NView.IsValid() select v).ToArray(); if (array.Count((VillagerBrain v) => v.NView.GetZDO().GetInt("vv_exp_state", 0) != 0) >= Cap(array.Length)) { return; } foreach (ExpeditionPortal p in All.Where((ExpeditionPortal expeditionPortal) => Object.op_Implicit((Object)(object)expeditionPortal))) { VillageStone s = VillageStone.Home(((Component)p).transform.position); if (Object.op_Implicit((Object)(object)s) && Object.op_Implicit((Object)(object)JobBoard.For(s))) { if (Object.op_Implicit((Object)(object)RandEventSystem.instance) && RandEventSystem.instance.GetCurrentRandomEvent() != null) { break; } VillagerBrain villagerBrain = (from x in array where Object.op_Implicit((Object)(object)x) && Object.op_Implicit((Object)(object)x.NView) && x.NView.IsOwner() && x.St != State.Rest && x.St != State.Sleep && x.St != State.Dodge && Object.op_Implicit((Object)(object)x.Body) && ((Character)x.Body).GetHealth() >= ((Character)x.Body).GetMaxHealth() * 0.5f && x.NView.GetZDO().GetInt("vv_exp_state", 0) == 0 && x.NView.GetZDO().GetFloat("vv_exp_cd", 0f) <= 0f && (Object)(object)VillageStone.Home(((Component)x).transform.position) == (Object)(object)s orderby Vector3.Distance(((Component)x).transform.position, ((Component)p).transform.position) select x).FirstOrDefault(); if (Object.op_Implicit((Object)(object)villagerBrain)) { Send(villagerBrain, 0f, force: false); break; } } } } public static bool Tick(VillagerBrain v) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0148: 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) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: 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_021a: Unknown result type (might be due to invalid IL or missing references) ZDO zDO = v.NView.GetZDO(); int num = zDO.GetInt("vv_exp_state", 0); if (num == 0) { return false; } Vector3 vec = zDO.GetVec3("vv_exp_portal", ((Component)v).transform.position); if (num == 1) { float num2 = Vector3.Distance(((Component)v).transform.position, vec); if (!v.ExpWalkT.HasValue) { v.ExpWalkT = Time.time; } if (num2 > 2.2f && Time.time - v.ExpWalkT.Value < 90f) { v.ExpMove(vec); return true; } v.ExpWalkT = null; long num3 = zDO.GetLong("vv_exp_until", (long)TripSeconds); zDO.Set("vv_exp_left", (float)num3); zDO.Set("vv_exp_state", 2); ZLog.Log((object)string.Format("[Villagers][expedition] {0}: DEPARTED through the portal @{1:F0} ({2}), gear score {3:0.0}, back in {4} s, target {5}", v.Npc, vec, (num2 > 2.2f) ? $"walk timed out {num2:0.0} m away" : "walked in", GearScore(v), num3, zDO.GetString("vv_exp_target", ""))); return true; } v.ExpHold(vec); float num4 = (v.ExpLeft ?? zDO.GetFloat("vv_exp_left", 0f)) - Time.deltaTime; v.ExpLeft = num4; if (Time.time > v.ExpSaveT) { v.ExpSaveT = Time.time + 1f; zDO.Set("vv_exp_left", Mathf.Max(0f, num4)); } if (Time.time > v.ExpLogT) { v.ExpLogT = Time.time + 30f; ZLog.Log((object)$"[Villagers][expedition] {v.Npc}: away, back in {Mathf.Max(0f, num4):0} s"); } if (num4 > 0f) { return true; } v.ExpLeft = null; Return(v, zDO, vec); return false; } private static float
plugins/ValheimNet_Villagers/Villagers.DayDial.dll
Decompiled 2 days agousing System; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Villagers.DayDial")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Villagers.DayDial")] [assembly: AssemblyTitle("Villagers.DayDial")] [assembly: AssemblyVersion("1.0.0.0")] [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 Villagers.DayDial { [BepInPlugin("valheimnet.villagers.daydial", "Villagers.DayDial", "0.1.0")] public class DayDialPlugin : BaseUnityPlugin { private ConfigEntry<bool> _enabled; private ConfigEntry<float> _size; private ConfigEntry<float> _offX; private ConfigEntry<float> _offY; private ConfigEntry<bool> _followMinimap; private Texture2D _ring; private int _texSize; private Rect _lastMapRect; private float _mapT; private const int Steps = 256; private Texture2D _scene; private Texture2D _star; private int _sceneStep = -1; private readonly Vector2[] _starPos = (Vector2[])(object)new Vector2[22]; private readonly float[] _starPhase = new float[22]; private readonly float[] _starSpeed = new float[22]; private readonly float[] _starSize = new float[22]; private const float HorizonY = 0.4f; private const float ArcR = 0.4f; private Color[] _px; static DayDialPlugin() { try { string configPath = Paths.ConfigPath; string text = Path.Combine(configPath, "jake.villagers.daydial.cfg"); string text2 = Path.Combine(configPath, "valheimnet.villagers.daydial.cfg"); if (File.Exists(text) && !File.Exists(text2)) { File.Copy(text, text2); } } catch (Exception) { } } private void Awake() { _enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("DayDial", "Enabled", true, "Show the day/night dial"); _size = ((BaseUnityPlugin)this).Config.Bind<float>("DayDial", "Size", 90f, "Dial diameter in pixels at 1080p (scales with screen height)"); _followMinimap = ((BaseUnityPlugin)this).Config.Bind<bool>("DayDial", "FollowMinimap", true, "Place the dial right under the small minimap (falls back to the top-right corner if it can't be found)"); _offX = ((BaseUnityPlugin)this).Config.Bind<float>("DayDial", "OffsetX", 0f, "Extra horizontal offset in pixels (+ = left)"); _offY = ((BaseUnityPlugin)this).Config.Bind<float>("DayDial", "OffsetY", 8f, "Extra vertical offset in pixels below the minimap (+ = down)"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Villagers.DayDial loaded"); } private bool ShouldShow() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 if (!_enabled.Value || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)EnvMan.instance)) { return false; } Minimap instance = Minimap.instance; if (!Object.op_Implicit((Object)(object)instance) || (int)instance.m_mode != 1 || !Object.op_Implicit((Object)(object)instance.m_smallRoot) || !instance.m_smallRoot.activeInHierarchy) { return false; } if (Menu.IsVisible() || Hud.IsUserHidden() || (Object.op_Implicit((Object)(object)InventoryGui.instance) && InventoryGui.IsVisible()) || Console.IsVisible() || TextInput.IsVisible() || StoreGui.IsVisible()) { return false; } return true; } private bool MapRect(out Rect r) { //IL_0002: 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_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0089: 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_0093: 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_00a0: 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_00af: 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_00be: 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_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) r = _lastMapRect; if (Time.unscaledTime < _mapT) { return ((Rect)(ref r)).width > 1f; } _mapT = Time.unscaledTime + 1f; try { RectTransform component = Minimap.instance.m_smallRoot.GetComponent<RectTransform>(); if (!Object.op_Implicit((Object)(object)component)) { return false; } Canvas componentInParent = ((Component)component).GetComponentInParent<Canvas>(); Camera obj = ((Object.op_Implicit((Object)(object)componentInParent) && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null); Vector3[] array = (Vector3[])(object)new Vector3[4]; component.GetWorldCorners(array); Vector2 val = RectTransformUtility.WorldToScreenPoint(obj, array[0]); Vector2 val2 = RectTransformUtility.WorldToScreenPoint(obj, array[2]); r = new Rect(val.x, (float)Screen.height - val2.y, val2.x - val.x, val2.y - val.y); _lastMapRect = r; return ((Rect)(ref r)).width > 1f; } catch { _lastMapRect = default(Rect); return false; } } private static float Smooth(float a, float b, float x) { float num = Mathf.Clamp01((x - a) / (b - a)); return num * num * (3f - 2f * num); } private static float SunAngle(float frac) { return (frac - 0.25f) * (float)Math.PI * 2f; } private static float StarAlpha(float frac) { return 1f - Smooth(-0.22f, 0.04f, Mathf.Sin(SunAngle(frac))); } private void OnGUI() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) if ((int)Event.current.type != 7 || !ShouldShow()) { return; } float num = (float)Screen.height / 1080f; float num2 = Mathf.Clamp(_size.Value, 30f, 400f) * num; int num3 = Mathf.Clamp(Mathf.NextPowerOfTwo(Mathf.CeilToInt(num2)), 64, 256); if (!Object.op_Implicit((Object)(object)_ring) || num3 != _texSize) { BuildTextures(num3); } float num4 = Mathf.Repeat(EnvMan.instance.GetDayFraction(), 1f); int num5 = Mathf.FloorToInt(num4 * 256f) % 256; if (num5 != _sceneStep) { _sceneStep = num5; BakeScene(((float)num5 + 0.5f) / 256f); } Rect val = default(Rect); if (_followMinimap.Value && MapRect(out var r)) { ((Rect)(ref val))..ctor(((Rect)(ref r)).xMax - num2 - _offX.Value * num, ((Rect)(ref r)).yMax + _offY.Value * num, num2, num2); } else { ((Rect)(ref val))..ctor((float)Screen.width - num2 - (20f + _offX.Value) * num, (280f + _offY.Value) * num, num2, num2); } GUI.DrawTexture(val, (Texture)(object)_scene); float num6 = StarAlpha(num4); if (num6 > 0.01f) { float num7 = SunAngle(num4) + (float)Math.PI; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f - Mathf.Cos(num7) * 0.4f, 1f - (0.4f + Mathf.Sin(num7) * 0.4f)); Color color = GUI.color; float unscaledTime = Time.unscaledTime; for (int i = 0; i < _starPos.Length; i++) { Vector2 val3 = _starPos[i]; Vector2 val4 = val3 - val2; if (!(((Vector2)(ref val4)).magnitude < 0.14f)) { float num8 = 0.45f + 0.55f * (0.5f + 0.5f * Mathf.Sin(unscaledTime * _starSpeed[i] + _starPhase[i])); float num9 = num2 * _starSize[i]; GUI.color = new Color(1f, 1f, 1f, num6 * num8); GUI.DrawTexture(new Rect(((Rect)(ref val)).x + val3.x * num2 - num9 / 2f, ((Rect)(ref val)).y + val3.y * num2 - num9 / 2f, num9, num9), (Texture)(object)_star); } } GUI.color = color; } GUI.DrawTexture(val, (Texture)(object)_ring); } private void OnDestroy() { if (Object.op_Implicit((Object)(object)_scene)) { Object.Destroy((Object)(object)_scene); } if (Object.op_Implicit((Object)(object)_ring)) { Object.Destroy((Object)(object)_ring); } if (Object.op_Implicit((Object)(object)_star)) { Object.Destroy((Object)(object)_star); } } private static Texture2D NewTex(int w, int h) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown return new Texture2D(w, h, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1, hideFlags = (HideFlags)61 }; } private static float Cover(float dist, float edge) { return Mathf.Clamp01(edge - dist + 0.5f); } private void BuildTextures(int n) { //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) OnDestroy(); _texSize = n; _sceneStep = -1; _scene = NewTex(n, n); _px = (Color[])(object)new Color[n * n]; float num = (float)n / 2f - 1f; float num2 = (float)n / 2f; float num3 = (float)n / 2f; _ring = NewTex(n, n); Color[] array = (Color[])(object)new Color[n * n]; Color val = default(Color); ((Color)(ref val))..ctor(0.85f, 0.7f, 0.35f); Color val2 = default(Color); ((Color)(ref val2))..ctor(0.08f, 0.06f, 0.04f); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { float num4 = (float)j + 0.5f - num2; float num5 = (float)i + 0.5f - num3; float dist = Mathf.Sqrt(num4 * num4 + num5 * num5); float num6 = Cover(dist, num); float num7 = Cover(dist, num - Mathf.Max(2f, (float)n * 0.035f)); float num8 = Cover(dist, num - (float)n * 0.012f) - Cover(dist, num - (float)n * 0.024f); Color val3 = Color.Lerp(val2, val, Mathf.Clamp01(num8)); val3.a = Mathf.Clamp01(num6 - num7) * 0.95f; array[i * n + j] = val3; } } _ring.SetPixels(array); _ring.Apply(false, true); int num9 = 16; _star = NewTex(num9, num9); Color[] array2 = (Color[])(object)new Color[num9 * num9]; for (int k = 0; k < num9; k++) { for (int l = 0; l < num9; l++) { float num10 = ((float)l + 0.5f - (float)num9 / 2f) / ((float)num9 / 2f); float num11 = ((float)k + 0.5f - (float)num9 / 2f) / ((float)num9 / 2f); float num12 = Mathf.Sqrt(num10 * num10 + num11 * num11); float num13 = Mathf.Clamp01(1f - num12 / 0.35f); num13 = num13 * num13 + Mathf.Clamp01(1f - Mathf.Abs(num10) / 0.08f) * Mathf.Clamp01(1f - Mathf.Abs(num11)) * 0.5f + Mathf.Clamp01(1f - Mathf.Abs(num11) / 0.08f) * Mathf.Clamp01(1f - Mathf.Abs(num10)) * 0.5f; array2[k * num9 + l] = new Color(1f, 0.97f, 0.88f, Mathf.Clamp01(num13)); } } _star.SetPixels(array2); _star.Apply(false, true); Random random = new Random(4242); Vector2 val4 = default(Vector2); for (int m = 0; m < _starPos.Length; m++) { int num14 = 0; Vector2 val5; do { ((Vector2)(ref val4))..ctor((float)random.NextDouble(), (float)random.NextDouble() * 0.5f); num14++; if (num14 >= 50) { break; } val5 = val4 - new Vector2(0.5f, 0.5f); } while (((Vector2)(ref val5)).magnitude > 0.4f); _starPos[m] = val4; _starPhase[m] = (float)random.NextDouble() * 6.283f; _starSpeed[m] = 1.2f + (float)random.NextDouble() * 2.3f; _starSize[m] = 0.035f + (float)random.NextDouble() * 0.035f; } } private static Color Lerp3(Color night, Color day, Color warm, float dayness, float warmth) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: 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_000b: Unknown result type (might be due to invalid IL or missing references) return Color.Lerp(Color.Lerp(night, day, dayness), warm, warmth); } private static float Hill(float u, float a, float b, float c) { return a + b * Mathf.Sin(u * 7.1f + c) + b * 0.6f * Mathf.Sin(u * 13.7f + c * 2.3f) + b * 0.3f * Mathf.Sin(u * 29.3f + c * 0.7f); } private void BakeScene(float frac) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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_0137: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_035b: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03b3: Unknown result type (might be due to invalid IL or missing references) //IL_03b5: Unknown result type (might be due to invalid IL or missing references) //IL_03ba: Unknown result type (might be due to invalid IL or missing references) //IL_03c5: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Unknown result type (might be due to invalid IL or missing references) //IL_04da: Unknown result type (might be due to invalid IL or missing references) //IL_04dc: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Unknown result type (might be due to invalid IL or missing references) //IL_04e5: Unknown result type (might be due to invalid IL or missing references) //IL_04e7: Unknown result type (might be due to invalid IL or missing references) //IL_04e9: Unknown result type (might be due to invalid IL or missing references) //IL_050b: Unknown result type (might be due to invalid IL or missing references) //IL_0512: Unknown result type (might be due to invalid IL or missing references) //IL_0517: Unknown result type (might be due to invalid IL or missing references) //IL_052f: Unknown result type (might be due to invalid IL or missing references) //IL_0531: Unknown result type (might be due to invalid IL or missing references) //IL_03dc: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_0405: Unknown result type (might be due to invalid IL or missing references) //IL_040a: Unknown result type (might be due to invalid IL or missing references) //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_0415: Unknown result type (might be due to invalid IL or missing references) //IL_0427: Unknown result type (might be due to invalid IL or missing references) //IL_042c: Unknown result type (might be due to invalid IL or missing references) //IL_0431: Unknown result type (might be due to invalid IL or missing references) //IL_0436: Unknown result type (might be due to invalid IL or missing references) //IL_0441: Unknown result type (might be due to invalid IL or missing references) //IL_0443: Unknown result type (might be due to invalid IL or missing references) //IL_046a: Unknown result type (might be due to invalid IL or missing references) //IL_046f: Unknown result type (might be due to invalid IL or missing references) int texSize = _texSize; float edge = (float)texSize / 2f - 1f; float num = (float)texSize / 2f; float num2 = (float)texSize / 2f; float num3 = SunAngle(frac); float num4 = Mathf.Sin(num3); float num5 = Smooth(-0.12f, 0.3f, num4); float num6 = Mathf.Exp((0f - num4 / 0.2f) * (num4 / 0.2f)); float num7 = 0.4f * (float)texSize; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(num - Mathf.Cos(num3) * 0.4f * (float)texSize, num7 + num4 * 0.4f * (float)texSize); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(num + Mathf.Cos(num3) * 0.4f * (float)texSize, num7 - num4 * 0.4f * (float)texSize); float num8 = (float)texSize * 0.075f; Color val3 = Lerp3(new Color(0.02f, 0.03f, 0.1f), new Color(0.16f, 0.42f, 0.86f), new Color(0.28f, 0.22f, 0.45f), num5, num6 * 0.45f); Color val4 = Lerp3(new Color(0.07f, 0.09f, 0.2f), new Color(0.62f, 0.82f, 0.98f), new Color(1f, 0.55f, 0.25f), num5, num6 * 0.85f); Color val5 = Lerp3(new Color(0.22f, 0.24f, 0.28f), new Color(0.36f, 0.62f, 0.32f), new Color(0.45f, 0.3f, 0.25f), num5, num6 * 0.5f); Color val6 = Lerp3(new Color(0.13f, 0.14f, 0.17f), new Color(0.2f, 0.48f, 0.16f), new Color(0.3f, 0.2f, 0.14f), num5, num6 * 0.5f); Color val7 = Color.Lerp(new Color(1f, 0.55f, 0.2f), new Color(1f, 0.93f, 0.55f), Smooth(0f, 0.4f, num4)); Color val8 = default(Color); ((Color)(ref val8))..ctor(0.9f, 0.91f, 0.85f); for (int i = 0; i < texSize; i++) { for (int j = 0; j < texSize; j++) { float num9 = (float)j + 0.5f; float num10 = (float)i + 0.5f; float num11 = num9 - num; float num12 = num10 - num2; float num13 = Cover(Mathf.Sqrt(num11 * num11 + num12 * num12), edge); if (num13 <= 0f) { _px[i * texSize + j] = Color.clear; continue; } float num14 = Mathf.Clamp01((num10 - num7) / ((float)texSize - num7)); Color val9 = Color.Lerp(val4, val3, Mathf.Pow(num14, 0.8f)); float num15 = num6 * Mathf.Exp(0f - Mathf.Pow((num9 - val.x) / ((float)texSize * 0.35f), 2f)) * Mathf.Exp((0f - num14) * 2.5f); val9 = Color.Lerp(val9, new Color(1f, 0.62f, 0.3f), num15 * 0.6f); Vector2 val10 = new Vector2(num9, num10) - val; float magnitude = ((Vector2)(ref val10)).magnitude; if (val.y > num7 - num8 * 2f) { val9 = Color.Lerp(val9, val7, Mathf.Clamp01(1f - magnitude / (num8 * 3.2f)) * 0.35f); val9 = Color.Lerp(val9, val7, Cover(magnitude, num8)); } val10 = new Vector2(num9, num10) - val2; float magnitude2 = ((Vector2)(ref val10)).magnitude; if (val2.y > num7 - num8 * 2f) { val9 = Color.Lerp(val9, val8, Mathf.Clamp01(1f - magnitude2 / (num8 * 2.6f)) * 0.15f * (1f - num5)); val10 = new Vector2(num9, num10) - (val2 + new Vector2(num8 * 0.55f, num8 * 0.25f)); float magnitude3 = ((Vector2)(ref val10)).magnitude; val9 = Color.Lerp(val9, val8, Cover(magnitude2, num8 * 0.95f) * (1f - Cover(magnitude3, num8 * 0.85f))); } float u = num9 / (float)texSize; float num16 = num7 + Hill(u, 0.02f * (float)texSize, 0.035f * (float)texSize, 1.3f); float num17 = num7 + Hill(u, -0.06f * (float)texSize, 0.03f * (float)texSize, 4.1f); float num18 = Mathf.Clamp01(num16 - num10 + 0.5f); float num19 = Mathf.Clamp01(num17 - num10 + 0.5f); val9 = Color.Lerp(val9, val5, num18); val9 = Color.Lerp(val9, val6 * (0.85f + 0.15f * Mathf.Clamp01(num10 / Mathf.Max(1f, num17))), num19); val9.a = num13; _px[i * texSize + j] = val9; } } _scene.SetPixels(_px); _scene.Apply(false, false); } } }
plugins/ValheimNet_Villagers/Villagers.Defense.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Villagers.Defense")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Villagers.Defense")] [assembly: AssemblyTitle("Villagers.Defense")] [assembly: AssemblyVersion("1.0.0.0")] [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 Villagers.Defense { [BepInPlugin("valheimnet.villagers.defense", "Villagers.Defense", "0.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class DefensePlugin : BaseUnityPlugin { public static float ThreatRadius; public static float FleeHealth; private static readonly Dictionary<int, float> _lastThreat; private static readonly Dictionary<int, float> _swing; static DefensePlugin() { ThreatRadius = 30f; FleeHealth = 0.3f; _lastThreat = new Dictionary<int, float>(); _swing = new Dictionary<int, float>(); try { string configPath = Paths.ConfigPath; string text = Path.Combine(configPath, "jake.villagers.defense.cfg"); string text2 = Path.Combine(configPath, "valheimnet.villagers.defense.cfg"); if (File.Exists(text) && !File.Exists(text2)) { File.Copy(text, text2); } } catch (Exception) { } } private void Awake() { ThreatRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Defense", "ThreatRadius", 30f, (ConfigDescription)null).Value; FleeHealth = ((BaseUnityPlugin)this).Config.Bind<float>("Defense", "FleeHealthFraction", 0.3f, (ConfigDescription)null).Value; VillagersApi.OverrideTick += Tick; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Villagers.Defense loaded (everyone defends)"); } private static bool Tick(VillagerBrain b, VillageStone s) { //IL_00c6: 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_0103: 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_0113: 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) //IL_0122: Unknown result type (might be due to invalid IL or missing references) int instanceID = ((Object)b).GetInstanceID(); Character val = NearestThreat(b, s); if (!Object.op_Implicit((Object)(object)val)) { return false; } if (!_lastThreat.TryGetValue(instanceID, out var value) || Time.time - value > 15f) { ZLog.Log((object)("[Villagers] " + b.Npc + " sees " + val.m_name + " -> defend")); } _lastThreat[instanceID] = Time.time; b.Run = true; b.Goal = null; if (((Character)b.Body).GetHealthPercentage() < FleeHealth) { b.Job = (Job)5; b.Target = s.Shelter() ?? ((Component)s).gameObject; return true; } b.Job = (Job)2; b.EquipFor((Job)2, s, 0); b.Target = ((Component)val).gameObject; if (b.Near(((Component)val).gameObject, 2.6f)) { ((Component)b).transform.rotation = Quaternion.LookRotation(Vector3.ProjectOnPlane(((Component)val).transform.position - ((Component)b).transform.position, Vector3.up)); if (!_swing.TryGetValue(instanceID, out var value2) || Time.time > value2) { _swing[instanceID] = Time.time + 1.2f; if (((Character)b.Body).StartAttack(val, false)) { b.Wear(b.Held, -1f); } } } return true; } private static Character NearestThreat(VillagerBrain b, VillageStone s) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0098: 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) Character result = null; float num = ThreatRadius; foreach (Character allCharacter in Character.GetAllCharacters()) { if (Object.op_Implicit((Object)(object)allCharacter) && !allCharacter.IsDead() && !allCharacter.IsPlayer() && !allCharacter.IsTamed() && (int)allCharacter.GetFaction() != 0 && !Object.op_Implicit((Object)(object)((Component)allCharacter).GetComponent<VillagerBrain>()) && BaseAI.IsEnemy((Character)(object)b.Body, allCharacter) && !(Vector3.Distance(((Component)allCharacter).transform.position, ((Component)s).transform.position) > s.Radius * 1.25f)) { float num2 = Vector3.Distance(((Component)b).transform.position, ((Component)allCharacter).transform.position); if (num2 < num) { num = num2; result = allCharacter; } } } return result; } } }
plugins/ValheimNet_Villagers/Villagers.Net.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Villagers.Net")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Villagers.Net")] [assembly: AssemblyTitle("Villagers.Net")] [assembly: AssemblyVersion("1.0.0.0")] [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 Villagers.Net { public static class Bosses { private sealed class Fight { public string Key; public string Name; public float LastPct = 1f; public int Fired; public bool Engaged; public float NextIdle; public float NextReply; public Vector3 Pos; } private static readonly Dictionary<ZDOID, Fight> _f = new Dictionary<ZDOID, Fight>(); private static readonly Dictionary<string, string> _map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); public static readonly Dictionary<string, string> Persona = new Dictionary<string, string> { { "eikthyr", "Eikthyr, the thunder stag of the Meadows; proud, wild, mocks weak first-timers." }, { "elder", "The Elder, an ancient tree giant of the Black Forest; slow, patient, deep, speaks of roots and rot." }, { "bonemass", "Bonemass, a gurgling swamp slime-skeleton; gleeful, gross, calls people maggot." }, { "moder", "Moder, the ice dragon mother of the Mountains; cold, sinister, hisses her S sounds." }, { "yagluth", "Yagluth, the dead goblin king of the Plains, a giant burning skeleton; arrogant, regal, demands kneeling." }, { "queen", "The Seeker Queen of the Mistlands hive; posh, snobby, sinister, calls people darling and food." }, { "fader", "Fader, the fire warlord of the Ashlands; furious, thunderous, enraged at being bothered." }, { "kall", "Kall Fimbulbringer, a Jotun frost giant just freed from ages in chains; vengeful, booming, promises endless winter." }, { "generic", "an ancient hostile boss; arrogant and cruel." } }; public static readonly string[] KnownKeys = new string[9] { "eikthyr", "elder", "bonemass", "moder", "yagluth", "queen", "fader", "kall", "generic" }; public static readonly string[] Events = new string[8] { "spawn", "hp75", "hp50", "hp25", "pdeath", "death", "idle", "reply" }; private static readonly List<ZDO> _zl = new List<ZDO>(); private static readonly Dictionary<int, Character> _proto = new Dictionary<int, Character>(); public static void ParseMap(string s) { _map.Clear(); string[] array = (s ?? "").Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { '=' }); if (array2.Length == 2 && array2[0].Trim().Length > 0) { _map[array2[0].Trim()] = array2[1].Trim().ToLowerInvariant(); } } } public static string KeyFor(Character c) { string prefabName = Utils.GetPrefabName(((Component)c).gameObject); if (_map.TryGetValue(prefabName, out var value)) { return value; } string text = (c.m_name ?? "").ToLowerInvariant(); string text2 = prefabName.ToLowerInvariant(); if (text2.Contains("kall") || text2.Contains("fimbul") || text.Contains("kall") || text.Contains("fimbul")) { return "kall"; } return "generic"; } private static string Display(Character c) { string text = c.m_name; if (Localization.instance != null && text != null) { text = Localization.instance.Localize(text); } if (!string.IsNullOrEmpty(text) && !text.StartsWith("$")) { return text; } return Utils.GetPrefabName(((Component)c).gameObject); } private static Character ProtoOf(int hash) { if (_proto.TryGetValue(hash, out var value)) { return value; } GameObject prefab = ZNetScene.instance.GetPrefab(hash); value = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<Character>() : null); if (Object.op_Implicit((Object)(object)value) && !value.IsBoss()) { value = null; } _proto[hash] = value; return value; } public static void Tick() { //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) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_0088: 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_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_04d1: Unknown result type (might be due to invalid IL or missing references) //IL_04d6: Unknown result type (might be due to invalid IL or missing references) //IL_04dd: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_050d: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_04fd: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_0389: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Unknown result type (might be due to invalid IL or missing references) //IL_045d: Unknown result type (might be due to invalid IL or missing references) if (!NetPlugin.CfgBoss.Value) { _f.Clear(); return; } List<Vector3> list = Villages.PlayerPositions(); float time = Time.time; HashSet<ZDOID> seen = new HashSet<ZDOID>(); if (ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null) { return; } HashSet<Vector2s> hashSet = new HashSet<Vector2s>(); foreach (Vector3 item in list) { Vector2s zone = ZoneSystem.GetZone(item); for (int i = -2; i <= 2; i++) { for (int j = -2; j <= 2; j++) { hashSet.Add(new Vector2s(zone.x + i, zone.y + j)); } } } List<ZDO> list2 = new List<ZDO>(); foreach (Vector2s item2 in hashSet) { _zl.Clear(); ZDOMan.instance.FindSectorObjects(item2, ZNet.instance.GetSyncedSimulationDistance(), _zl, (List<ZDO>)null); list2.AddRange(_zl); } foreach (ZDO item3 in list2) { if (item3 == null || !item3.IsValid()) { continue; } ZDOID uid = item3.m_uid; if (seen.Contains(uid)) { continue; } Character val = ProtoOf(item3.GetPrefab()); if (!Object.op_Implicit((Object)(object)val)) { continue; } seen.Add(uid); float num = item3.GetFloat(ZDOVars.s_maxHealth, 0f); float num2 = item3.GetFloat(ZDOVars.s_health, num); float num3 = ((num > 0f) ? Mathf.Clamp01(num2 / num) : 1f); if (!_f.TryGetValue(uid, out var value)) { value = new Fight { Key = KeyFor(val), Name = Display(val), LastPct = num3 }; if (num3 < 0.75f) { value.Fired |= 1; } if (num3 < 0.5f) { value.Fired |= 2; } if (num3 < 0.25f) { value.Fired |= 4; } if (num3 < 0.98f) { value.Engaged = true; value.NextIdle = time + 20f; } _f[uid] = value; ZLog.Log((object)("[ValheimNet] boss seen: " + Utils.GetPrefabName(((Component)val).gameObject) + " -> " + value.Key + " (" + Mathf.RoundToInt(num3 * 100f) + "%)")); } value.Pos = item3.GetPosition(); float num4 = float.MaxValue; foreach (Vector3 item4 in list) { num4 = Mathf.Min(num4, Vector3.Distance(item4, value.Pos)); } if (num > 0f && num2 <= 0f) { Died(uid, value); continue; } if (!value.Engaged) { if (num4 < 40f) { value.Engaged = true; value.NextIdle = time + 20f; Taunt(uid, value, "spawn"); } } else { int num5 = ((num3 <= 0.25f) ? 4 : ((num3 <= 0.5f) ? 2 : ((num3 <= 0.75f) ? 1 : 0))); if (num5 != 0 && (value.Fired & num5) == 0) { value.Fired |= num5 | (num5 - 1); value.NextIdle = time + 15f; Taunt(uid, value, num5 switch { 2 => "hp50", 4 => "hp25", _ => "hp75", }); } else if (time >= value.NextIdle && num4 < 60f) { value.NextIdle = time + NetPlugin.CfgIdleTaunt.Value * (0.7f + (float)NetPlugin.Rng.NextDouble() * 0.6f); Taunt(uid, value, "idle"); } } value.LastPct = num3; } foreach (ZDOID item5 in _f.Keys.Where((ZDOID k) => !seen.Contains(k)).ToList()) { Fight fight = _f[item5]; if (fight.Engaged && fight.LastPct <= 0.15f) { Died(item5, fight); } else { _f.Remove(item5); } } } private static void Died(ZDOID id, Fight f) { //IL_0005: 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) _f.Remove(id); Taunt(id, f, "death"); Villages.OnBossKilled(f.Name); } private static string Line(string key, string ev, string player) { return NetPlugin.Lines.Pick(NetPlugin.Rng, "boss." + key + "." + ev, "player", player ?? "Viking") ?? NetPlugin.Lines.Pick(NetPlugin.Rng, "boss.generic." + ev, "player", player ?? "Viking"); } private static void Taunt(ZDOID id, Fight f, string ev, string player = null) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) string text = Line(f.Key, ev, player); if (text != null) { NetMsg.Say(1, id, f.Name, text, f.Pos, "boss:" + f.Key, f.Key); ZLog.Log((object)("[ValheimNet] " + f.Name + " (" + ev + "): " + text)); } } public static void OnBossDiedReport(Vector3 pos) { //IL_0002: 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_002b: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) Fight fight = null; ZDOID id = ZDOID.None; float num = 30f; foreach (KeyValuePair<ZDOID, Fight> item in _f) { float num2 = Vector3.Distance(item.Value.Pos, pos); if (num2 < num) { num = num2; fight = item.Value; id = item.Key; } } if (fight != null) { Died(id, fight); } } public static void OnPlayerDied(string player, Vector3 pos) { //IL_0002: 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_002b: 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_0058: 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_008b: Unknown result type (might be due to invalid IL or missing references) Fight fight = null; ZDOID id = ZDOID.None; float num = 100f; foreach (KeyValuePair<ZDOID, Fight> item in _f) { float num2 = Vector3.Distance(item.Value.Pos, pos); if (item.Value.Engaged && num2 < num) { num = num2; fight = item.Value; id = item.Key; } } if (fight != null) { fight.NextIdle = Time.time + 15f; Taunt(id, fight, "pdeath", player); } } public static bool OnPlayerChat(string player, Vector3 pos, string text) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) Fight f = null; ZDOID id = ZDOID.None; float num = 80f; foreach (KeyValuePair<ZDOID, Fight> item in _f) { float num2 = Vector3.Distance(item.Value.Pos, pos); if (item.Value.Engaged && num2 < num) { num = num2; f = item.Value; id = item.Key; } } if (f == null) { return false; } if (!NetPlugin.CfgBossLlm.Value || Time.time < f.NextReply) { return true; } f.NextReply = Time.time + NetPlugin.CfgBossReplyCd.Value; if (!Persona.TryGetValue(f.Key, out var value)) { value = Persona["generic"]; } string prompt = Prompts.Boss(f.Name, value, player, text.Replace("\"", "'")); if (!Llm.Ask("b:" + f.Key, prompt, 0.95f, delegate(string txt) { //IL_0038: 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_001a: Unknown result type (might be due to invalid IL or missing references) string text2 = TextUtil.Sanitize(txt, f.Name, 16, 120); if (text2 == null) { Taunt(id, f, "reply", player); } else { NetMsg.Say(1, id, f.Name, text2, f.Pos, "boss:" + f.Key, f.Key); ZLog.Log((object)("[ValheimNet] " + f.Name + " (reply to " + player + "): " + text2)); } }) && NetPlugin.Rng.NextDouble() < 0.5) { Taunt(id, f, "reply", player); } return true; } public static string Status() { if (_f.Count != 0) { return "bosses:\n" + string.Join("\n", _f.Values.Select((Fight f) => " " + f.Name + " [" + f.Key + "] " + Mathf.RoundToInt(f.LastPct * 100f) + "% engaged=" + f.Engaged).ToArray()); } return "bosses: none tracked"; } public static string Preview(string key, string ev) { return Line(key, ev, "Viking") ?? ("(no line for boss." + key + "." + ev + ")"); } } public static class Cmds { private class SpawnCmd : ConsoleCommand { public override string Name => "vv_spawn"; public override string Help => "<name|Prefab> [count=1, max 10] spawn creatures in front of you (server admin). Names: " + string.Join(", ", SpawnNames.Names); public override void Run(string[] a) { if (a.Length < 1) { Print("usage: vv_spawn <name|Prefab> [count] names: " + string.Join(", ", SpawnNames.Names)); return; } int result = 1; int num = a.Length; if (num > 1 && int.TryParse(a[num - 1], out result)) { num--; } else { result = 1; } string text = SpawnNames.Resolve(string.Join(" ", a, 0, num)); Print("vv_spawn: " + text + " x" + Mathf.Clamp(result, 1, 10)); ((Terminal)Console.instance).TryRunCommand("vv_mob " + text + " " + Mathf.Clamp(result, 1, 10), false, false); } } private class Fwd : ConsoleCommand { private readonly string _n; private readonly string _h; public override string Name => _n; public override string Help => _h; public Fwd(string n, string h) { _n = n; _h = h; } public override void Run(string[] args) { //IL_003c: 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) if (_n == "vn_status") { Print(MicIn.ClientStatus()); } Player localPlayer = Player.m_localPlayer; NetMsg.Up("cmd", Object.op_Implicit((Object)(object)localPlayer) ? ((Component)localPlayer).transform.position : Vector3.zero, Object.op_Implicit((Object)(object)localPlayer) ? localPlayer.GetPlayerName() : "?", _n + ((args.Length != 0) ? (" " + string.Join(" ", args)) : "")); } } private static readonly string[] AdminOnly = new string[5] { "vn_mood", "vn_reroll", "vn_taunt", "vn_llm", "vn_reload" }; private static readonly FieldInfo _adminList = AccessTools.Field(typeof(ZNet), "m_adminList"); private static readonly MethodInfo _contains = AccessTools.Method(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); public static void Init() { Add("vn_mtaunt", "preview a monster line: vn_mtaunt <family> <event>"); Add("vn_status", "ValheimNet: personalities, moods, voices, LLM + boss status"); Add("vn_chatter", "vn_chatter <Name> : make that villager say a line now"); Add("vn_mood", "[admin] vn_mood <Name> <content|happy|cranky|gloomy|tired|scared|restless|hurt|grieving> [why...]"); Add("vn_reroll", "[admin] vn_reroll <Name> : new random personality + voice"); Add("vn_taunt", "[admin] vn_taunt <eikthyr|elder|bonemass|moder|yagluth|queen|fader|kall> <spawn|hp75|hp50|hp25|pdeath|death|idle|reply> : show a canned taunt"); Add("vn_llm", "[admin] vn_llm <text> : test the desk LLM round trip from the server"); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SpawnCmd()); Add("vn_reload", "[admin] reload lines.txt and the config on the server"); } private static void Add(string n, string h) { CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new Fwd(n, h)); } public static void Print(string s) { string[] array = (s ?? "").Split(new char[1] { '\n' }); foreach (string text in array) { if (Object.op_Implicit((Object)(object)Console.instance)) { Console.instance.Print(text); } } Debug.Log((object)("[ValheimNet] " + s)); } public static bool IsAdmin(long sender) { if (sender == 0L || sender == ZNet.GetUID()) { return true; } try { ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer == null || peer.m_socket == null) { return false; } if (_adminList == null || _contains == null) { Debug.LogWarning((object)"[ValheimNet] admin check unavailable (game changed) - refusing"); return false; } return (bool)_contains.Invoke(ZNet.instance, new object[2] { _adminList.GetValue(ZNet.instance), peer.m_socket.GetHostName() }); } catch (Exception ex) { Debug.LogWarning((object)("[ValheimNet] admin check failed: " + ex.Message)); return false; } } private static VillagerBrain Find(string name) { return ((IEnumerable<VillagerBrain>)VillagersApi.AllVillagers()).FirstOrDefault((Func<VillagerBrain, bool>)((VillagerBrain v) => Object.op_Implicit((Object)(object)v) && string.Equals(Villages.Name(v), name, StringComparison.OrdinalIgnoreCase))); } public static string RunServer(string line, string by, long sender) { string[] array = (line ?? "").Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return "?"; } if (Array.IndexOf(AdminOnly, array[0]) >= 0 && !IsAdmin(sender)) { return array[0] + ": admin only (add your ID to the server's adminlist.txt)"; } try { string text = array[0]; if (text != null) { switch (text.Length) { case 9: switch (text[7]) { case 'u': { if (!(text == "vn_status")) { break; } StringBuilder stringBuilder = new StringBuilder("ValheimNet 0.6.3 | day " + Villages.Today + " | LLM " + ((!Llm.Enabled) ? "off" : (Llm.Available ? "up" : "backing off")) + " ok=" + Llm.Ok + " fail=" + Llm.Failed + " limited=" + Llm.Limited + ((Llm.LastError.Length > 0) ? (" last error: " + Llm.LastError) : "") + "\n"); foreach (VillagerBrain item in (from x in VillagersApi.AllVillagers() where Object.op_Implicit((Object)(object)x) && Object.op_Implicit((Object)(object)x.NView) && x.NView.IsValid() select x).OrderBy(Villages.Name)) { string text3 = Villages.Why(item); stringBuilder.Append(" " + Villages.Name(item) + ": " + Villages.Pers(item) + ", mood " + Villages.Mood(item) + ((text3.Length > 0) ? (" (" + text3 + ")") : "") + ", voice " + Villages.Voice(item) + ", " + ((object)Unsafe.As<State, State>(ref item.St)/*cast due to .constrained prefix*/).ToString() + "\n"); } stringBuilder.Append(Bosses.Status()).Append("\n").Append(Trophies.Status()) .Append("\n") .Append(Monsters.Status()) .Append("\n") .Append(MicIn.ServerStatus()); return stringBuilder.ToString(); } case 'l': { if (!(text == "vn_reroll")) { break; } if (array.Length < 2) { return "usage: vn_reroll <Name>"; } VillagerBrain val = Find(array[1]); if (!Object.op_Implicit((Object)(object)val)) { return array[1] + " is not loaded"; } Villages.Reroll(val); return Villages.Name(val) + ": " + Villages.Pers(val) + ", voice " + Villages.Voice(val); } case 'n': if (!(text == "vn_mtaunt")) { break; } if (array.Length < 3) { return "usage: vn_mtaunt <family> <event> families: " + string.Join(", ", Monsters.Families) + " events: " + string.Join(", ", Monsters.Events); } return "[" + array[1] + " " + array[2] + "] " + Monsters.Preview(array[1].ToLowerInvariant(), array[2].ToLowerInvariant()); case 'a': if (!(text == "vn_reload")) { break; } ((BaseUnityPlugin)NetPlugin.I).Config.Reload(); NetPlugin.ApplyConfig(); NetPlugin.LoadLines(); Llm.DownUntil = DateTime.MinValue; return "reloaded: " + NetPlugin.Lines.Count + " lines"; } break; case 10: { if (!(text == "vn_chatter")) { break; } if (array.Length < 2) { return "usage: vn_chatter <Name>"; } VillagerBrain val3 = Find(array[1]); if (!Object.op_Implicit((Object)(object)val3)) { return array[1] + " is not loaded"; } Villages.Speak(val3, "Say something about your day.", null, priority: true); return "asked " + Villages.Name(val3) + " to speak"; } case 7: { if (!(text == "vn_mood")) { break; } if (array.Length < 3) { return "usage: vn_mood <Name> <mood> [why]"; } VillagerBrain val2 = Find(array[1]); if (!Object.op_Implicit((Object)(object)val2)) { return array[1] + " is not loaded"; } string text4 = array[2].ToLowerInvariant(); if (!Moods.IsValid(text4)) { return "moods: " + string.Join(", ", Moods.Keys); } string text5 = ((array.Length > 3) ? string.Join(" ", array, 3, array.Length - 3) : ("set by " + by)); return Villages.SetMood(val2, text4, text5, force: true) ? (Villages.Name(val2) + " is now " + text4 + " (" + text5 + ")") : "could not set (villager not owned by the server?)"; } case 8: { if (!(text == "vn_taunt")) { break; } if (array.Length < 3) { return "usage: vn_taunt <boss> <event> bosses: " + string.Join(", ", Bosses.KnownKeys) + " events: " + string.Join(", ", Bosses.Events); } string text6 = Bosses.Preview(array[1].ToLowerInvariant(), array[2].ToLowerInvariant()); return "[" + array[1] + " " + array[2] + "] " + text6; } case 6: { if (!(text == "vn_llm")) { break; } string text2 = ((array.Length > 1) ? string.Join(" ", array, 1, array.Length - 1) : "Say hello like a Viking villager."); DateTime t0 = DateTime.UtcNow; return Llm.Ask("test", text2 + "\nReply in one short line.", 0.7f, delegate(string txt) { NetMsg.Reply(sender, "[vn_llm] " + (DateTime.UtcNow - t0).TotalMilliseconds.ToString("0") + " ms: " + (txt ?? ("(failed: " + Llm.LastError + ")"))); }) ? ("sent to " + Llm.Url + " - answer follows in a few seconds") : "not sent: LLM off, backing off after an error, or rate limited"; } } } } catch (Exception ex) { return array[0] + " error: " + ex.Message; } return "unknown command"; } } public static class HelperSetup { public static ConfigEntry<bool> CfgEnabled; public static ConfigEntry<bool> CfgStopOnExit; private static bool _started; private static int _startedPid = -1; private static string PluginDir => Path.Combine(Path.GetDirectoryName(typeof(HelperSetup).Assembly.Location) ?? "", "vh"); private static string CacheDir => Path.Combine(Path.Combine(Paths.CachePath, "ValheimNet"), "vh"); public static string Dir { get { try { if (Has(CacheDir)) { return CacheDir; } if (Has(PluginDir)) { return PluginDir; } } catch { } return CacheDir; } } private static string Pyw => Path.Combine(Path.Combine(Dir, "py"), "pythonw.exe"); private static bool Windows => (int)Application.platform == 2; private static bool Bundled => Has(Dir); private static int Port { get { try { Uri uri = new Uri(Voice.CfgHelper.Value); return (uri.Port > 0) ? uri.Port : 5014; } catch { return 5014; } } } public static void Bind(ConfigFile c) { CfgEnabled = c.Bind<bool>("VoiceHelper", "Enabled", true, "Client: start an already-installed local voice helper (BepInEx/cache/ValheimNet/vh) hidden when the game starts. Not included in the package; voices work through the server without it."); CfgStopOnExit = c.Bind<bool>("VoiceHelper", "StopOnExit", true, "Client: stop the helper when the game closes (only a copy this mod started)."); } private static bool Has(string d) { if (File.Exists(Path.Combine(Path.Combine(d, "py"), "pythonw.exe"))) { return File.Exists(Path.Combine(d, "vn_tts_helper.py")); } return false; } private static bool Running() { string text = ((Voice.CfgHelper == null) ? "" : (Voice.CfgHelper.Value ?? "")); if (text.Length == 0) { return false; } try { return Encoding.UTF8.GetString(Voice.Http(text.TrimEnd(new char[1] { '/' }) + "/health", null, null, 1500)).Contains("\"ok\""); } catch { return false; } } public static void Init() { if (MenuBadge.Headless || !Windows || CfgEnabled == null || !CfgEnabled.Value || Voice.CfgHelper == null || string.IsNullOrEmpty(Voice.CfgHelper.Value)) { return; } ThreadPool.QueueUserWorkItem(delegate { try { if (Running()) { Debug.Log((object)"[ValheimNet] voice helper already running, using it"); } else if (!Bundled) { Debug.LogWarning((object)"[ValheimNet] no local voice helper (optional) - voices come finished from the server"); } else { StartHidden(); } } catch (Exception ex) { Debug.LogWarning((object)("[ValheimNet] voice helper check: " + ex.Message)); } }); } private static void StartHidden() { try { string text = Path.Combine(Dir, "ffmpeg.exe"); string arguments = "\"" + Path.Combine(Dir, "vn_tts_helper.py") + "\" --port " + Port + (File.Exists(text) ? (" --ffmpeg \"" + text + "\"") : ""); ProcessStartInfo processStartInfo = new ProcessStartInfo(Pyw, arguments); processStartInfo.UseShellExecute = false; processStartInfo.CreateNoWindow = true; processStartInfo.WorkingDirectory = Dir; processStartInfo.WindowStyle = ProcessWindowStyle.Hidden; processStartInfo.EnvironmentVariables["PYTHONDONTWRITEBYTECODE"] = "1"; Process process = Process.Start(processStartInfo); if (process != null) { _startedPid = process.Id; _started = true; } Debug.Log((object)("[ValheimNet] voice helper started (hidden) from " + Dir)); } catch (Exception ex) { Debug.LogWarning((object)("[ValheimNet] voice helper start failed, using built-in voices: " + ex.Message)); } } public static void Pump() { } public static void OnGUI() { } public static void Shutdown() { if (!_started || CfgStopOnExit == null || !CfgStopOnExit.Value) { return; } try { if (_startedPid > 0) { try { Process processById = Process.GetProcessById(_startedPid); if (!processById.HasExited) { processById.Kill(); } } catch { } } string[] array = new string[2] { "pythonw", "ffmpeg" }; for (int i = 0; i < array.Length; i++) { Process[] processesByName = Process.GetProcessesByName(array[i]); foreach (Process process in processesByName) { try { if (process.MainModule.FileName.StartsWith(Dir, StringComparison.OrdinalIgnoreCase)) { process.Kill(); } } catch { } } } } catch { } } } public static class Llm { public static string Url = "http://127.0.0.1:5001/v1/chat/completions"; public static volatile bool Enabled = true; public static int TimeoutMs = 8000; public static int MaxTokens = 48; public static readonly RateLimiter Limit = new RateLimiter(4); private static readonly Queue<Action> _results = new Queue<Action>(); private static readonly object _rl = new object(); private static readonly HashSet<string> _inFlight = new HashSet<string>(); public static int Ok; public static int Failed; public static int Limited; public static volatile string LastError = ""; private static long _downUntilTicks; public static DateTime DownUntil { get { return new DateTime(Interlocked.Read(in _downUntilTicks), DateTimeKind.Utc); } set { Interlocked.Exchange(ref _downUntilTicks, value.Ticks); } } public static bool Available { get { if (Enabled) { return DateTime.UtcNow >= DownUntil; } return false; } } public static void Pump() { while (true) { Action action; lock (_rl) { if (_results.Count == 0) { break; } action = _results.Dequeue(); } try { action(); } catch (Exception ex) { Debug.LogError((object)("[ValheimNet] " + ex)); } } } public static bool Ask(string key, string prompt, float temperature, Action<string> onDone) { if (!Available) { return false; } lock (_inFlight) { if (_inFlight.Contains(key)) { return false; } } if (!Limit.TryTake(DateTime.UtcNow)) { Interlocked.Increment(ref Limited); return false; } lock (_inFlight) { if (!_inFlight.Add(key)) { return false; } } string body = "{\"messages\":[{\"role\":\"user\",\"content\":" + TextUtil.Json(prompt) + "}],\"max_tokens\":" + MaxTokens + ",\"temperature\":" + temperature.ToString("0.00", CultureInfo.InvariantCulture) + ",\"stop\":[\"\\n\"]}"; string url = Url; int timeout = TimeoutMs; DateTime started = DateTime.UtcNow; ThreadPool.QueueUserWorkItem(delegate { string text = null; try { text = TextUtil.JsonString(Post(url, body, timeout), "content"); Interlocked.Increment(ref Ok); } catch (Exception ex) { Interlocked.Increment(ref Failed); LastError = ex.Message; DownUntil = DateTime.UtcNow.AddSeconds(60.0); Debug.LogWarning((object)("[ValheimNet] LLM request failed after " + (DateTime.UtcNow - started).TotalMilliseconds.ToString("0") + " ms: " + ex.Message + " - canned lines for 60 s")); } finally { lock (_inFlight) { _inFlight.Remove(key); } } lock (_rl) { _results.Enqueue(delegate { onDone(text); }); } }); return true; } private static string Post(string url, string json, int timeout) { HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url); httpWebRequest.Method = "POST"; httpWebRequest.ContentType = "application/json"; httpWebRequest.Timeout = timeout; httpWebRequest.ReadWriteTimeout = timeout; httpWebRequest.Proxy = null; byte[] bytes = Encoding.UTF8.GetBytes(json); httpWebRequest.ContentLength = bytes.Length; using (Stream stream = httpWebRequest.GetRequestStream()) { stream.Write(bytes, 0, bytes.Length); } using WebResponse webResponse = httpWebRequest.GetResponse(); using StreamReader streamReader = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8); return streamReader.ReadToEnd(); } } public sealed class Personality { public string Key; public string Style; public float Pitch; public float Speed; public string[] MoodBias; public Personality(string k, string style, float pitch, float speed, params string[] bias) { Key = k; Style = style; Pitch = pitch; Speed = speed; MoodBias = bias; } } public static class Personas { public static readonly Personality[] All = new Personality[10] { new Personality("cheerful", "upbeat, warm, finds the bright side, says 'Skål!' a lot", 1.05f, 1.06f, "happy", "happy", "content"), new Personality("grumpy", "blunt, complains about everything, secretly loyal", 0.93f, 0.94f, "cranky", "gloomy", "content"), new Personality("lazy", "yawns, looks for excuses to rest, slow and drawling", 0.97f, 0.88f, "tired", "content", "content"), new Personality("proud", "boastful, brags about strength and deeds, compares self to heroes of the sagas", 0.98f, 1f, "happy", "content", "restless"), new Personality("nervous", "jumpy, worries about trolls and the dark, stammers a little", 1.06f, 1.1f, "scared", "content", "gloomy"), new Personality("brave", "bold, eager for a fight, scoffs at danger", 0.95f, 1.02f, "restless", "happy", "content"), new Personality("gossip", "nosy, chatty, loves rumours about the other villagers and the players", 1.04f, 1.08f, "happy", "content", "restless"), new Personality("pious", "devout, invokes Odin, Thor and the norns, sees omens everywhere", 0.99f, 0.95f, "content", "content", "gloomy"), new Personality("sarcastic", "dry, deadpan, mocking but not cruel", 0.98f, 0.98f, "content", "cranky", "content"), new Personality("dreamy", "distracted, poetic, talks about the sky, the sea and odd dreams", 1.02f, 0.92f, "content", "happy", "gloomy") }; public static readonly string[] FemaleVoices = new string[13] { "af_heart", "af_bella", "af_sarah", "af_sky", "af_aoede", "af_jessica", "af_kore", "af_nova", "af_river", "af_alloy", "bf_alice", "bf_isabella", "bf_lily" }; public static readonly string[] MaleVoices = new string[7] { "am_michael", "am_liam", "am_eric", "am_echo", "am_puck", "bm_fable", "am_santa" }; public static Personality Get(string key) { Personality[] all = All; foreach (Personality personality in all) { if (personality.Key == key) { return personality; } } return All[0]; } public static bool IsValid(string key) { Personality[] all = All; for (int i = 0; i < all.Length; i++) { if (all[i].Key == key) { return true; } } return false; } public static string RollVoice(Random r, bool female, string pers, ICollection<string> taken) { string[] array = (female ? FemaleVoices : MaleVoices); List<string> list = new List<string>(); string[] array2 = array; foreach (string text in array2) { bool flag = false; foreach (string item in taken) { if (item != null && item.StartsWith("kokoro:" + text + "@")) { flag = true; break; } } if (!flag) { list.Add(text); } } if (list.Count == 0) { list.AddRange(array); } Personality personality = Get(pers); float num = Clamp(personality.Pitch + (float)(r.NextDouble() * 0.12 - 0.06), 0.85f, 1.15f); float num2 = Clamp(personality.Speed + (float)(r.NextDouble() * 0.08 - 0.04), 0.82f, 1.18f); return "kokoro:" + list[r.Next(list.Count)] + "@p" + num.ToString("0.00", CultureInfo.InvariantCulture) + ",s" + num2.ToString("0.00", CultureInfo.InvariantCulture); } private static float Clamp(float v, float a, float b) { if (!(v < a)) { if (!(v > b)) { return v; } return b; } return a; } } public static class Moods { public static readonly string[] Keys = new string[9] { "content", "happy", "cranky", "gloomy", "tired", "scared", "restless", "hurt", "grieving" }; public static string Describe(string m) { return m switch { "happy" => "in a good mood", "cranky" => "cranky and short-tempered", "gloomy" => "gloomy", "tired" => "tired", "scared" => "on edge", "restless" => "restless, wants adventure", "hurt" => "hurt and sore", "grieving" => "sad, grieving", _ => "content", }; } public static int Rank(string m) { return m switch { "grieving" => 5, "hurt" => 4, "cranky" => 3, "scared" => 2, _ => 1, }; } public static bool IsValid(string m) { return Array.IndexOf(Keys, m) >= 0; } public static string Roll(Random r, string pers, bool pendingCranky, out string why) { if (pendingCranky) { why = "my sleep was interrupted last night"; return "cranky"; } Personality personality = Personas.Get(pers); why = ""; if (r.NextDouble() < 0.5) { string text = personality.MoodBias[r.Next(personality.MoodBias.Length)]; why = ((text == "content") ? "" : "woke up that way"); return text; } double num = r.NextDouble(); if (num < 0.45) { return "content"; } if (num < 0.65) { why = "slept well"; return "happy"; } if (num < 0.75) { why = "bad dreams"; return "gloomy"; } if (num < 0.85) { why = "didn't sleep enough"; return "tired"; } if (num < 0.93) { why = "itching for something to happen"; return "restless"; } why = "got up on the wrong side of the bed"; return "cranky"; } } public static class TextUtil { public static string Sanitize(string s, string speakerName, int maxWords, int maxChars) { if (s == null) { return null; } int num = s.IndexOf("</think>", StringComparison.OrdinalIgnoreCase); if (num >= 0) { s = s.Substring(num + 8); } s = s.Replace("\r", "\n").Trim(); string[] array = s.Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0) { s = text; break; } } if (!string.IsNullOrEmpty(speakerName) && s.StartsWith(speakerName + ":", StringComparison.OrdinalIgnoreCase)) { s = s.Substring(speakerName.Length + 1).Trim(); } s = StripBetween(s, '*', '*'); s = StripBetween(s, '(', ')'); s = StripBetween(s, '[', ']'); s = s.Replace("<", "").Replace(">", ""); s = s.Trim().Trim('"', '\'', '“', '”', ' ').Trim(); if (s.Length == 0) { return null; } string[] array2 = s.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array2.Length > maxWords) { s = string.Join(" ", array2, 0, maxWords); s = CutToSentence(s) ?? (s.TrimEnd(',', ';', ':', '-') + "..."); } if (s.Length > maxChars) { s = s.Substring(0, maxChars); s = CutToSentence(s) ?? (s.TrimEnd(',', ';', ':', '-', ' ') + "..."); } if (s.Length != 0) { return s; } return null; } private static string CutToSentence(string s) { int num = Math.Max(s.LastIndexOf('.'), Math.Max(s.LastIndexOf('!'), s.LastIndexOf('?'))); if (num < 8) { return null; } return s.Substring(0, num + 1); } private static string StripBetween(string s, char a, char b) { StringBuilder stringBuilder = new StringBuilder(); int num = 0; foreach (char c in s) { if (a == b) { if (c == a) { num = ((num == 0) ? 1 : 0); continue; } } else { if (c == a) { num++; continue; } if (c == b && num > 0) { num--; continue; } } if (num == 0) { stringBuilder.Append(c); } } string text = stringBuilder.ToString(); while (text.Contains(" ")) { text = text.Replace(" ", " "); } if (text.Trim().Length != 0) { return text; } return s; } public static string Json(string s) { StringBuilder stringBuilder = new StringBuilder("\""); string text = s ?? ""; foreach (char c in text) { switch (c) { case '\\': stringBuilder.Append("\\\\"); continue; case '"': stringBuilder.Append("\\\""); continue; case '\n': stringBuilder.Append("\\n"); continue; case '\t': stringBuilder.Append("\\t"); continue; case '\r': continue; } if (c < ' ') { StringBuilder stringBuilder2 = stringBuilder.Append("\\u"); int num = c; stringBuilder2.Append(num.ToString("x4")); } else { stringBuilder.Append(c); } } return stringBuilder.Append('"').ToString(); } public static string JsonString(string doc, string key) { if (doc == null) { return null; } string text = "\"" + key + "\""; for (int num = doc.IndexOf(text, StringComparison.Ordinal); num >= 0; num = doc.IndexOf(text, num + 1, StringComparison.Ordinal)) { int i; for (i = num + text.Length; i < doc.Length && char.IsWhiteSpace(doc[i]); i++) { } if (i < doc.Length && doc[i] == ':') { for (i++; i < doc.Length && char.IsWhiteSpace(doc[i]); i++) { } if (i < doc.Length && doc[i] == '"') { StringBuilder stringBuilder = new StringBuilder(); for (i++; i < doc.Length; i++) { char c = doc[i]; switch (c) { case '"': return stringBuilder.ToString(); case '\\': { if (i + 1 >= doc.Length) { break; } char c2 = doc[++i]; switch (c2) { case 'n': stringBuilder.Append('\n'); break; case 't': stringBuilder.Append('\t'); break; case 'u': if (i + 4 < doc.Length) { if (int.TryParse(doc.Substring(i + 1, 4), NumberStyles.HexNumber, null, out var result)) { stringBuilder.Append((char)result); } i += 4; } break; default: stringBuilder.Append(c2); break; case 'b': case 'f': case 'r': break; } continue; } } stringBuilder.Append(c); } return null; } } } return null; } } public sealed class RateLimiter { private readonly Queue<DateTime> _q = new Queue<DateTime>(); private readonly object _l = new object(); public int PerMinute; public RateLimiter(int perMinute) { PerMinute = perMinute; } public bool TryTake(DateTime now) { lock (_l) { while (_q.Count > 0 && (now - _q.Peek()).TotalSeconds >= 60.0) { _q.Dequeue(); } if (_q.Count >= PerMinute) { return false; } _q.Enqueue(now); return true; } } } public sealed class LineBank { private readonly Dictionary<string, List<string>> _b = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, int> _last = new Dictionary<string, int>(); public int Count { get { int num = 0; foreach (KeyValuePair<string, List<string>> item in _b) { num += item.Value.Count; } return num; } } public void Load(string text) { _b.Clear(); string[] array = text.Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2[0] == '#') { continue; } int num = text2.IndexOf('|'); if (num <= 0 || num == text2.Length - 1) { continue; } string key = text2.Substring(0, num).Trim(); string text3 = text2.Substring(num + 1).Trim(); if (text3.Length != 0) { if (!_b.TryGetValue(key, out var value)) { value = new List<string>(); _b[key] = value; } value.Add(text3); } } } public bool Has(string bank) { return _b.ContainsKey(bank); } public void Merge(LineBank over) { foreach (KeyValuePair<string, List<string>> item in over._b) { _b[item.Key] = new List<string>(item.Value); } _last.Clear(); } public string Pick(Random r, string bank, params string[] vars) { if (!_b.TryGetValue(bank, out var value) || value.Count == 0) { return null; } _last.TryGetValue(bank, out var value2); int num = r.Next(value.Count); if (value.Count > 1 && num == value2 - 1) { num = (num + 1) % value.Count; } _last[bank] = num + 1; string text = value[num]; for (int i = 0; i + 1 < vars.Length; i += 2) { text = text.Replace("{" + vars[i] + "}", vars[i + 1] ?? ""); } return text; } } public static class Prompts { public static string Villager(string name, string pers, string mood, string why, string task, string timeOfDay, string situation, string recent) { Personality personality = Personas.Get(pers); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("You are ").Append(name).Append(", a villager in a small Viking settlement in Valheim. "); stringBuilder.Append("Personality: ").Append(personality.Key).Append(" - ") .Append(personality.Style) .Append(". "); stringBuilder.Append("Today you are ").Append(Moods.Describe(mood)); if (!string.IsNullOrEmpty(why)) { stringBuilder.Append(" (").Append(why).Append(")"); } stringBuilder.Append(". "); stringBuilder.Append("Right now you are ").Append(task).Append(". It is ") .Append(timeOfDay) .Append(". "); if (!string.IsNullOrEmpty(recent)) { stringBuilder.Append("Do not repeat these recent lines: ").Append(recent).Append(" "); } stringBuilder.Append("Reply with ONE short spoken line, at most 16 words, in character, no narration, no quotes, no emojis."); return stringBuilder.ToString() + "\n\n" + situation; } public static string Trophy(string bossName, string persona, string situation) { return "You are the severed, mounted head of " + bossName + ", a boss from the game Valheim, hung up as a trophy on the Sacrificial Stones (a ring of standing stones) by the Vikings who killed you. Persona: " + persona + " You are bitter, sarcastic and still arrogant; you trash-talk players and the other mounted heads. Never say slab, shelf, pedestal or wall; you hang on a standing stone. Reply with ONE line of at most 14 words, in character, no narration, no quotes, no emojis.\n\n" + situation; } public static string Monster(string persona, string situation, bool profane = false) { string text = (profane ? " Be crude and vulgar: mix your lore and grudges with ordinary human insults and swearing (fuck, shit, bastard, ass, piss) - you know how Vikings curse. Never use slurs or hate speech about race, religion, sexuality, gender or disability." : " Mix your lore and grudges with ordinary insults, but keep it clean: no swearing."); return "You are " + persona + ", a monster in the game Valheim, fighting Vikings. Trash-talk them in your own voice, using your lore." + text + " Reply with ONE line of at most 12 words, in character, no narration, no quotes, no emojis.\n\n" + situation; } public static string Boss(string bossName, string persona, string playerName, string playerText) { return "You are " + bossName + ", a boss in the game Valheim, mid-fight against Vikings. Persona: " + persona + " You are arrogant and hostile, never helpful, never give hints. Reply to what the player just said with ONE taunt of at most 14 words, in character, no narration, no quotes, no emojis.\n\n" + playerName + " says: " + playerText; } } public static class MicText { private static readonly string[] Junk = new string[18] { "thank you", "thanks for watching", "thank you for watching", "thanks", "you", "bye", "okay", "ok", "so", "uh", "um", "hmm", "mm", "ah", "oh", "yeah", "subtitles by the amara.org community", "please subscribe" }; public static string Clean(string t) { if (string.IsNullOrEmpty(t)) { return ""; } StringBuilder stringBuilder = new StringBuilder(); int num = 0; bool flag = false; foreach (char c in t) { switch (c) { case '(': case '[': num++; break; case ')': case ']': if (num > 0) { num--; } break; case '*': flag = !flag; break; default: if (num == 0 && !flag) { stringBuilder.Append(c); } break; } } string text = Regex.Replace(stringBuilder.ToString(), "\\s+", " ").Trim(); text = text.Replace("♪", "").Trim(); if (text.Length <= 300) { return text; } return text.Substring(0, 300); } public static string[] Words(string s) { return (from w in Regex.Split((s ?? "").ToLowerInvariant(), "[^a-z0-9']+") where w.Length > 0 select w).ToArray(); } public static string Accept(string raw, IEnumerable<string> heard, int minWords, int minChars, out string why) { string text = Clean(raw); string[] array = Words(text); why = ""; if (text.Length < minChars || array.Length < minWords) { why = "too short"; return null; } string value = string.Join(" ", array); if (Junk.Contains(value)) { why = "whisper filler"; return null; } if (array.Distinct().Count() <= 1 && array.Length > 2) { why = "repetition"; return null; } if (heard != null) { foreach (string item in heard) { if (EchoOf(array, Words(item))) { why = "echo of our own line"; return null; } } } return text; } public static bool EchoOf(string[] said, string[] line) { if (said.Length == 0 || line.Length == 0) { return false; } HashSet<string> hashSet = new HashSet<string>(line); int num = said.Count(hashSet.Contains); if (num >= Math.Min(3, said.Length)) { return (double)num >= 0.6 * (double)said.Length; } return false; } public static double JsonNum(string doc, string key, double dflt) { Match match = Regex.Match(doc ?? "", "\"" + key + "\"\\s*:\\s*(-?[0-9.eE+-]+)"); if (!match.Success || !double.TryParse(match.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return dflt; } return result; } public static List<string> Segments(string doc) { List<string> list = new List<string>(); if (string.IsNullOrEmpty(doc)) { return list; } int num = doc.IndexOf("{\"t0\"", StringComparison.Ordinal); while (num >= 0) { int num2 = doc.IndexOf("{\"t0\"", num + 1, StringComparison.Ordinal); list.Add((num2 < 0) ? doc.Substring(num) : doc.Substring(num, num2 - num)); num = num2; } return list; } public static bool OverlapsPlayback(double t0, double t1, IEnumerable<double[]> windows, double tail) { foreach (double[] window in windows) { if (t0 < window[1] + tail && t1 > window[0]) { return true; } } return false; } } public static class MonsterRules { private static readonly List<KeyValuePair<string, string>> _map = new List<KeyValuePair<string, string>>(); private static readonly string[] _bad = new string[9] { "as an ai", "language model", "assistant", "valheim", "in this game", "roleplay", "slab", "shelf", "pedestal" }; private static readonly string[] _slur = new string[7] { "nigg", "faggot", "retard", "tranny", "wetback", "raghead", "towelhead" }; private static readonly string[] _slurExact = new string[12] { "fag", "fags", "kike", "kikes", "spic", "spics", "chink", "chinks", "gook", "gooks", "dyke", "dykes" }; private static readonly string[] _swear = new string[15] { "fuck", "shit", "bastard", "bitch", "piss", "cunt", "cock", "dick", "asshole", "damn", "crap", "arse", "bollock", "twat", "wank" }; public static void ParseMap(string s) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); string[] array = (s ?? "").Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { '=' }); if (array2.Length == 2 && array2[0].Trim().Length > 0) { dictionary[array2[0].Trim()] = array2[1].Trim().ToLowerInvariant(); } } lock (_map) { _map.Clear(); foreach (KeyValuePair<string, string> item in dictionary) { _map.Add(new KeyValuePair<string, string>(item.Key, item.Value)); } _map.Sort((KeyValuePair<string, string> a, KeyValuePair<string, string> b) => b.Key.Length.CompareTo(a.Key.Length)); } } public static string FamilyOf(string prefab) { if (string.IsNullOrEmpty(prefab)) { return null; } lock (_map) { foreach (KeyValuePair<string, string> item in _map) { if (prefab.StartsWith(item.Key, StringComparison.OrdinalIgnoreCase)) { return (item.Value == "-") ? null : item.Value; } } } return null; } private static bool HasStem(string l, string[] stems) { string[] array = Regex.Split(l, "[^a-z]+"); foreach (string text in array) { foreach (string value in stems) { if (text.StartsWith(value)) { return true; } } } return false; } public static bool IsProfane(string s) { if (!string.IsNullOrEmpty(s)) { return HasStem(s.ToLowerInvariant(), _swear); } return false; } public static string Clean(string s, bool profane = true) { if (string.IsNullOrEmpty(s)) { return null; } string text = s.ToLowerInvariant(); string[] bad = _bad; foreach (string value in bad) { if (text.Contains(value)) { return null; } } if (HasStem(text, _slur) || Regex.Split(text, "[^a-z]+").Any((string w) => Array.IndexOf(_slurExact, w) >= 0)) { return null; } if (!profane && HasStem(text, _swear)) { return null; } return s.Trim().Trim('"', '\'').Trim(); } public static string Quote(string said) { string text = (said ?? "").Replace("\"", "'").Replace("\n", " ").Trim(); if (text.Length <= 160) { return text; } return text.Substring(0, 160); } public static bool Allowed(float now, float next, bool important, IEnumerable<float> playerLast, float gap) { if (!important && now < next) { return false; } foreach (float item in playerLast) { if (now - item < (important ? (gap * 0.4f) : gap)) { return false; } } return true; } } public static class SpawnNames { private static readonly Dictionary<string, string> _m = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "greydwarf", "Greydwarf" }, { "brute", "Greydwarf_Elite" }, { "greydwarfbrute", "Greydwarf_Elite" }, { "shaman", "Greydwarf_Shaman" }, { "greydwarfshaman", "Greydwarf_Shaman" }, { "greyling", "Greyling" }, { "troll", "Troll" }, { "cavetroll", "Troll" }, { "frosttroll", "TrollFrost" }, { "skeleton", "Skeleton" }, { "archer", "Skeleton" }, { "rancid", "Skeleton_Poison" }, { "rancidremains", "Skeleton_Poison" }, { "draugr", "Draugr" }, { "draugrelite", "Draugr_Elite" }, { "draugrarcher", "Draugr_Ranged" }, { "goblin", "Goblin" }, { "fuling", "Goblin" }, { "berserker", "GoblinBrute" }, { "fulingberserker", "GoblinBrute" }, { "goblinbrute", "GoblinBrute" }, { "fulingshaman", "GoblinShaman" }, { "goblinshaman", "GoblinShaman" }, { "fulingarcher", "GoblinArcher" }, { "dverger", "Dverger" }, { "dvergr", "Dverger" }, { "dvergrmage", "DvergerMage" }, { "cultist", "Fenring_Cultist" }, { "seeker", "Seeker" }, { "seekersoldier", "SeekerBrute" }, { "charred", "Charred_Melee" }, { "charredarcher", "Charred_Archer" }, { "charredmage", "Charred_Mage" }, { "jotun", "JotunWarrior" }, { "jotunwitch", "JotunWitch" }, { "brenna", "Skeleton_Hildir" }, { "geirrhafa", "Fenring_Cultist_Hildir" }, { "zil", "GoblinShaman_Hildir" }, { "thungr", "GoblinBrute_Hildir" } }; public static IEnumerable<string> Names => _m.Keys.OrderBy<string, string>((string k) => k, StringComparer.Ordinal); public static string Resolve(string name) { string text = (name ?? "").Trim(); if (!_m.TryGetValue(text.Replace(" ", "").Replace("_", "").Replace("-", ""), out var value)) { return text; } return value; } } public static class MenuBadge { private sealed class Placer : MonoBehaviour { private FejdStartup _fs; private GameObject _go; private RectTransform _logo; private float _until; private float _next; private bool _logged; private bool _rectLogged; private string _how = ""; public void Init(FejdStartup fs) { _fs = fs; _until = Time.realtimeSinceStartup + 20f; } private void Update() { if (!Object.op_Implicit((Object)(object)_fs) || Time.realtimeSinceStartup < _next) { return; } _next = Time.realtimeSinceStartup + 0.5f; if (!Object.op_Implicit((Object)(object)_go)) { Build(); } if (!Object.op_Implicit((Object)(object)_go)) { return; } bool flag = Object.op_Implicit((Object)(object)_fs.m_mainMenu) && _fs.m_mainMenu.activeInHierarchy; if (_go.activeSelf != flag) { _go.SetActive(flag); } if (Time.realtimeSinceStartup < _until) { if (!Object.op_Implicit((Object)(object)_logo)) { _logo = FindLogo(_fs.m_mainMenu.transform.root); } if (flag) { Place(); } } } private void Build() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_004d: 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_005a: 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_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_00b7: 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_00e2: Expected O, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Expected O, but got Unknown //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) Transform transform = _fs.m_mainMenu.transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (Object.op_Implicit((Object)(object)val)) { _go = new GameObject("ValheimNetBadge", new Type[1] { typeof(RectTransform) }); RectTransform val2 = (RectTransform)_go.transform; ((Transform)val2).SetParent((Transform)(object)val, false); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(0.5f, 0.5f); val2.pivot = val3; Vector2 anchorMin = (val2.anchorMax = val3); val2.anchorMin = anchorMin; val2.sizeDelta = new Vector2(560f, 96f); ((Transform)val2).localEulerAngles = new Vector3(0f, 0f, 40f); TMP_FontAsset val5 = NorseFont(); ((RectTransform)((TMP_Text)Text(val2, "Title", "ValheimNet", 56f, val5, new Color(1f, 0.78f, 0.42f), glow: true)).transform).anchoredPosition = new Vector2(0f, 0f); ((RectTransform)((TMP_Text)Text(val2, "Version", "v0.6.3", 20f, val5, new Color(1f, 0.92f, 0.8f, 0.8f), glow: false)).transform).anchoredPosition = new Vector2(0f, -70f); Graphic[] componentsInChildren = _go.GetComponentsInChildren<Graphic>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].raycastTarget = false; } Place(); if (!_logged) { _logged = true; Debug.Log((object)("[ValheimNet] menu badge created (font " + (Object.op_Implicit((Object)(object)val5) ? ((Object)val5).name : "default") + ")")); } } } private static Rect ScreenRect(RectTransform r) { //IL_0010: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_008d: Unknown result type (might be due to invalid IL or missing references) Canvas componentInParent = ((Component)r).GetComponentInParent<Canvas>(); Camera obj = ((Object.op_Implicit((Object)(object)componentInParent) && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null); Vector3[] array = (Vector3[])(object)new Vector3[4]; r.GetWorldCorners(array); Vector2 val = RectTransformUtility.WorldToScreenPoint(obj, array[0]); Vector2 val2 = RectTransformUtility.WorldToScreenPoint(obj, array[2]); return Rect.MinMaxRect(Mathf.Min(val.x, val2.x), Mathf.Min(val.y, val2.y), Mathf.Max(val.x, val2.x), Mathf.Max(val.y, val2.y)); } private unsafe void Place() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Expected O, but got Unknown //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: 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) //IL_0204: 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) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) RectTransform val = (RectTransform)_go.transform; RectTransform val2 = (RectTransform)((Transform)val).parent; Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor((float)Screen.width * 0.5f + (float)Screen.height * 0.12f, (float)Screen.height * 0.63f); string text = "screen"; Rect val5; if (Object.op_Implicit((Object)(object)_logo)) { Rect val4 = ScreenRect(_logo); if (((Rect)(ref val4)).height > 1f && ((Rect)(ref val4)).height < (float)Screen.height * 0.9f && ((Rect)(ref val4)).width < (float)Screen.width * 0.95f) { ((Vector2)(ref val3))..ctor(((Rect)(ref val4)).xMin + ((Rect)(ref val4)).width * 0.78f, ((Rect)(ref val4)).yMin + ((Rect)(ref val4)).height * 0.52f); text = "logo"; } if (!_rectLogged) { _rectLogged = true; string[] obj = new string[8] { "[ValheimNet] menu logo '", ((Object)_logo).name, "' screen rect ", null, null, null, null, null }; val5 = val4; obj[3] = ((object)(*(Rect*)(&val5))/*cast due to .constrained prefix*/).ToString(); obj[4] = " on "; obj[5] = Screen.width.ToString(); obj[6] = "x"; obj[7] = Screen.height.ToString(); Debug.Log((object)string.Concat(obj)); } } if (Object.op_Implicit((Object)(object)_fs.m_menuList)) { val5 = ScreenRect((RectTransform)_fs.m_menuList.transform); float yMax = ((Rect)(ref val5)).yMax; float num = BadgeHalfExtent(val) + 8f; if (val3.y - num < yMax) { val3.y = yMax + num; text += "+lifted"; } } Canvas componentInParent = ((Component)val2).GetComponentInParent<Canvas>(); Camera val6 = ((Object.op_Implicit((Object)(object)componentInParent) && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null); Vector2 val7 = default(Vector2); if (RectTransformUtility.ScreenPointToLocalPointInRectangle(val2, val3, val6, ref val7)) { Vector2 val8 = val7; val5 = val2.rect; val.anchoredPosition = val8 - ((Rect)(ref val5)).center; } if (text != _how) { _how = text; string[] obj2 = new string[5] { "[ValheimNet] menu badge placed (", text, ") at ", null, null }; Vector2 val9 = val3; obj2[3] = ((object)(*(Vector2*)(&val9))/*cast due to .constrained prefix*/).ToString(); obj2[4] = (Object.op_Implicit((Object)(object)_logo) ? (" near '" + ((Object)_logo).name + "'") : ""); Debug.Log((object)string.Concat(obj2)); } } private static float BadgeHalfExtent(RectTransform rt) { //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) Rect val = ScreenRect(rt); return ((Rect)(ref val)).height * 0.5f; } private static TextMeshProUGUI Text(RectTransform parent, string name, string s, float size, TMP_FontAsset font, Color col, bool glow) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent((Transform)(object)parent, false); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(0.5f, 1f); val2.anchorMax = val3; val2.anchorMin = val3; val2.pivot = new Vector2(0.5f, 1f); val2.sizeDelta = new Vector2(560f, size * 1.3f); TextMeshProUGUI val4 = val.AddComponent<TextMeshProUGUI>(); if (Object.op_Implicit((Object)(object)font)) { ((TMP_Text)val4).font = font; } ((TMP_Text)val4).text = s; ((TMP_Text)val4).fontSize = size; ((TMP_Text)val4).alignment = (TextAlignmentOptions)514; ((Graphic)val4).color = col; ((TMP_Text)val4).textWrappingMode = (TextWrappingModes)0; if (glow && Object.op_Implicit((Object)(object)((TMP_Text)val4).fontSharedMaterial)) { Material val5 = new Material(((TMP_Text)val4).fontSharedMaterial); ((Object)val5).name = "ValheimNetGlow"; val5.EnableKeyword("UNDERLAY_ON"); if (val5.HasProperty(ShaderUtilities.ID_UnderlayColor)) { val5.SetColor(ShaderUtilities.ID_UnderlayColor, new Color(1f, 0.45f, 0.1f, 0.85f)); } if (val5.HasProperty(ShaderUtilities.ID_UnderlaySoftness)) { val5.SetFloat(ShaderUtilities.ID_UnderlaySoftness, 0.6f); } if (val5.HasProperty(ShaderUtilities.ID_UnderlayDilate)) { val5.SetFloat(ShaderUtilities.ID_UnderlayDilate, 0.5f); } if (val5.HasProperty(ShaderUtilities.ID_OutlineColor)) { val5.SetColor(ShaderUtilities.ID_OutlineColor, new Color(0.35f, 0.12f, 0.02f, 1f)); val5.SetFloat(ShaderUtilities.ID_OutlineWidth, 0.12f); } ((TMP_Text)val4).fontSharedMaterial = val5; } return val4; } } public static ConfigEntry<bool> CfgOn; public static bool Headless { get { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 if (!Application.isBatchMode) { return (int)SystemInfo.graphicsDeviceType == 4; } return true; } } public static void Bind(ConfigFile c) { CfgOn = c.Bind<bool>("Menu", "ShowBadge", true, "Client: show the ValheimNet title under the Valheim logo on the main menu."); } public static void OnMenu(FejdStartup fs) { try { if (!Headless && CfgOn != null && CfgOn.Value && Object.op_Implicit((Object)(object)fs) && Object.op_Implicit((Object)(object)fs.m_mainMenu) && !Object.op_Implicit((Object)(object)((Component)fs).GetComponent<Placer>())) { ((Component)fs).gameObject.AddComponent<Placer>().Init(fs); } } catch (Exception ex) { Debug.LogWarning((object)("[ValheimNet] menu badge: " + ex.Message)); } } private static TMP_FontAsset NorseFont() { TMP_FontAsset[] source = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); return ((IEnumerable<TMP_FontAsset>)source).FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset f) => Object.op_Implicit((Object)(object)f) && ((Object)f).name.IndexOf("norsebold", StringComparison.OrdinalIgnoreCase) >= 0)) ?? ((IEnumerable<TMP_FontAsset>)source).FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset f) => Object.op_Implicit((Object)(object)f) && ((Object)f).name.IndexOf("norse", StringComparison.OrdinalIgnoreCase) >= 0)); } private static RectTransform FindLogo(Transform root) { //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: 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) RectTransform result = null; float num = float.MaxValue; Graphic[] componentsInChildren = ((Component)root).GetComponentsInChildren<Graphic>(true); foreach (Graphic val in componentsInChildren) { if (!(val is Image) && !(val is RawImage)) { continue; } Image val2 = (Image)(object)((val is Image) ? val : null); string text = ((Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.sprite)) ? ((Object)val2.sprite).name : ""); string text2 = ((Object)val).name + "|" + text + "|" + (Object.op_Implicit((Object)(object)((Component)val).transform.parent) ? ((Object)((Component)val).transform.parent).name : ""); if ((text2.IndexOf("logo", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("deepnorth", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("deep_north", StringComparison.OrdinalIgnoreCase) >= 0) && ((Component)val).gameObject.activeInHierarchy) { Canvas canvas = val.canvas; Camera obj = ((Object.op_Implicit((Object)(object)canvas) && (int)canvas.renderMode != 0) ? canvas.worldCamera : null); Vector3[] array = (Vector3[])(object)new Vector3[4]; val.rectTransform.GetWorldCorners(array); float y = RectTransformUtility.WorldToScreenPoint(obj, array[0]).y; float y2 = RectTransformUtility.WorldToScreenPoint(obj, array[1]).y; if (!((y + y2) * 0.5f < (float)Screen.height * 0.45f) && y < num) { num = y; result = val.rectTransform; } } } return result; } } public static class MicIn { private class Wait { public ManualResetEvent ev = new ManualResetEvent(initialState: false); public string text; } private static class M { private static Type _mic; private static Type _clipT; private static bool _ok; private static bool _tried; public static bool Ok() { if (_tried) { return _ok; } _tried = true; try { _mic = Type.GetType("UnityEngine.Microphone, UnityEngine.AudioModule"); _clipT = Type.GetType("UnityEngine.AudioClip, UnityEngine.AudioModule"); _ok = _mic != null && _clipT != null; } catch { _ok = false; } if (!_ok) { Debug.LogWarning((object)"[ValheimNet] open mic: Unity microphone API not found - game mic off"); } return _ok; } public static string[] Devices() { return (string[])_mic.GetProperty("devices").GetValue(null, null); } public static void Caps(string d, out int min, out int max) { object[] array = new object[3] { d, 0, 0 }; _mic.GetMethod("GetDeviceCaps").Invoke(null, array); min = (int)array[1]; max = (int)array[2]; } public static object Start(string d, int rate) { return _mic.GetMethod("Start", new Type[4] { typeof(string), typeof(bool), typeof(int), typeof(int) }).Invoke(null, new object[4] { d, true, 10, rate }); } public static void End(string d) { if (_mic != null) { _mic.GetMethod("End", new Type[1] { typeof(string) }).Invoke(null, new object[1] { d }); } } public static int Pos(string d) { return (int)_mic.GetMethod("GetPosition", new Type[1] { typeof(string) }).Invoke(null, new object[1] { d }); } public static int Int(object clip, string prop) { return (int)_clipT.GetProperty(prop).GetValue(clip, null); } public static void GetData(object clip, float[] buf, int off) { _clipT.GetMethod("GetData", new Type[2] { typeof(float[]), typeof(int) }).Invoke(clip, new object[2] { buf, off }); } } [CompilerGenerated] private static class <>O { public static CoroutineHandler <0>__OnReq; public static CoroutineHandler <1>__Noop; public static CoroutineHandler <2>__OnData; public static ThreadStart <3>__Worker; } public static ConfigEntry<bool> CfgOn; public static ConfigEntry<bool> CfgStartOn; public static ConfigEntry<bool> CfgUseServer; public static ConfigEntry<bool> CfgServe; public static ConfigEntry<bool> CfgMuteDuring; public static ConfigEntry<KeyboardShortcut> CfgKey; public static ConfigEntry<string> CfgLocalWhisper; public static ConfigEntry<string> CfgServerWhisper; public static ConfigEntry<int> CfgMinWords; public static ConfigEntry<int> CfgMinChars; public static ConfigEntry<int> CfgPerMin; public static ConfigEntry<int> CfgServePerMin; public static ConfigEntry<float> CfgServerCooldown; public static ConfigEntry<float> CfgVadLevel; public static ConfigEntry<bool> CfgBuiltIn; public static ConfigEntry<string> CfgDevice; private static readonly MicVad _vad = new MicVad(); private static object _clip; private static string _dev; private static int _lastPos; private static int _devRate; private static double _phase; private static bool _builtinLive; private static float _noMicUntil; private static volatile bool _helperChecked; private static int _clipsInFlight; private static CustomRPC _req; private static CustomRPC _data; private static bool _started; private static volatile bool _want; private static volatile bool _helperMic; private static volatile string _helperUrl = ""; private static bool _live; private static float _assertAt; private static float _toggleMsgAt; private static readonly Queue<Action> _main = new Queue<Action>(); private static readonly List<double[]> _play = new List<double[]>(); private static readonly List<KeyValuePair<double, string>> _heard = new List<KeyValuePair<double, string>>(); private static readonly RateLimiter _send = new RateLimiter(6); private static readonly Dictionary<int, Wait> _pending = new Dictionary<int, Wait>(); private static int _seq; public static int CSent; public static int COk; public static int CEmpty; public static int CRefused; public static int CTimeout; public static int CNoServer; public static int CDropped; public static int SOk; public static int SFail; public static int SRate; public static int SOff; public static int SBad; private static readonly Dictionary<long, RateLimiter> _peerLimit = new Dictionary<long, RateLimiter>(); private static readonly Dictionary<string, float> _lastVoice = new Dictionary<string, float>(); public static volatile bool Sending; public static void Bind(ConfigFile c) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) CfgOn = c.Bind<bool>("OpenMic", "Enabled", true, "Client: open-mic voice input (always listening with voice activity detection, no push-to-talk). Uses a local helper if one runs, else the built-in game mic ([OpenMic] BuiltInMic). Off = never touches the mic."); CfgStartOn = c.Bind<bool>("OpenMic", "StartListening", true, "Client: mic is live when you load in. The hotkey toggles it."); CfgKey = c.Bind<KeyboardShortcut>("OpenMic", "ToggleKey", new KeyboardShortcut((KeyCode)277, Array.Empty<KeyCode>()), "Client: mic on/off hotkey."); CfgLocalWhisper = c.Bind<string>("OpenMic", "LocalWhisperUrl", "", "Client: KoboldCpp-style /api/extra/transcribe on THIS PC, used when the helper has no local Whisper. Empty = ask the server."); CfgUseServer = c.Bind<bool>("OpenMic", "UseServerWhisper", true, "Client: when neither the helper nor LocalWhisperUrl can transcribe, send the clip to the server (desk Whisper)."); CfgMinWords = c.Bind<int>("OpenMic", "MinWords", 2, "Client: ignore transcripts with fewer words (fragments, 'uh', 'yeah')."); CfgMinChars = c.Bind<int>("OpenMic", "MinChars", 6, "Client: ignore transcripts shorter than this."); CfgMuteDuring = c.Bind<bool>("OpenMic", "MuteDuringPlayback", false, "Mute the mic while our voice lines play and drop clips that overlap them. Only needed with speakers; with a headset it deafens the mic for most of a talkative boss fight. Text that repeats a line we just showed is always dropped."); CfgPerMin = c.Bind<int>("OpenMic", "MaxPerMinute", 6, "Client: max voice messages sent per minute."); CfgBuiltIn = c.Bind<bool>("OpenMic", "BuiltInMic", true, "Client: when no local helper answers, capture the mic inside the game (open mic with voice activity detection, no push-to-talk) and send short 16 kHz clips to the server for transcription. Off = open mic only through a local helper."); CfgDevice = c.Bind<string>("OpenMic", "BuiltInMicDevice", "", "Client: microphone name for the built-in mic (empty = system default). Names are listed in the log when the mic starts."); CfgVadLevel = c.Bind<float>("OpenMic", "BuiltInMicSensitivity", 0.012f, "Client: built-in mic speech level (RMS 0..1). Lower = picks up quieter speech (and more noise)."); CfgServerWhisper = c.Bind<string>("OpenMic", "ServerWhisperUrl", "http://127.0.0.1:5013/api/extra/transcribe", "Server: desk Whisper (KoboldCpp) used to transcribe client clips."); CfgServe = c.Bind<bool>("OpenMic", "ServeClients", true, "Server: transcribe clips for clients."); CfgServePerMin = c.Bind<int>("OpenMic", "ServePerMinutePerPlayer", 24, "Server: max clips transcribed per player per minute."); CfgServerCooldown = c.Bind<float>("OpenMic", "ReplyCooldownSeconds", 4f, "Server: min seconds between voice messages from one player that get answered."); } public static string ClientStatus() { return "open mic (this client): " + (_live ? "ON" : "OFF") + " via=" + (_helperMic ? "helper" : (_builtinLive ? "game mic" : "none")) + " sent=" + CSent + " ok=" + COk + " empty=" + CEmpty + " refused=" + CRefused + " timeout=" + CTimeout + " noserver=" + CNoServer + " dropped=" + CDropped; } public static string ServerStatus() { return "open mic relay (server): ok=" + SOk + " whisperfail=" + SFail + " ratelimited=" + SRate + " disabled=" + SOff + " badclip=" + SBad; } private static void Lost(string why, ref int counter) { Interlocked.Increment(ref counter); Debug.LogWarning((object)("[ValheimNet] mic: clip lost (" + why + ") - " + ClientStatus())); } private static double Now() { return (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; } public static void Init() { //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_0025: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0064: 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_006f: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown NetworkManager instance = NetworkManager.Instance; object obj = <>O.<0>__OnReq; if (obj == null) { CoroutineHandler val = OnReq; <>O.<0>__OnReq = val; obj = (object)val; } object obj2 = <>O.<1>__Noop; if (obj2 == null) { CoroutineHandler val2 = Noop; <>O.<1>__Noop = val2; obj2 = (object)val2; } _req = instance.AddRPC("vn_sttreq", (CoroutineHandler)obj, (CoroutineHandler)obj2); NetworkManager instance2 = NetworkManager.Instance; object obj3 = <>O.<1>__Noop; if (obj3 == null) { CoroutineHandler val3 = Noop; <>O.<1>__Noop = val3; obj3 = (object)val3; } object obj4 = <>O.<2>__OnData; if (obj4 == null) { CoroutineHandler val4 = OnData; <>O.<2>__OnData = val4; obj4 = (object)val4; } _data = instance2.AddRPC("vn_sttdata", (CoroutineHandler)obj3, (CoroutineHandler)obj4); _live = CfgStartOn.Value; if (!_started) { _started = true; Thread thread = new Thread(Worker); thread.IsBackground = true; thread.Name = "vn_mic"; thread.Start(); } } private static IEnumerator Noop(long s, ZPackage p) { yield break; } public static void NotePlayback(float sec) { double t = Now(); lock (_play) { _play.Add(new double[2] { t, t + (double)sec }); _play.RemoveAll((double[] w) => w[1] < t - 30.0); } string u = _helperUrl; if (!CfgMuteDuring.Value || !_want || !_helperMic || u.Length <= 0) { return; } int ms = (int)(sec * 1000f) + 400; ThreadPool.QueueUserWorkItem(delegate { try { Voice.Http(u + "/mic/mute?ms=" + ms, new byte[0], "text/plain", 1500); } catch { } }); } public static void NoteHeard(string text) { if (string.IsNullOrEmpty(text)) { return; } double t = Now(); lock (_heard) { _heard.Add(new KeyValuePair<double, string>(t, text)); _heard.RemoveAll((KeyValuePair<double, string> h) => h.Key < t - 45.0); } } public static void Pump() { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) for (int num = 0; num < 8; num++) { Action action; lock (_main) { if (_main.Count == 0) { break; } action = _main.Dequeue(); goto IL_0040; } IL_0040: try { action(); } catch (Exception ex) { Debug.LogWarning((object)("[ValheimNet] mic main: " + ex.Message)); } } if (CfgOn == null) { return; } Player localPlayer = Player.m_localPlayer; if (CfgOn.Value && Object.op_Implicit((Object)(object)localPlayer)) { KeyboardShortcut value = CfgKey.Value; if (((KeyboardShortcut)(ref value)).IsDown() && !Typing()) { _live = !_live; if (Object.op_Implicit((Object)(object)MessageHud.instance)) { MessageHud.instance.ShowMessage((MessageType)2, _live ? "Open mic: ON" : "Open mic: OFF", 0, (Sprite)null, false, true); } Debug.Log((object)("[ValheimNet] " + DateTime.Now.ToString("HH:mm:ss.fff") + " open mic " + (_live ? "on" : "off") + " (frame " + Time.frameCount + ")")); _assertAt = 0f; } } bool flag = CfgOn.Value && _live && Object.op_Implicit((Object)(object)localPlayer) && !((Character)localPlayer).IsDead() && Object.op_Implicit((Object)(object)ZNet.instance) && !Game.IsPaused(); _helperUrl = (Voice.CfgHelper.Value ?? "").TrimEnd(new char[1] { '/' }); if (flag != _want || Time.realtimeSinceStartup > _assertAt) { _want = flag; _assertAt = Time.realtimeSinceStartup + 10f; string u = _helperUrl; bool w = flag; if (u.Length > 0) { ThreadPool.QueueUserWorkItem(delegate { try { Voice.Http(u + (w ? "/mic/on" : "/mic/off"), new byte[0], "text/plain", 1500); _helperMic = true; _helperChecked = true; } catch { if (_helperMic && w && Time.realtimeSinceStartup > _toggleMsgAt) { _toggleMsgAt = Time.realtimeSinceStartup + 300f; Debug.LogWarning((object)"[ValheimNet] open mic: helper not answering"); } _helperMic = false; _helperChecked = true; } }); } } bool flag2 = flag && CfgBuiltIn.Value && !_helperMic && (_helperUrl.Length == 0 || _helperChecked) && !MenuBadge.Headless && Time.realtimeSinceStartup > _noMicUntil; if (flag2 && !_builtinLive) { StartBuiltin(); } else if (!flag2 && _builtinLive) { StopBuiltin(); } if (!_builtinLive) { return; } try { ReadBuiltin(); } catch (Exception ex2) { Debug.LogWarning((object)("[ValheimNet] game mic read: " + ex2.Message)); StopBuiltin(); _noMicUntil = Time.realtimeSinceStartup + 30f; } } private static void StartBuiltin() { try { if (!M.Ok()) { _noMicUntil = float.MaxValue; return; } string[] array = M.Devices(); if (array == null || array.Length == 0) { Debug.LogWarning((object)"[ValheimNet] open mic: no microphone found (retry in 60 s)"); _noMicUntil = Time.realtimeSinceStartup + 60f; return; } string text = (CfgDevice.Value ?? "").Trim(); _dev = ((text.Length > 0 && array.Contains(text)) ? text : null); M.Caps(_dev, out var min, out var max); int rate = (((min == 0 && max == 0) || (min <= 16000 && max >= 16000)) ? 16000 : ((max < 16000) ? max : min)); _clip = M.Start(_dev, rate); if (_clip == null) { _noMicUntil = Time.realtimeSinceStartup + 60f; Debug.LogWarning((object)"[ValheimNet] open mic: game mic failed to start"); return; } _devRate = M.Int(_clip, "frequency"); _lastPos = 0; _phase = 0.0; _vad.Reset(); _vad.MinRms = Mathf.Clamp(CfgVadLevel.Value, 0.002f, 0.2f); _builtinLive = true; Debug.Log((object)("[ValheimNet] open mic: game mic ON (" + (_dev ?? ("default: " + array[0])) + ", " + _devRate + " Hz; devices: " + string.Join(" | ", array) + ")")); if (Object.op_Implicit((Object)(object)MessageHud.instance)) { MessageHud.instance.ShowMessage((MessageType)1, "Open mic: listening (Insert toggles)", 0, (Sprite)null, false, true); } } catch (Exception ex) { Debug.LogWarning((object)("[ValheimNet] open mic: game mic start failed: " + ex.Message)); _noMicUntil = Time.realtimeSinceStartup + 60f; } } private static void StopBuiltin() { _builtinLive = false; try { M.End(_dev); } catch { } _clip = null; _vad.Reset(); Debug.Log((object)"[ValheimNet] open mic: game mic off"); } private static void ReadBuiltin() { if (_clip == null) { _builtinLive = false; return; } int num = M.Pos(_dev); if (num < 0) { return; } int num2 = M.Int(_clip, "samples"); int num3 = Math.Max(1, M.Int(_clip, "channels")); int num4 = num - _lastPos; if (num4 < 0) { num4 += num2; } if (num4 <= 0) { return; } if (num4 > _devRate * 2) { _lastPos = (num - _devRate * 2 + num2) % num2; num4 = _devRate * 2; } float[] array = new float[num4 * num3]; int num5 = Math.Min(num4, num2 - _lastPos); float[] array2 = new float[num5 * num3]; M.GetData(_clip, array2, _lastPos); Array.Copy(array2, array, array2.Length); if (num5 < num4) { float[] array3 = new float[(num4 - num5) * num3]; M.GetData(_clip, array3, 0); Array.Copy(array3, 0, array, array2.Length, array3.Length); } _lastPos = num; float[] array4 = array; if (num3 > 1) { array4 = new float[num4]; for (int i = 0; i < num4; i++) { float num6 = 0f; for (int j = 0; j < num3; j++) { num6 += array[i * num3 + j]; } array4[i] = num6 / (float)num3; } } float[] array5 = MicVad.To16k(array4, num4, _devRate, ref _phase); foreach (byte[] item in _vad.Feed(array5, 0, array5.Length)) { double t1 = Now(); double t2 = t1 - (double)(item.Length - 44) / 2.0 / 16000.0; if (Interlocked.Increment(ref _clipsInFlight) > 2) { Interlocked.Decrement(ref _clipsInFlight); Interlocked.Increment(ref CDropped); Debug.Log((object)"[ValheimNet] mic: dropped (2 clips already waiting)"); continue; } string b64 = Convert.ToBase64String(item); ThreadPool.QueueUserWorkItem(delegate { try { HandleClip(t2, t1, null, b64); } catch (Exception ex) { Debug.LogWarning((object)("[ValheimNet] mic clip: " + ex.Message)); } finally { Interlocked.Decrement(ref _clipsInFlight); } }); } } private static bool Typing() { if ((!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !TextInput.IsVisible() && !Menu.IsVisible()) { return InventoryGui.IsVisible(); } return true; } public static void Shutdown() { if (_builtinLive) { try { M.End(_dev); } catch { } } string helperUrl = _helperUrl; if (helperUrl.Length > 0) { try { Voice.Http(helperUrl + "/mic/off", new byte[0], "text/plain", 800); } catch { } } } private static void Worker() { while (true) { Thread.Sleep(400); try { string helperUrl = _helperUrl; if (!_want || !_helperMic || helperUrl.Length == 0) { continue; } foreach (string item in MicText.Segments(Encoding.UTF8.GetString(Voice.Http(helperUrl + "/mic/next", null, null, 1500)))) { Handle(item); } } catch (Exception ex) { if (_want) { Debug.LogWarning((object)("[ValheimNet] mic poll: " + ex.Message)); } Thread.Sleep(3000); } } } private static void Handle(string seg) { HandleClip(MicText.JsonNum(seg, "t0", 0.0), MicText.JsonNum(seg, "t1", 0.0), TextUtil.JsonString(seg, "text"), TextUtil.JsonString(seg, "wav")); } private static void HandleClip(double t0, double t1, string text, string b64) { List<double[]> windows; lock (_play) { windows = _play.ToList(); } if (CfgMuteDuring.Value && MicText.OverlapsPlayback(t0, t1, windows, 0.4)) { Debug.Log((object)"[ValheimNet] mic: dropped (during our playback)"); return; } if (text == null) { if (string.IsNullOrEmpty(b64)) { return; } text = Transcribe(b64); if (text == null) { return; } } if (text.Trim().Length == 0) { Lost("whisper heard nothing", ref CEmpty); return; } List<string> heard; lock (_heard) { heard = _heard.Select((KeyValuePair<double, string> h) => h.Value).ToList(); } string why; string ok = MicText.Accept(text, heard, Math.Max(1, CfgMinWords.Value), Math.Max(1, CfgMinChars.Value), out why); if (ok == null) { Interlocked.Increment(ref CDropped); Debug.Log((object)("[ValheimNet] mic: dropped '" + Short(text) + "' (" + why + ")")); return; } _send.PerMinute = Math.Max(1, CfgPerMin.Value); if (!_send.TryTake(DateTime.UtcNow)) { Interlocked.Increment(ref CDropped); Debug.Log((object)"[ValheimNet] mic: dropped (client rate limit, MaxPerMinute)"); return; } Interlocked.Increment(ref COk); lock (_main) { _main.Enqueue(delegate { Say(ok); }); } } private static string Short(string s) { if (s.Length <= 60) { return s; } return s.Substring(0, 60) + "..."; } private static void Say(string text) { if (!Object.op_Implicit((Object)(object)Chat.instance) || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !_want) { return; } Debug.Log((object)("[ValheimNet] mic: '" + text + "'")); Sending = true; try { Chat.instance.SendText((Type)1, text); } finally { Sending = false; } } public static string WhisperJson(string b64) { return "{\"audio_data\":\"data:audio/wav;base64," + b64 + "\",\"prompt\":\"\",\"suppress_non_speech\":true,\"langcode\":\"en\"}"; } private static string Transcribe(string b64) { string value = CfgLocalWhisper.Value; if (!string.IsNullOrEmpty(value)) { try { return TextUtil.JsonString(Encoding.UTF8.GetString(Voice.Http(value, Encoding.UTF8.GetBytes(WhisperJson(b64)), "application/json", 15000)), "text") ?? ""; } catch (Exception ex) { Debug.LogWarning((object)("[ValheimNet] local Whisper: " + ex.Message)); } } bool isServer = false; RunMain(delegate { isServer = Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer(); }); if (isServer) { try { return TextUtil.JsonString(Encoding.UTF8.GetString(Voice.Http(CfgServerWhisper.Value, Encoding.UTF8.GetBytes(WhisperJson(b64)), "application/json", 15000)), "text") ?? ""; } catch (Exception ex2) { Debug.LogWarning((object)("[ValheimNet] desk Whisper: " + ex2.Message)); return null; } } if (!CfgUseServer.Value) { Lost("UseServerWhisper=false and no local Whisper", ref CNoServer); return null; } byte[] wav; try { wav = Convert.FromBase64String(b64); } catch { Lost("bad clip from helper", ref CRefused); return null; } Interlocked.Increment(ref CSent); bool noServer = false; Wait w = new Wait(); int id = Interlocked.Increment(ref _seq); lock (_pending) { _pending[id] = w; } RunMain(delegate { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown long num = VillagersApi.ServerPeerId(); if (num == 0L) { noServer = true; w.ev.Set(); } else { ZPackage val = new ZPackage(); val.Write(1); val.Write(id); val.Write(wav); _req.SendPackage(num, val); } }); bool flag = w.ev.WaitOne(20000); lock (_pending) { _pending.Remove(id); } if (noServer) { Lost("not connected to a server", ref CNoServer); return null; } if (!flag) { Lost("no answer from server in 20 s", ref CTimeout); return null; } if (w.text == null) { Lost("server refused or Whisper failed - see server log", ref CRefused); return null; } return w.text; } private static void RunMain(Action a) { ManualResetEvent ev = new ManualResetEvent(initialState: false); lock (_main) { _main.Enqueue(delegate { try { a(); } finally { ev.Set(); } }); } ev.WaitOne(5000); } private static IEnumerator OnReq(lo
plugins/ValheimNet_Villagers/Villagers.Talk.dll
Decompiled 2 days agousing System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using BepInEx; using BepInEx.Configuration; using Jotunn.Entities; using Jotunn.Managers; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Villagers.Talk")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Villagers.Talk")] [assembly: AssemblyTitle("Villagers.Talk")] [assembly: AssemblyVersion("1.0.0.0")] [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 Villagers.Talk { public static class TalkChat { private class TalkCmd : ConsoleCommand { public override string Name => "vv"; public override string Help => "vv <message> : talk to nearest villager"; public override void Run(string[] args) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer)) { ZPackage val = new ZPackage(); val.Write(((Component)localPlayer).transform.position); val.Write(localPlayer.GetPlayerName()); val.Write(string.Join(" ", args)); _rpc.SendPackage(VillagersApi.ServerPeerId(), val); } } } [CompilerGenerated] private static class <>O { public static CoroutineHandler <0>__OnServer; public static CoroutineHandler <1>__OnClient; } private static CustomRPC _rpc; public static void Init() { //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_0025: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown NetworkManager instance = NetworkManager.Instance; object obj = <>O.<0>__OnServer; if (obj == null) { CoroutineHandler val = OnServer; <>O.<0>__OnServer = val; obj = (object)val; } object obj2 = <>O.<1>__OnClient; if (obj2 == null) { CoroutineHandler val2 = OnClient; <>O.<1>__OnClient = val2; obj2 = (object)val2; } _rpc = instance.AddRPC("vv_chat", (CoroutineHandler)obj, (CoroutineHandler)obj2); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new TalkCmd()); } private static IEnumerator OnServer(long sender, ZPackage pkg) { Vector3 val = pkg.ReadVector3(); string speaker = pkg.ReadString(); string text = pkg.ReadString(); if (!ZNet.instance.IsServer()) { yield break; } VillagerBrain val2 = null; float num = 8f; VillagerBrain[] array = VillagersApi.AllVillagers(); foreach (VillagerBrain val3 in array) { float num2 = Vector3.Distance(((Component)val3).transform.position, val); if (num2 < num) { num = num2; val2 = val3; } } if (Object.op_Implicit((Object)(object)val2)) { VillagerBrain b = val2; LlmBridge.Ask(b.Npc, b.Npc, $"Your job is {b.Job}. Speak briefly, in character.", speaker, text, delegate(string reply) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val4 = new ZPackage(); val4.Write(b.Npc); val4.Write(reply); _rpc.SendPackage(0L, val4); }); } } private static IEnumerator OnClient(long sender, ZPackage pkg) { string text = pkg.ReadString(); string text2 = pkg.ReadString(); Chat instance = Chat.instance; if (instance != null) { ((Terminal)instance).AddString("<color=yellow>" + text + "</color>: " + text2); } yield break; } } public static class LlmBridge { public static string KoboldUrl = "http://127.0.0.1:5001/api/v1/generate"; public static string MemoryUrl = "http://127.0.0.1:5020"; public static int MaxPerMinute = 6; private static readonly ConcurrentQueue<Action> _results = new ConcurrentQueue<Action>(); private static readonly ConcurrentDictionary<string, bool> _inFlight = new ConcurrentDictionary<string, bool>(); private static int _sentThisMinute; private static DateTime _window = DateTime.UtcNow; private static readonly string[] Canned = new string[4] { "Skål, friend.", "Busy day, the wood won't chop itself.", "Odin watches over us.", "Mind the greylings near the treeline." }; public static void Pump() { Action result; while (_results.TryDequeue(out result)) { try { result(); } catch (Exception ex) { Debug.LogError((object)ex); } } } public static bool Ask(string npcId, string npcName, string persona, string speaker, string text, Action<string> onReply) { if ((DateTime.UtcNow - _window).TotalSeconds > 60.0) { _window = DateTime.UtcNow; _sentThisMinute = 0; } if (_sentThisMinute >= MaxPerMinute || !_inFlight.TryAdd(npcId, value: true)) { onReply(Fallback()); return false; } _sentThisMinute++; ThreadPool.QueueUserWorkItem(delegate { string reply; try { string s = NpcMemory.Recall(npcId, speaker, 12); string s2 = "You are " + npcName + ", a Viking villager. " + persona + "\nMemories: " + Trim(s, 1200) + "\n" + speaker + ": " + text + "\n" + npcName + ":"; string json = "{\"prompt\":" + Json(s2) + ",\"max_length\":80,\"temperature\":0.8,\"stop_sequence\":[\"\\n\"]}"; string res = Post(KoboldUrl, json, 20000); reply = Extract(res) ?? Fallback(); NpcMemory.Remember(npcId, speaker + " said: " + text + " | I replied: " + reply); } catch (Exception ex) { Debug.LogWarning((object)("[Villagers] LLM: " + ex.Message)); reply = Fallback(); } finally { _inFlight.TryRemove(npcId, out var _); } _results.Enqueue(delegate { onReply(reply); }); }); return true; } private static string Fallback() { return Canned[Random.Range(0, Canned.Length)]; } private static string Trim(string s, int n) { if (s != null) { if (s.Length <= n) { return s; } return s.Substring(0, n); } return ""; } private static string Get(string url) { try { using WebClient webClient = new WebClient(); return webClient.DownloadString(url); } catch { return ""; } } private static string Post(string url, string json, int timeout) { HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url); httpWebRequest.Method = "POST"; httpWebRequest.ContentType = "application/json"; httpWebRequest.Timeout = timeout; byte[] bytes = Encoding.UTF8.GetBytes(json); using (Stream stream = httpWebRequest.GetRequestStream()) { stream.Write(bytes, 0, bytes.Length); } using StreamReader streamReader = new StreamReader(httpWebRequest.GetResponse().GetResponseStream()); return streamReader.ReadToEnd(); } private static string Extract(string res) { int num = res.IndexOf("\"text\":"); if (num < 0) { return null; } num = res.IndexOf('"', num + 7) + 1; StringBuilder stringBuilder = new StringBuilder(); for (; num < res.Length && res[num] != '"'; num++) { if (res[num] == '\\' && num + 1 < res.Length) { num++; stringBuilder.Append((res[num] == 'n') ? ' ' : res[num]); } else { stringBuilder.Append(res[num]); } } string text = stringBuilder.ToString().Trim(); if (text.Length != 0) { return text; } return null; } public static string Json(string s) { return "\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n") .Replace("\r", "") + "\""; } } public static class NpcMemory { private static readonly object _lock = new object(); private static string Dir() { string path = (Object.op_Implicit((Object)(object)ZNet.instance) ? ZNet.instance.GetWorldName() : "default"); string text = Path.Combine(Paths.ConfigPath, "VillagersMemory", path); Directory.CreateDirectory(text); return text; } private static string FileFor(string npc) { return Path.Combine(Dir(), string.Concat(string.Concat(npc.Split(Path.GetInvalidFileNameChars())), ".log")); } public static void Remember(string npc, string line) { lock (_lock) { File.AppendAllText(FileFor(npc), $"[{DateTime.Now:yyyy-MM-dd HH:mm}] {line.Replace('\n', ' ')}\n"); } } public static string Recall(string npc, string speaker, int n) { string[] source; lock (_lock) { string path = FileFor(npc); if (!File.Exists(path)) { return "(none yet)"; } source = File.ReadAllLines(path); } IEnumerable<string> first = source.Where((string l) => l.Contains(speaker)).Reverse().Take(n / 2); IEnumerable<string> second = source.Reverse().Take(n); return string.Join("\n", first.Concat(second).Distinct().Take(n) .Reverse()); } } [BepInPlugin("valheimnet.villagers.talk", "Villagers.Talk", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class TalkPlugin : BaseUnityPlugin { static TalkPlugin() { try { string configPath = Paths.ConfigPath; string text = Path.Combine(configPath, "jake.villagers.talk.cfg"); string text2 = Path.Combine(configPath, "valheimnet.villagers.talk.cfg"); if (File.Exists(text) && !File.Exists(text2)) { File.Copy(text, text2); } } catch (Exception) { } } private void Awake() { LlmBridge.KoboldUrl = ((BaseUnityPlugin)this).Config.Bind<string>("LLM", "KoboldUrl", LlmBridge.KoboldUrl, "Server-side KoboldCpp endpoint").Value; LlmBridge.MaxPerMinute = ((BaseUnityPlugin)this).Config.Bind<int>("LLM", "MaxPerMinute", 6, (ConfigDescription)null).Value; TalkChat.Init(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Villagers.Talk loaded"); } private void Update() { LlmBridge.Pump(); } } }