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 VikingFactions v0.1.0
VikingFactions.dll
Decompiled 3 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Microsoft.CodeAnalysis; using Splatform; using UnityEngine; using VikingAI.Core; using VikingAI.NPC.Affordances; using VikingAI.NPC.Prefabs; using VikingAI.Profiles; using VikingAI.Simulation; using VikingAI.Tactics; using VikingFactions.Configuration; using VikingFactions.Core; using VikingFactions.Marauders; using VikingFactions.Telemetry; using VikingFactions.Wayfarers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("DigitalVikings")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+b6666f0c545d294537dbbdd0de1bace62fa14cf3")] [assembly: AssemblyProduct("VikingFactions")] [assembly: AssemblyTitle("VikingFactions")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 VikingFactions { public static class FactionPrefabs { public static void RegisterAll() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0091: 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_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: 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_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown //IL_00f7: 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_0107: 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_0119: 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: 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_0142: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Expected O, but got Unknown HumanoidPrefabConfig val = new HumanoidPrefabConfig(); val.PrefabName = "VF_Wayfarer"; val.DisplayName = "Wayfarer"; val.Faction = (Faction)0; val.Group = "VF_Wayfarers"; val.MaxHealth = 90f; val.IsAggravatable = false; val.DefaultItemNames = new string[3] { "ArmorRagsChest", "ArmorRagsLegs", "CapeDeerHide" }; val.BehaviorProfile = AgentBehaviorProfile.CreateVillagerDefault("VF_Wayfarer", (NpcRoleFlags)65536); HumanoidPrefabBuilder.CreateHumanoidNpcPrefab(val); if (RaiderFactionRegistry.TryGetFaction("marauders", out IRaiderFactionDefinition definition)) { HumanoidPrefabBuilder.CreateHumanoidNpcPrefab(new HumanoidPrefabConfig { PrefabName = "VF_Marauder", DisplayName = "Exile Marauder", Faction = (Faction)7, Group = "VF_Marauders", MaxHealth = 160f, IsAggravatable = true, DefaultItemNames = definition.RaiderDefaultEquipment, BehaviorProfile = AgentBehaviorProfile.CreateVillagerDefault("VF_Marauder", (NpcRoleFlags)131072) }); HumanoidPrefabBuilder.CreateHumanoidNpcPrefab(new HumanoidPrefabConfig { PrefabName = "VF_Warlord", DisplayName = "Marauder Warlord", Faction = (Faction)7, Group = "VF_Marauders", MaxHealth = 700f, IsAggravatable = true, DefaultItemNames = definition.WarlordDefaultEquipment, BehaviorProfile = AgentBehaviorProfile.CreateVillagerDefault("VF_Warlord", (NpcRoleFlags)131072) }); } } } [BepInPlugin("com.digitalvikings.vikingfactions", "VikingFactions", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class VikingFactionsPlugin : BaseUnityPlugin { public const string ModGuid = "com.digitalvikings.vikingfactions"; public const string ModName = "VikingFactions"; public const string ModVersion = "0.1.0"; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"VikingFactions v0.1.0 initializing..."); ModConfig.Init(((BaseUnityPlugin)this).Config); RaiderFactionRegistry.RegisterBuiltInFactions(); BrainPipeline.RegisterModule((IAIBrainModule)(object)new WayfarerBrainModule()); BrainPipeline.RegisterModule((IAIBrainModule)(object)new MarauderHeistBrainModule()); VirtualAgentDirector.RegisterTask((IVirtualAgentTask)(object)new WayfarerVirtualTask()); VirtualAgentDirector.RegisterTask((IVirtualAgentTask)(object)new MarauderVirtualTask()); VirtualAgentDirector.OnAgentRestored += OnAgentRestoredAfterWorldLoad; PrefabManager.OnVanillaPrefabsAvailable += FactionPrefabs.RegisterAll; VikingFactionsConsoleCommand.Register(); _harmony = new Harmony("com.digitalvikings.vikingfactions"); _harmony.PatchAll(); Log.LogInfo((object)"VikingFactions v0.1.0 loaded successfully."); } private void Update() { if (ModConfig.ModEnabled.Value) { HospitalityScanner.Tick(); } } private void OnDestroy() { VirtualAgentDirector.OnAgentRestored -= OnAgentRestoredAfterWorldLoad; PrefabManager.OnVanillaPrefabsAvailable -= FactionPrefabs.RegisterAll; Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private static void OnAgentRestoredAfterWorldLoad(ZDO zdo) { if (zdo != null && zdo.IsValid()) { if (zdo.GetPrefab() == StringExtensionMethods.GetStableHashCode("VF_Wayfarer")) { HospitalityScanner.RegisterWayfarer(zdo); } else if (zdo.GetInt("VF_Raider_State", -1) >= 0) { RaiderCampDirector.RegisterCampPiece(zdo); } } } } } namespace VikingFactions.Wayfarers { public static class HospitalityScanner { public const string WayfarerPrefabName = "VF_Wayfarer"; private static float _nextScanTime; private static float _lastSpawnTime = -9999f; private static readonly HashSet<ZDO> _wayfarerZdos = new HashSet<ZDO>(); public static int LastEvaluatedComfort { get; private set; } public static Vector3 LastQualifyingHearthPos { get; private set; } public static int ActiveWayfarerCount { get; private set; } public static void RegisterWayfarer(ZDO zdo) { if (zdo != null) { _wayfarerZdos.Add(zdo); } } public static void Tick() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) if (ModConfig.ModEnabled.Value && ModConfig.EnableWayfarers.Value && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !((Object)(object)Player.m_localPlayer == (Object)null)) { float time = Time.time; if (!(time < _nextScanTime)) { _nextScanTime = time + 15f; EvaluateAndSchedule(((Component)Player.m_localPlayer).transform.position, time); } } } public static bool TryFindQualifyingHearth(Vector3 origin, out Vector3 hearthPos, out int comfort) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //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_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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) hearthPos = Vector3.zero; comfort = 0; int num = 0; float num2 = 3600f; float num3 = default(float); bool flag = default(bool); foreach (Fireplace fireplace in PieceRegistry.Fireplaces) { if ((Object)(object)fireplace == (Object)null) { continue; } Vector3 val = ((Component)fireplace).transform.position - origin; if (((Vector3)(ref val)).sqrMagnitude > num2 || !fireplace.IsBurning()) { continue; } Vector3 val2 = ((Component)fireplace).transform.position + Vector3.up * 0.8f; Cover.GetCoverForPoint(val2, ref num3, ref flag, 0.5f); if (flag || !(num3 < 0.7f)) { int num4 = SE_Rested.CalculateComfortLevel(true, val2); if (num4 > num) { num = num4; hearthPos = ((Component)fireplace).transform.position; } } } if (num < ModConfig.MinHospitalityComfort.Value) { return false; } comfort = num; return true; } private static void EvaluateAndSchedule(Vector3 playerPos, float now) { //IL_0000: 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_005d: Unknown result type (might be due to invalid IL or missing references) if (!TryFindQualifyingHearth(playerPos, out var hearthPos, out var comfort)) { LastEvaluatedComfort = 0; return; } LastEvaluatedComfort = comfort; LastQualifyingHearthPos = hearthPos; int num = Mathf.Max(1, comfort / Mathf.Max(1, ModConfig.ComfortPerWayfarerCap.Value)); int num2 = (ActiveWayfarerCount = CountActiveWayfarers()); if (num2 < num && !(now - _lastSpawnTime < ModConfig.WayfarerSpawnIntervalSeconds.Value) && SpawnWayfarerTowards(hearthPos)) { _lastSpawnTime = now; ActiveWayfarerCount = num2 + 1; } } public static int CountActiveWayfarers() { int num = 0; _wayfarerZdos.RemoveWhere((ZDO z) => z == null || !z.IsValid()); foreach (ZDO wayfarerZdo in _wayfarerZdos) { if (wayfarerZdo.GetInt("VF_WF_State", 0) != 2) { num++; } } return num; } public static bool SpawnWayfarerTowards(Vector3 hearthPos, float minDist = 150f, float maxDist = 280f) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_0062: 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_00a4: Unknown result type (might be due to invalid IL or missing references) if (!FindValidLandPoint(hearthPos, minDist, maxDist, out var pos)) { return false; } Vector3 val = pos; Vector3 val2 = hearthPos - pos; ZDO val3 = VirtualAgentDirector.SpawnVirtualAgentZdo("VF_Wayfarer", val, Quaternion.LookRotation(((Vector3)(ref val2)).normalized), (NpcRoleFlags)65536, true); if (val3 == null) { return false; } double timeSeconds = ZNet.instance.GetTimeSeconds(); val3.Set("VF_WF_State", 0); val3.Set("VF_WF_TargetPos", hearthPos); val3.Set("VF_WF_OriginPos", pos); val3.Set("VF_WF_LastMeal", (float)timeSeconds); val3.Set("VF_WF_Mood", 0); val3.Set("VF_WF_Since", (float)timeSeconds); RegisterWayfarer(val3); VikingFactionsPlugin.Log.LogInfo((object)$"[Hospitality] Wayfarer dispatched from {pos:F0} toward hearth {hearthPos:F0} (comfort {LastEvaluatedComfort})."); return true; } public static bool FindValidLandPoint(Vector3 center, float minDist, float maxDist, out Vector3 pos) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Invalid comparison between Unknown and I4 //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: 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) pos = Vector3.zero; if (WorldGenerator.instance == null) { return false; } float num = (((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.m_waterLevel : 30f); for (int i = 0; i < 24; i++) { float num2 = Random.Range(0f, MathF.PI * 2f); float num3 = Random.Range(minDist, maxDist); Vector3 val = center + new Vector3(Mathf.Cos(num2) * num3, 0f, Mathf.Sin(num2) * num3); Biome biome = WorldGenerator.instance.GetBiome(val.x, val.z, 0.02f, false); if ((int)biome != 256 && (int)biome != 0) { float height = WorldGenerator.instance.GetHeight(val.x, val.z); if (!(height <= num + 1.2f)) { val.y = height; pos = val; return true; } } } return false; } } public sealed class WayfarerBrainModule : IAIBrainModule { private sealed class LocalState { public float NextChat = Time.time + Random.Range(15f, 35f); public float NextBedCheck; public float Neglect; public Component? Bed; } private const float MovingScore = 60f; private const float ServiceScore = 50f; private static readonly string[] _happy = new string[4] { "A warm fire and good company!", "May Odin bless this hall.", "Fine shelter from the wilds.", "Nothing beats a hot hearth after a long march." }; private static readonly string[] _hungry = new string[3] { "The larders in this hall are bare...", "My stomach rumbles. Is there no food to spare?", "A warm fire, yet an empty table." }; private static readonly string[] _noBed = new string[2] { "Not a spare bunk in the hall tonight...", "Sleeping on the floorboards again, it seems." }; private static readonly string[] _depart = new string[2] { "The hearth grows cold. Time to move on.", "Safe travels, friend. I must return to the road." }; private static readonly ConditionalWeakTable<Character, LocalState> _states = new ConditionalWeakTable<Character, LocalState>(); public string Id => "VikingFactions.WayfarerHospitality"; private static LocalState GetState(Character c) { return _states.GetValue(c, (Character _) => new LocalState()); } private static bool IsWayfarer(Character c, out ZDO zdo) { zdo = null; ZNetView nView = AgentProfileRegistry.GetNView(c); if ((Object)(object)nView == (Object)null || !nView.IsValid() || !nView.IsOwner()) { return false; } zdo = nView.GetZDO(); if (zdo != null) { return zdo.GetPrefab() == StringExtensionMethods.GetStableHashCode("VF_Wayfarer"); } return false; } public float EvaluateUtility(BaseAI ai, Character character, AgentBehaviorProfile profile) { if (!ModConfig.ModEnabled.Value || !ModConfig.EnableWayfarers.Value) { return 0f; } if (!IsWayfarer(character, out ZDO zdo)) { return 0f; } MonsterAI val = (MonsterAI)(object)((ai is MonsterAI) ? ai : null); if (val != null && (Object)(object)((BaseAI)val).GetTargetCreature() != (Object)null) { return 0f; } if (zdo.GetInt("VF_WF_State", 0) != 1) { return 60f; } LocalState state = GetState(character); double timeSeconds = ZNet.instance.GetTimeSeconds(); if (timeSeconds - (double)zdo.GetFloat("VF_WF_LastMeal", (float)timeSeconds) >= (double)ModConfig.WayfarerMealIntervalSeconds.Value || EnvMan.IsNight() || state.Neglect >= 120f) { return 50f; } CheckHearthUpkeep(character, state, Time.deltaTime); AmbientChatter(character, zdo, state); return 0f; } public bool Tick(BaseAI ai, Character character, AgentBehaviorProfile profile, float dt) { //IL_0026: 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_0035: 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_003d: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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_00d3: Unknown result type (might be due to invalid IL or missing references) if (!IsWayfarer(character, out ZDO zdo)) { return false; } LocalState state = GetState(character); int num = zdo.GetInt("VF_WF_State", 0); Vector3 position = ((Component)character).transform.position; switch (num) { case 0: { Vector3 vec2 = zdo.GetVec3("VF_WF_TargetPos", position); if (Utils.DistanceXZ(position, vec2) <= 8f) { zdo.Set("VF_WF_State", 1); zdo.Set("VF_WF_Since", (float)ZNet.instance.GetTimeSeconds()); ai.StopMoving(); AiHelpers.Say(character, "Hail! May a weary traveler share your hearth?"); return true; } AiHelpers.MoveTo(ai, dt, vec2, 3f, run: false); return true; } case 2: { ReleaseBed(character, state); Vector3 vec = zdo.GetVec3("VF_WF_OriginPos", position); if (Utils.DistanceXZ(position, vec) <= 10f) { if (ZDOMan.instance != null) { ZDOMan.instance.DestroyZDO(zdo); } return true; } AiHelpers.MoveTo(ai, dt, vec, 3f, run: false); return true; } default: { CheckHearthUpkeep(character, state, dt); if (state.Neglect >= 120f) { AiHelpers.Say(character, _depart[Random.Range(0, _depart.Length)]); zdo.Set("VF_WF_State", 2); return true; } double timeSeconds = ZNet.instance.GetTimeSeconds(); if (timeSeconds - (double)zdo.GetFloat("VF_WF_LastMeal", (float)timeSeconds) >= (double)ModConfig.WayfarerMealIntervalSeconds.Value && TryMeal(ai, character, zdo, state, timeSeconds, dt)) { return true; } if (EnvMan.IsNight()) { if (TrySleep(ai, character, state, dt)) { return true; } } else { ReleaseBed(character, state); } AmbientChatter(character, zdo, state); return false; } } } private static bool TryMeal(BaseAI ai, Character character, ZDO zdo, LocalState local, double now, float dt) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_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_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) Container val = null; ItemData val2 = null; float num = float.MaxValue; Vector3 position = ((Component)character).transform.position; foreach (Container container in PieceRegistry.Containers) { if ((Object)(object)container == (Object)null) { continue; } Vector3 val3 = ((Component)container).transform.position - position; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude > 484f || sqrMagnitude >= num || container.IsInUse()) { continue; } Inventory inventory = container.GetInventory(); if (inventory == null) { continue; } List<ItemData> allItems = inventory.GetAllItems(); for (int i = 0; i < allItems.Count; i++) { ItemData val4 = allItems[i]; if (val4?.m_shared != null && val4.m_shared.m_food > 0f) { num = sqrMagnitude; val = container; val2 = val4; break; } } } if ((Object)(object)val == (Object)null || val2 == null) { zdo.Set("VF_WF_Mood", -1); local.Neglect += dt * 1.5f; if (Time.time >= local.NextChat) { local.NextChat = Time.time + Random.Range(35f, 60f); AiHelpers.Say(character, _hungry[Random.Range(0, _hungry.Length)]); } return false; } if (Vector3.Distance(position, ((Component)val).transform.position) > 2.2f) { AiHelpers.MoveTo(ai, dt, ((Component)val).transform.position, 1.8f, run: false); return true; } ai.StopMoving(); AiHelpers.LookAt(ai, ((Component)val).transform.position); ZNetView component = ((Component)val).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid()) { component.ClaimOwnership(); } string name = val2.m_shared.m_name; val.GetInventory().RemoveOneItem(val2); zdo.Set("VF_WF_LastMeal", (float)now); zdo.Set("VF_WF_Mood", 1); local.Neglect = Mathf.Max(0f, local.Neglect - 45f); character.Heal(25f, true); AiHelpers.Say(character, "Grateful for the " + Localization.instance.Localize(name) + "!"); local.NextChat = Time.time + Random.Range(45f, 90f); return true; } private static bool TrySleep(BaseAI ai, Character character, LocalState local, float dt) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)character).transform.position; if ((Object)(object)local.Bed == (Object)null) { if (Time.time < local.NextBedCheck) { return false; } local.NextBedCheck = Time.time + 8f; Bed val = null; float num = 576f; foreach (Bed bed in PieceRegistry.Beds) { if (!((Object)(object)bed == (Object)null) && !AffordanceRegistry.IsReservedByOther((Component)(object)bed, character) && !PlayerInBed(bed)) { Vector3 val2 = ((Component)bed).transform.position - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; val = bed; } } } if (!((Object)(object)val != (Object)null) || !AffordanceRegistry.TryReserve((Component)(object)val, character, 60f)) { if (Time.time >= local.NextChat) { local.NextChat = Time.time + Random.Range(45f, 80f); AiHelpers.Say(character, _noBed[Random.Range(0, _noBed.Length)]); } return false; } local.Bed = (Component?)(object)val; } AffordanceRegistry.TryReserve(local.Bed, character, 30f); Vector3 position2 = local.Bed.transform.position; if (Vector3.Distance(position, position2) > 2f) { AiHelpers.MoveTo(ai, dt, position2, 1.6f, run: false); return true; } ai.StopMoving(); ZSyncAnimation component = ((Component)character).GetComponent<ZSyncAnimation>(); if (component != null) { component.SetBool("attach_bed", true); } return true; } private static void ReleaseBed(Character character, LocalState local) { if (!((Object)(object)local.Bed == (Object)null)) { AffordanceRegistry.ReleaseReservation(local.Bed, character); local.Bed = null; ZSyncAnimation component = ((Component)character).GetComponent<ZSyncAnimation>(); if (component != null) { component.SetBool("attach_bed", false); } } } private static bool PlayerInBed(Bed bed) { //IL_0029: 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) List<Player> allPlayers = Player.GetAllPlayers(); for (int i = 0; i < allPlayers.Count; i++) { Player val = allPlayers[i]; if ((Object)(object)val != (Object)null && ((Character)val).InBed() && Vector3.Distance(((Component)val).transform.position, ((Component)bed).transform.position) < 2.5f) { return true; } } return false; } private static void CheckHearthUpkeep(Character character, LocalState local, float dt) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_004e: Unknown result type (might be due to invalid IL or missing references) bool flag = false; float num = 484f; Vector3 position = ((Component)character).transform.position; foreach (Fireplace fireplace in PieceRegistry.Fireplaces) { if ((Object)(object)fireplace != (Object)null && fireplace.IsBurning()) { Vector3 val = ((Component)fireplace).transform.position - position; if (((Vector3)(ref val)).sqrMagnitude <= num) { flag = true; break; } } } local.Neglect = (flag ? Mathf.Max(0f, local.Neglect - dt * 0.5f) : (local.Neglect + dt)); } private static void AmbientChatter(Character character, ZDO zdo, LocalState local) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (!(Time.time < local.NextChat)) { local.NextChat = Time.time + Random.Range(45f, 95f); if (AiHelpers.PlayerNear(((Component)character).transform.position, 12f)) { string[] array = ((zdo.GetInt("VF_WF_Mood", 0) >= 0) ? _happy : _hungry); AiHelpers.Say(character, array[Random.Range(0, array.Length)]); } } } } public sealed class WayfarerVirtualTask : IVirtualAgentTask { private const float UnattendedStaySeconds = 1200f; public string Id => "VikingFactions.WayfarerVirtual"; public NpcRoleFlags RequiredRole => (NpcRoleFlags)65536; public void TickUnloadedZdo(ZDO zdo, NpcRoleFlags roles, float dt) { //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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: 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_00dc: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.ModEnabled.Value || !ModConfig.EnableWayfarers.Value || !zdo.IsValid() || !zdo.IsOwner()) { return; } int num = zdo.GetInt("VF_WF_State", 0); double num2 = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTimeSeconds() : 0.0); switch (num) { case 0: { Vector3 vec2 = zdo.GetVec3("VF_WF_TargetPos", zdo.GetPosition()); if (VirtualAgentDirector.MoveZdoAlongTerrain(zdo, vec2, 3.4f, dt, 8f)) { zdo.Set("VF_WF_State", 1); zdo.Set("VF_WF_Since", (float)num2); } break; } case 1: if (num2 - (double)zdo.GetFloat("VF_WF_Since", (float)num2) > 1200.0) { zdo.Set("VF_WF_State", 2); } break; default: { Vector3 vec = zdo.GetVec3("VF_WF_OriginPos", zdo.GetPosition()); if (VirtualAgentDirector.MoveZdoAlongTerrain(zdo, vec, 3.6f, dt, 8f) && ZDOMan.instance != null) { ZDOMan.instance.DestroyZDO(zdo); } break; } } } public void ApplyCatchUpOnLoad(Character agent, AgentBehaviorProfile profile, double elapsedOfflineSeconds) { } } } namespace VikingFactions.Telemetry { public sealed class VikingFactionsConsoleCommand : ConsoleCommand { public override string Name => "vf"; public override string Help => "VikingFactions tools (spawn_wayfarer, trigger_raid [marauders|draugr|fulings], status, clear_camps)"; public override bool IsCheat => true; public static void Register() { CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new VikingFactionsConsoleCommand()); } public override void Run(string[] args) { //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_0083: 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_00e5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { Console.instance.Print("[VikingFactions] No local player."); return; } string text = ((args != null && args.Length != 0) ? args[0].ToLowerInvariant() : "status"); Vector3 position = ((Component)Player.m_localPlayer).transform.position; switch (text) { case "spawn_wayfarer": { bool flag = HospitalityScanner.SpawnWayfarerTowards(position, 60f, 100f); Console.instance.Print(flag ? "[VikingFactions] Wayfarer dispatched toward your position." : "[VikingFactions] No valid spawn point found."); break; } case "trigger_raid": { RaiderCampDirector.ActiveCampRecord record; bool flag = RaiderCampDirector.TriggerFactionRaid((args.Length > 1) ? args[1] : "marauders", position, out record); Console.instance.Print((flag && record != null) ? $"[VikingFactions] {record.FactionDisplayName} camp placed at {record.CampCenter:F0}." : "[VikingFactions] Raid failed (unknown faction, disabled, not server, or no valid land)."); break; } case "clear_camps": Console.instance.Print($"[VikingFactions] Destroyed {RaiderCampDirector.ClearAllCamps()} camp/raider ZDOs."); break; case "status": Console.instance.Print($"[VikingFactions] Hearth comfort: {HospitalityScanner.LastEvaluatedComfort}, " + $"active wayfarers: {HospitalityScanner.CountActiveWayfarers()}, " + $"active camps: {RaiderCampDirector.ActiveCamps.Count}"); break; default: Console.instance.Print("Usage: vf spawn_wayfarer | trigger_raid [factionId] | status | clear_camps"); break; } } public override List<string> CommandOptionList() { return new List<string> { "spawn_wayfarer", "trigger_raid", "status", "clear_camps" }; } } } namespace VikingFactions.Patches { [HarmonyPatch] public static class FactionPatches { [HarmonyPatch(typeof(Character), "Start")] [HarmonyPostfix] private static void Character_Start_Postfix(Character __instance) { if (!ModConfig.ModEnabled.Value || (Object)(object)__instance == (Object)null || __instance.IsPlayer()) { return; } ZNetView nView = AgentProfileRegistry.GetNView(__instance); if ((Object)(object)nView == (Object)null || !nView.IsValid()) { return; } ZDO zDO = nView.GetZDO(); if (zDO != null) { if (zDO.GetPrefab() == StringExtensionMethods.GetStableHashCode("VF_Wayfarer")) { HospitalityScanner.RegisterWayfarer(zDO); VirtualAgentDirector.TrackZdoDirectly(zDO); } else if (zDO.GetInt("VF_Raider_State", -1) >= 0) { VirtualAgentDirector.TrackZdoDirectly(zDO); } } } [HarmonyPatch(typeof(RandEventSystem), "SetRandomEvent")] [HarmonyPrefix] private static bool RandEventSystem_SetRandomEvent_Prefix(RandomEvent ev, Vector3 pos, bool __runOriginal) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (!__runOriginal) { return true; } if (ev == null) { return true; } if (!ModConfig.ModEnabled.Value || !ModConfig.EnableMarauderHeists.Value || !ModConfig.InterceptVanillaRaids.Value) { return true; } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return true; } if (!RaiderFactionRegistry.TryGetFactionForVanillaEvent(ev.m_name, out IRaiderFactionDefinition definition)) { if (!RaiderFactionRegistry.TrySelectProgressiveFaction(out definition)) { return true; } } else if (!RaiderFactionRegistry.IsFactionAvailable(definition) && !RaiderFactionRegistry.TrySelectProgressiveFaction(out definition)) { return true; } if (RaiderCampDirector.TriggerFactionRaid(definition.FactionId, pos, out RaiderCampDirector.ActiveCampRecord _)) { return false; } return true; } [HarmonyPatch(typeof(Character), "GetJogSpeedFactor")] [HarmonyPostfix] private static void Character_GetJogSpeedFactor_Postfix(Character __instance, ref float __result) { if (IsCarrying(__instance)) { __result *= ModConfig.RaiderCarrySpeedMultiplier.Value; } } [HarmonyPatch(typeof(Character), "GetRunSpeedFactor")] [HarmonyPostfix] private static void Character_GetRunSpeedFactor_Postfix(Character __instance, ref float __result) { if (IsCarrying(__instance)) { __result *= ModConfig.RaiderCarrySpeedMultiplier.Value; } } private static bool IsCarrying(Character c) { if ((Object)(object)c != (Object)null && MarauderHeistBrainModule.CarryStates.TryGetValue(c, out CarryBox value)) { return value.Carrying; } return false; } [HarmonyPatch(typeof(Character), "OnDeath")] [HarmonyPostfix] private static void Character_OnDeath_Postfix(Character __instance) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_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_0081: 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || __instance.IsPlayer()) { return; } ZNetView nView = AgentProfileRegistry.GetNView(__instance); if ((Object)(object)nView == (Object)null || !nView.IsValid() || !nView.IsOwner()) { return; } ZDO zDO = nView.GetZDO(); if (zDO != null && zDO.GetInt("VF_Raider_State", -1) >= 0) { List<ItemData> allItems = LootStore.LoadBase64(zDO, "VF_Raider_Loot").GetAllItems(); for (int i = 0; i < allItems.Count; i++) { Vector3 val = ((Component)__instance).transform.position + Vector3.up * 0.6f + Random.insideUnitSphere * 0.4f; ItemDrop.DropItem(allItems[i], 0, val, Quaternion.Euler(0f, (float)Random.Range(0, 360), 0f)); } LootStore.SaveBase64(zDO, "VF_Raider_Loot", LootStore.NewInventory()); zDO.Set("VF_Raider_Stolen", 0); MarauderHeistBrainModule.CarryStates.Remove(__instance); } } } } namespace VikingFactions.Marauders { public sealed class CarryBox { public bool Carrying; } public sealed class MarauderHeistBrainModule : IAIBrainModule { private sealed class RaiderState { public Inventory Loot; public bool Initialized; public Container? Target; public float SiphonProgress; public float NextScan; public float RaidingSince; } private const float HeistScore = 60f; public static readonly ConditionalWeakTable<Character, CarryBox> CarryStates = new ConditionalWeakTable<Character, CarryBox>(); private static readonly ConditionalWeakTable<Character, RaiderState> _states = new ConditionalWeakTable<Character, RaiderState>(); public string Id => "VikingFactions.MarauderHeist"; private static bool TryGet(Character c, out ZDO zdo) { zdo = null; ZNetView nView = AgentProfileRegistry.GetNView(c); if ((Object)(object)nView == (Object)null || !nView.IsValid() || !nView.IsOwner()) { return false; } zdo = nView.GetZDO(); if (zdo != null) { return zdo.GetInt("VF_Raider_State", -1) >= 0; } return false; } private static RaiderState Init(Character c, ZDO zdo, AgentBehaviorProfile profile) { RaiderState value = _states.GetValue(c, (Character _) => new RaiderState()); if (!value.Initialized) { value.Initialized = true; value.Loot = LootStore.LoadBase64(zdo, "VF_Raider_Loot"); CarryStates.GetValue(c, (Character _) => new CarryBox()).Carrying = value.Loot.NrOfItems() > 0; AgentBehaviorProfile val = profile.Clone(); val.YieldToPlayers = false; val.UseFormationFollowing = false; val.CanTraversePortals = false; AgentProfileRegistry.SetInstanceProfile(c, val, false); } return value; } public float EvaluateUtility(BaseAI ai, Character character, AgentBehaviorProfile profile) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.ModEnabled.Value || !ModConfig.EnableMarauderHeists.Value) { return 0f; } if (!TryGet(character, out ZDO zdo)) { return 0f; } RaiderState raiderState = Init(character, zdo, profile); int num = zdo.GetInt("VF_Raider_State", 3); switch (num) { case 3: return 0f; case 2: return 60f; default: { bool flag = character.GetHealthPercentage() < 0.99f; if (num == 1 && flag && raiderState.Loot.NrOfItems() > 0 && character.GetHealthPercentage() < 0.5f) { RaiderCampDirector.SetState(zdo, 2); return 60f; } if (flag || AiHelpers.PlayerNear(((Component)character).transform.position, 7f)) { return 0f; } return 60f; } } } public bool Tick(BaseAI ai, Character character, AgentBehaviorProfile profile, float dt) { //IL_0028: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (!TryGet(character, out ZDO zdo)) { return false; } RaiderState raiderState = Init(character, zdo, profile); int num = zdo.GetInt("VF_Raider_State", 3); Vector3 position = ((Component)character).transform.position; switch (num) { case 0: { Vector3 vec = zdo.GetVec3("VF_Raider_TargetPos", position); if (Utils.DistanceXZ(position, vec) <= 14f) { RaiderCampDirector.SetState(zdo, 1); raiderState.RaidingSince = Time.time; return true; } AiHelpers.MoveTo(ai, dt, vec, 6f, run: true); return true; } case 1: return TickRaiding(ai, character, zdo, raiderState, dt); case 2: return TickFleeing(ai, character, zdo, raiderState, dt); default: return false; } } private static bool TickRaiding(BaseAI ai, Character character, ZDO zdo, RaiderState st, float dt) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_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_00c5: 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_00d5: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)character).transform.position; if (st.RaidingSince <= 0f) { st.RaidingSince = Time.time; } if ((Object)(object)st.Target == (Object)null || !HasLoot(st.Target) || st.Target.IsInUse()) { st.Target = null; st.SiphonProgress = 0f; if (Time.time >= st.NextScan) { st.NextScan = Time.time + 1.5f; st.Target = FindTarget(position, zdo); } if ((Object)(object)st.Target == (Object)null) { if (Time.time - st.RaidingSince > 10f) { RaiderCampDirector.SetState(zdo, 2); } return true; } } Vector3 position2 = ((Component)st.Target).transform.position; if (Utils.DistanceXZ(position, position2) > 2.2f) { AiHelpers.MoveTo(ai, dt, position2, 1.8f, run: true); return true; } ai.StopMoving(); AiHelpers.LookAt(ai, position2); st.SiphonProgress += dt; if (st.SiphonProgress < ModConfig.RaiderSiphonSecondsPerStack.Value) { return true; } st.SiphonProgress = 0f; StealOneStack(character, zdo, st); return true; } private static bool TickFleeing(BaseAI ai, Character character, ZDO zdo, RaiderState st, float dt) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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) Vector3 position = ((Component)character).transform.position; Vector3 vec = zdo.GetVec3("VF_Raider_CampPos", position); if (Utils.DistanceXZ(position, vec) <= 6f) { RaiderCampDirector.DepositLoot(zdo); st.Loot = LootStore.NewInventory(); CarryStates.GetValue(character, (Character _) => new CarryBox()).Carrying = false; RaiderCampDirector.SetState(zdo, 3); ai.StopMoving(); return true; } AiHelpers.MoveTo(ai, dt, vec, 4f, run: true); return true; } private static Container? FindTarget(Vector3 pos, ZDO raiderZdo) { //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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) Container result = null; float num = 2025f; foreach (Container container in PieceRegistry.Containers) { if ((Object)(object)container == (Object)null) { continue; } Vector3 val = ((Component)container).transform.position - pos; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!(sqrMagnitude >= num) && HasLoot(container) && !container.IsInUse()) { ZNetView component = ((Component)container).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid() && !component.GetZDO().GetBool("VF_IsSpoilsChest", false) && (!ModConfig.RaidersRespectWards.Value || !PrivateArea.InsideFactionArea(((Component)container).transform.position, (Faction)0))) { num = sqrMagnitude; result = container; } } } return result; } private static bool HasLoot(Container c) { Inventory inventory = c.GetInventory(); if (inventory != null) { return inventory.NrOfItems() > 0; } return false; } private static void StealOneStack(Character character, ZDO zdo, RaiderState st) { Container target = st.Target; Inventory inventory = target.GetInventory(); ItemData val = null; float num = -1f; List<ItemData> allItems = inventory.GetAllItems(); for (int i = 0; i < allItems.Count; i++) { ItemData val2 = allItems[i]; if (val2?.m_shared != null) { float num2 = ((val2.m_shared.m_value <= 0) ? 1 : val2.m_shared.m_value) * val2.m_stack; if (num2 > num) { num = num2; val = val2; } } } if (val == null) { return; } ZNetView component = ((Component)target).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid()) { component.ClaimOwnership(); } ItemData val3 = val.Clone(); if (!st.Loot.AddItem(val3)) { RaiderCampDirector.SetState(zdo, 2); return; } inventory.RemoveItem(val); LootStore.SaveBase64(zdo, "VF_Raider_Loot", st.Loot); int num3 = zdo.GetInt("VF_Raider_Stolen", 0) + 1; zdo.Set("VF_Raider_Stolen", num3); CarryStates.GetValue(character, (Character _) => new CarryBox()).Carrying = true; if (num3 >= ModConfig.MaxStacksPerRaider.Value) { RaiderCampDirector.SetState(zdo, 2); } } } public sealed class MarauderVirtualTask : IVirtualAgentTask { private const float GiveUpSeconds = 180f; public string Id => "VikingFactions.MarauderVirtual"; public NpcRoleFlags RequiredRole => (NpcRoleFlags)131072; public void TickUnloadedZdo(ZDO zdo, NpcRoleFlags roles, float dt) { //IL_0069: 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_0073: 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_00f1: 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_00fb: 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) if (!ModConfig.ModEnabled.Value || !ModConfig.EnableMarauderHeists.Value || !zdo.IsValid() || !zdo.IsOwner()) { return; } int num = zdo.GetInt("VF_Raider_State", 3); if (num == 3) { return; } double num2 = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTimeSeconds() : 0.0); switch (num) { case 0: { Vector3 vec2 = zdo.GetVec3("VF_Raider_TargetPos", zdo.GetPosition()); if (VirtualAgentDirector.MoveZdoAlongTerrain(zdo, vec2, 4.5f, dt, 14f)) { RaiderCampDirector.SetState(zdo, 1); } break; } case 1: if (num2 - (double)zdo.GetFloat("VF_Raider_Since", (float)num2) > 180.0) { RaiderCampDirector.SetState(zdo, 2); } break; case 2: { bool flag = zdo.GetInt("VF_Raider_Stolen", 0) > 0; float num3 = 4.6f * (flag ? ModConfig.RaiderCarrySpeedMultiplier.Value : 1f); Vector3 vec = zdo.GetVec3("VF_Raider_CampPos", zdo.GetPosition()); if (VirtualAgentDirector.MoveZdoAlongTerrain(zdo, vec, num3, dt, 6f)) { if (flag) { RaiderCampDirector.DepositLoot(zdo); } RaiderCampDirector.SetState(zdo, 3); } break; } } } public void ApplyCatchUpOnLoad(Character agent, AgentBehaviorProfile profile, double elapsedOfflineSeconds) { } } public static class RaiderCampDirector { public sealed class ActiveCampRecord { public int CampGuid; public string FactionId = ""; public string FactionDisplayName = ""; public Vector3 CampCenter; public Vector3 RaidTargetPos; public bool MinimapPinned; public readonly List<ZDO> Zdos = new List<ZDO>(); } public const int SpoilsChestWidth = 6; public const int SpoilsChestHeight = 3; private static readonly Dictionary<int, ActiveCampRecord> _camps = new Dictionary<int, ActiveCampRecord>(); public static IReadOnlyDictionary<int, ActiveCampRecord> ActiveCamps => _camps; public static void RegisterCampPiece(ZDO zdo) { //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_0081: 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_0098: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || !zdo.IsValid()) { return; } int num = zdo.GetInt("VF_CampGuid", 0); if (num == 0) { return; } if (!_camps.TryGetValue(num, out ActiveCampRecord value)) { string factionId = zdo.GetString("VF_Raider_Faction", "marauders"); string factionDisplayName = "Raider"; if (RaiderFactionRegistry.TryGetFaction(factionId, out IRaiderFactionDefinition definition)) { factionDisplayName = definition.DisplayName; } value = new ActiveCampRecord { CampGuid = num, FactionId = factionId, FactionDisplayName = factionDisplayName, CampCenter = zdo.GetVec3("VF_Raider_CampPos", zdo.GetPosition()), RaidTargetPos = zdo.GetVec3("VF_Raider_TargetPos", zdo.GetPosition()) }; _camps[num] = value; } if (!value.Zdos.Contains(zdo)) { value.Zdos.Add(zdo); } } public static void SetState(ZDO zdo, int state) { zdo.Set("VF_Raider_State", state); zdo.Set("VF_Raider_Since", ((Object)(object)ZNet.instance != (Object)null) ? ((float)ZNet.instance.GetTimeSeconds()) : 0f); } public static bool TriggerFactionRaid(string factionId, Vector3 settlementPos, out ActiveCampRecord? record) { //IL_007c: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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_00ca: 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_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) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0111: 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_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011a: 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_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: 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_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0185: 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_019d: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0238: 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) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0290: 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_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: 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_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_032a: 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) //IL_0342: 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_0348: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_03c2: Unknown result type (might be due to invalid IL or missing references) //IL_03d6: Unknown result type (might be due to invalid IL or missing references) record = null; if (!ModConfig.ModEnabled.Value || !ModConfig.EnableMarauderHeists.Value) { return false; } if (ZDOMan.instance == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return false; } if (!RaiderFactionRegistry.TryGetAvailableFaction(factionId, out IRaiderFactionDefinition definition) && !RaiderFactionRegistry.TrySelectProgressiveFaction(out definition)) { return false; } float value = ModConfig.CampSpawnMinDistance.Value; float num = Mathf.Max(value + 50f, ModConfig.CampSpawnMaxDistance.Value); if (TryFindReusableCampNearby(definition.FactionId, settlementPos, value * 0.8f, num * 1.2f, out ActiveCampRecord reusable) && ReoccupyCamp(reusable, definition, settlementPos)) { record = reusable; return true; } if (!HospitalityScanner.FindValidLandPoint(settlementPos, value, num, out var pos)) { return false; } int num2 = Random.Range(100000, int.MaxValue); Vector3 val = settlementPos - pos; val.y = 0f; Quaternion val2 = Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up); ActiveCampRecord activeCampRecord = new ActiveCampRecord { CampGuid = num2, FactionId = definition.FactionId, FactionDisplayName = definition.DisplayName, CampCenter = pos, RaidTargetPos = settlementPos }; for (int i = 0; i < definition.CampPieces.Count; i++) { CampPieceEntry campPieceEntry = definition.CampPieces[i]; int stableHashCode = StringExtensionMethods.GetStableHashCode(campPieceEntry.PrefabName); Vector3 val3 = GroundAt(pos + val2 * campPieceEntry.LocalOffset); ZDO val4 = ZDOMan.instance.CreateNewZDO(val3, stableHashCode); val4.Persistent = true; val4.SetPrefab(stableHashCode); val4.SetRotation(val2 * Quaternion.Euler(0f, campPieceEntry.LocalYawDegrees, 0f)); val4.Set("VF_CampGuid", num2); val4.Set("VF_CampState", 0); val4.Set("VF_CampLastRaid", (float)ZNet.instance.GetTimeSeconds()); if (campPieceEntry.IsSpoilsChest) { val4.Set("VF_IsSpoilsChest", true); val4.Set(ZDOVars.s_addedDefaultItems, true); SeedBounty(val4, definition.WarlordBountyTable); } activeCampRecord.Zdos.Add(val4); } SpawnRaider(definition, definition.WarlordPrefabName, GroundAt(pos + val2 * new Vector3(0f, 0f, 1.5f)), val2, num2, pos, settlementPos, 3, activeCampRecord); for (int j = 0; j < definition.CampGuardCount; j++) { float num3 = (float)j / (float)Mathf.Max(1, definition.CampGuardCount) * MathF.PI * 2f; Vector3 pos2 = GroundAt(pos + new Vector3(Mathf.Cos(num3) * 3.5f, 0f, Mathf.Sin(num3) * 3.5f)); SpawnRaider(definition, definition.RaiderPrefabName, pos2, val2, num2, pos, settlementPos, 3, activeCampRecord); } int num4 = Random.Range(definition.MinRaidSize, definition.MaxRaidSize + 1); for (int k = 0; k < num4; k++) { Vector3 pos3 = GroundAt(pos + val2 * new Vector3(((float)k - (float)num4 * 0.5f) * 2f, 0f, -2.5f)); SpawnRaider(definition, definition.RaiderPrefabName, pos3, val2, num2, pos, settlementPos, 0, activeCampRecord); } _camps[num2] = activeCampRecord; record = activeCampRecord; if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)2, definition.DisplayName + " are marching on your stores!", 0, (Sprite)null, false, true); } VikingFactionsPlugin.Log.LogInfo((object)$"[Camps] '{definition.DisplayName}' camp {num2} at {pos:F0}; {num4} raiders -> {settlementPos:F0}."); return true; } private static bool TryFindReusableCampNearby(string factionId, Vector3 center, float minDist, float maxDist, out ActiveCampRecord reusable) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) reusable = null; float num = minDist * minDist; float num2 = maxDist * maxDist; foreach (KeyValuePair<int, ActiveCampRecord> camp in _camps) { ActiveCampRecord value = camp.Value; if (string.Equals(value.FactionId, factionId, StringComparison.OrdinalIgnoreCase)) { float num3 = Utils.DistanceXZ(center, value.CampCenter); num3 *= num3; if (!(num3 < num) && !(num3 > num2) && FindSpoilsChest(value.CampGuid) != null) { reusable = value; return true; } } } return false; } private static bool ReoccupyCamp(ActiveCampRecord camp, IRaiderFactionDefinition faction, Vector3 targetPos) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: 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_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) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: 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_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: 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_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0219: 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) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) camp.RaidTargetPos = targetPos; Vector3 campCenter = camp.CampCenter; Vector3 val = targetPos - campCenter; val.y = 0f; Quaternion val2 = Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up); camp.Zdos.RemoveAll((ZDO z) => z == null || !z.IsValid()); ZDO val3 = FindSpoilsChest(camp.CampGuid); if (val3 != null) { if (LootStore.LoadChest(val3).NrOfItems() == 0) { SeedBounty(val3, faction.WarlordBountyTable); } val3.Set("VF_CampState", 0); val3.Set("VF_CampLastRaid", (float)ZNet.instance.GetTimeSeconds()); } int num = 0; for (int num2 = 0; num2 < camp.Zdos.Count; num2++) { if (camp.Zdos[num2].GetInt("VF_Raider_State", -1) == 3) { num++; } } if (num < faction.CampGuardCount + 1) { SpawnRaider(faction, faction.WarlordPrefabName, GroundAt(campCenter + val2 * new Vector3(0f, 0f, 1.5f)), val2, camp.CampGuid, campCenter, targetPos, 3, camp); int num3 = Mathf.Max(0, faction.CampGuardCount - num); for (int num4 = 0; num4 < num3; num4++) { float num5 = (float)num4 / (float)Mathf.Max(1, num3) * MathF.PI * 2f; Vector3 pos = GroundAt(campCenter + new Vector3(Mathf.Cos(num5) * 3.5f, 0f, Mathf.Sin(num5) * 3.5f)); SpawnRaider(faction, faction.RaiderPrefabName, pos, val2, camp.CampGuid, campCenter, targetPos, 3, camp); } } int num6 = Random.Range(faction.MinRaidSize, faction.MaxRaidSize + 1); for (int num7 = 0; num7 < num6; num7++) { Vector3 pos2 = GroundAt(campCenter + val2 * new Vector3(((float)num7 - (float)num6 * 0.5f) * 2f, 0f, -2.5f)); SpawnRaider(faction, faction.RaiderPrefabName, pos2, val2, camp.CampGuid, campCenter, targetPos, 0, camp); } if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)2, faction.DisplayName + " are rearming at their camp and march again!", 0, (Sprite)null, false, true); } VikingFactionsPlugin.Log.LogInfo((object)$"[Camps] Reoccupied '{faction.DisplayName}' camp {camp.CampGuid} at {campCenter:F0}; {num6} raiders -> {targetPos:F0}."); return true; } private static void SpawnRaider(IRaiderFactionDefinition faction, string prefab, Vector3 pos, Quaternion rot, int campGuid, Vector3 campPos, Vector3 target, int state, ActiveCampRecord rec) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) ZDO val = VirtualAgentDirector.SpawnVirtualAgentZdo(prefab, pos, rot, (NpcRoleFlags)131072, faction.UsesCustomHumanoidRig); if (val != null) { val.Set("VF_CampGuid", campGuid); val.Set("VF_Raider_Faction", faction.FactionId); val.Set("VF_Raider_TargetPos", target); val.Set("VF_Raider_CampPos", campPos); val.Set("VF_Raider_Stolen", 0); val.Set(ZDOVars.s_spawnPoint, campPos); SetState(val, state); rec.Zdos.Add(val); } } private static Vector3 GroundAt(Vector3 p) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (WorldGenerator.instance != null) { p.y = WorldGenerator.instance.GetHeight(p.x, p.z); } return p; } private static void SeedBounty(ZDO chestZdo, IReadOnlyList<BountyLootEntry> table) { if (table == null || table.Count == 0 || (Object)(object)ObjectDB.instance == (Object)null) { return; } Inventory inv = LootStore.NewInventory("SpoilsChest"); for (int i = 0; i < table.Count; i++) { BountyLootEntry bountyLootEntry = table[i]; if (!(Random.value > bountyLootEntry.DropChance)) { LootStore.AddPrefab(inv, bountyLootEntry.ItemPrefabName, Random.Range(bountyLootEntry.MinStack, bountyLootEntry.MaxStack + 1)); } } LootStore.SaveChest(chestZdo, inv); } public static void DepositLoot(ZDO raiderZdo) { //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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) int campGuid = raiderZdo.GetInt("VF_CampGuid", 0); Vector3 vec = raiderZdo.GetVec3("VF_Raider_CampPos", raiderZdo.GetPosition()); Inventory val = LootStore.LoadBase64(raiderZdo, "VF_Raider_Loot"); if (val.NrOfItems() <= 0) { return; } ZDO val2 = FindSpoilsChest(campGuid); if (val2 != null) { val2.SetOwner(ZDOMan.GetSessionID()); Inventory val3 = LootStore.LoadChest(val2); int num = LootStore.MoveAll(val, val3); LootStore.SaveChest(val2, val3); if (num > 0) { VikingFactionsPlugin.Log.LogWarning((object)$"[Camps] Spoils chest full; {num} stolen stack(s) lost."); } } LootStore.SaveBase64(raiderZdo, "VF_Raider_Loot", LootStore.NewInventory()); raiderZdo.Set("VF_Raider_Stolen", 0); RevealCamp(campGuid, vec); } public static ZDO? FindSpoilsChest(int campGuid) { if (ZDOMan.instance == null || campGuid == 0) { return null; } int num = 0; List<ZDO> list = new List<ZDO>(64); bool allZDOsWithPrefabIterative; do { list.Clear(); allZDOsWithPrefabIterative = ZDOMan.instance.GetAllZDOsWithPrefabIterative("piece_chest", list, ref num); for (int i = 0; i < list.Count; i++) { ZDO val = list[i]; if (val.GetBool("VF_IsSpoilsChest", false) && val.GetInt("VF_CampGuid", 0) == campGuid) { return val; } } } while (!allZDOsWithPrefabIterative); return null; } public static void RevealCamp(int campGuid, Vector3 campPos) { //IL_004c: 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_005c: Unknown result type (might be due to invalid IL or missing references) if (_camps.TryGetValue(campGuid, out ActiveCampRecord value)) { if (value.MinimapPinned) { return; } value.MinimapPinned = true; } if ((Object)(object)Minimap.instance != (Object)null) { string text = ((value != null) ? (value.FactionDisplayName + " Camp") : "Raider Camp"); Minimap.instance.AddPin(campPos, (PinType)9, text, true, false, 0L, default(PlatformUserID)); } if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)2, "Raiders escaped with your spoils! Their camp is marked on your map.", 0, (Sprite)null, false, true); } } public static int ClearAllCamps() { int num = 0; foreach (ActiveCampRecord value in _camps.Values) { for (int i = 0; i < value.Zdos.Count; i++) { ZDO val = value.Zdos[i]; if (val != null && val.IsValid() && ZDOMan.instance != null) { ZDOMan.instance.DestroyZDO(val); num++; } } } _camps.Clear(); return num; } } public readonly struct CampPieceEntry { public readonly string PrefabName; public readonly Vector3 LocalOffset; public readonly float LocalYawDegrees; public readonly bool IsSpoilsChest; public CampPieceEntry(string prefabName, Vector3 localOffset, float localYawDegrees = 0f, bool isSpoilsChest = false) { //IL_0008: 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) PrefabName = prefabName; LocalOffset = localOffset; LocalYawDegrees = localYawDegrees; IsSpoilsChest = isSpoilsChest; } } public readonly struct BountyLootEntry { public readonly string ItemPrefabName; public readonly int MinStack; public readonly int MaxStack; public readonly float DropChance; public BountyLootEntry(string itemPrefabName, int minStack, int maxStack, float dropChance = 1f) { ItemPrefabName = itemPrefabName; MinStack = minStack; MaxStack = maxStack; DropChance = dropChance; } } public interface IRaiderFactionDefinition { string FactionId { get; } string DisplayName { get; } string RaiderPrefabName { get; } string WarlordPrefabName { get; } bool UsesCustomHumanoidRig { get; } IReadOnlyList<string> RequiredGlobalKeys { get; } IReadOnlyList<string> ForbiddenGlobalKeys { get; } int MinRaidSize { get; } int MaxRaidSize { get; } int CampGuardCount { get; } string[] RaiderDefaultEquipment { get; } string[] WarlordDefaultEquipment { get; } IReadOnlyList<CampPieceEntry> CampPieces { get; } IReadOnlyList<BountyLootEntry> WarlordBountyTable { get; } IReadOnlyList<string> InterceptedVanillaEvents { get; } } public sealed class RaiderFactionDefinition : IRaiderFactionDefinition { public string FactionId { get; set; } = ""; public string DisplayName { get; set; } = ""; public string RaiderPrefabName { get; set; } = ""; public string WarlordPrefabName { get; set; } = ""; public bool UsesCustomHumanoidRig { get; set; } public IReadOnlyList<string> RequiredGlobalKeys { get; set; } = Array.Empty<string>(); public IReadOnlyList<string> ForbiddenGlobalKeys { get; set; } = Array.Empty<string>(); public int MinRaidSize { get; set; } = 3; public int MaxRaidSize { get; set; } = 4; public int CampGuardCount { get; set; } = 2; public string[] RaiderDefaultEquipment { get; set; } = Array.Empty<string>(); public string[] WarlordDefaultEquipment { get; set; } = Array.Empty<string>(); public IReadOnlyList<CampPieceEntry> CampPieces { get; set; } = Array.Empty<CampPieceEntry>(); public IReadOnlyList<BountyLootEntry> WarlordBountyTable { get; set; } = Array.Empty<BountyLootEntry>(); public IReadOnlyList<string> InterceptedVanillaEvents { get; set; } = Array.Empty<string>(); } public static class RaiderFactionRegistry { public const string MarauderPrefabName = "VF_Marauder"; public const string WarlordPrefabName = "VF_Warlord"; private static readonly Dictionary<string, IRaiderFactionDefinition> _factions = new Dictionary<string, IRaiderFactionDefinition>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, IRaiderFactionDefinition> _vanillaEventMap = new Dictionary<string, IRaiderFactionDefinition>(StringComparer.OrdinalIgnoreCase); public static void RegisterFaction(IRaiderFactionDefinition definition) { if (definition == null || string.IsNullOrWhiteSpace(definition.FactionId)) { return; } _factions[definition.FactionId] = definition; if (definition.InterceptedVanillaEvents == null) { return; } for (int i = 0; i < definition.InterceptedVanillaEvents.Count; i++) { string text = definition.InterceptedVanillaEvents[i]; if (!string.IsNullOrWhiteSpace(text)) { _vanillaEventMap[text] = definition; } } } public static bool TryGetFaction(string factionId, out IRaiderFactionDefinition definition) { return _factions.TryGetValue(factionId, out definition); } public static bool TryGetFactionForVanillaEvent(string eventName, out IRaiderFactionDefinition definition) { return _vanillaEventMap.TryGetValue(eventName, out definition); } public static bool IsFactionAvailable(IRaiderFactionDefinition definition) { if (definition == null) { return false; } if ((Object)(object)ZoneSystem.instance == (Object)null) { return true; } if (definition.RequiredGlobalKeys != null) { for (int i = 0; i < definition.RequiredGlobalKeys.Count; i++) { string text = definition.RequiredGlobalKeys[i]; if (!string.IsNullOrWhiteSpace(text) && !ZoneSystem.instance.GetGlobalKey(text)) { return false; } } } if (definition.ForbiddenGlobalKeys != null) { for (int j = 0; j < definition.ForbiddenGlobalKeys.Count; j++) { string text2 = definition.ForbiddenGlobalKeys[j]; if (!string.IsNullOrWhiteSpace(text2) && ZoneSystem.instance.GetGlobalKey(text2)) { return false; } } } return true; } public static bool TryGetAvailableFaction(string factionId, out IRaiderFactionDefinition definition) { definition = null; if (!TryGetFaction(factionId, out IRaiderFactionDefinition definition2)) { return false; } if (!IsFactionAvailable(definition2)) { return false; } definition = definition2; return true; } public static bool TrySelectProgressiveFaction(out IRaiderFactionDefinition chosen) { chosen = null; List<IRaiderFactionDefinition> list = new List<IRaiderFactionDefinition>(); foreach (IRaiderFactionDefinition value in _factions.Values) { if (IsFactionAvailable(value)) { list.Add(value); } } if (list.Count == 0) { return false; } list.Sort((IRaiderFactionDefinition a, IRaiderFactionDefinition b) => (b.RequiredGlobalKeys?.Count ?? 0).CompareTo(a.RequiredGlobalKeys?.Count ?? 0)); if (list.Count > 1 && Random.value < 0.3f) { chosen = list[Random.Range(1, list.Count)]; } else { chosen = list[0]; } return true; } public static IEnumerable<IRaiderFactionDefinition> GetAllFactions() { return _factions.Values; } internal static void RegisterBuiltInFactions() { //IL_00e3: 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_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: 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_0210: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: 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_0431: Unknown result type (might be due to invalid IL or missing references) //IL_045c: Unknown result type (might be due to invalid IL or missing references) //IL_0487: Unknown result type (might be due to invalid IL or missing references) //IL_04b2: 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_0508: Unknown result type (might be due to invalid IL or missing references) //IL_0533: Unknown result type (might be due to invalid IL or missing references) //IL_0652: Unknown result type (might be due to invalid IL or missing references) //IL_067d: 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_06d3: Unknown result type (might be due to invalid IL or missing references) //IL_06fe: Unknown result type (might be due to invalid IL or missing references) //IL_0729: Unknown result type (might be due to invalid IL or missing references) //IL_0754: Unknown result type (might be due to invalid IL or missing references) //IL_077f: Unknown result type (might be due to invalid IL or missing references) RaiderFactionDefinition raiderFactionDefinition = new RaiderFactionDefinition(); raiderFactionDefinition.FactionId = "marauders"; raiderFactionDefinition.DisplayName = "Exile Marauders"; raiderFactionDefinition.RaiderPrefabName = "VF_Marauder"; raiderFactionDefinition.WarlordPrefabName = "VF_Warlord"; raiderFactionDefinition.UsesCustomHumanoidRig = true; raiderFactionDefinition.RequiredGlobalKeys = Array.Empty<string>(); raiderFactionDefinition.ForbiddenGlobalKeys = Array.Empty<string>(); raiderFactionDefinition.MinRaidSize = 3; raiderFactionDefinition.MaxRaidSize = 4; raiderFactionDefinition.CampGuardCount = 2; raiderFactionDefinition.RaiderDefaultEquipment = new string[5] { "ArmorLeatherChest", "ArmorLeatherLegs", "HelmetLeather", "AxeBronze", "ShieldWood" }; raiderFactionDefinition.WarlordDefaultEquipment = new string[6] { "ArmorIronChest", "ArmorIronLegs", "HelmetIron", "CapeWolf", "SwordIron", "ShieldBanded" }; raiderFactionDefinition.CampPieces = new CampPieceEntry[13] { new CampPieceEntry("bonfire", Vector3.zero), new CampPieceEntry("piece_chest", new Vector3(0f, 0f, 2.8f), 180f, isSpoilsChest: true), new CampPieceEntry("piece_chair02", new Vector3(-2.4f, 0f, 0.5f), 90f), new CampPieceEntry("piece_chair02", new Vector3(2.4f, 0f, 0.5f), -90f), new CampPieceEntry("piece_banner01", new Vector3(-2.2f, 0f, 3f), 160f), new CampPieceEntry("piece_banner01", new Vector3(2.2f, 0f, 3f), -160f), new CampPieceEntry("wood_wall_roof_45", new Vector3(0f, 0f, 3.4f)), new CampPieceEntry("stake_wall", new Vector3(-4.5f, 0f, 4.5f), 45f), new CampPieceEntry("stake_wall", new Vector3(4.5f, 0f, 4.5f), -45f), new CampPieceEntry("stake_wall", new Vector3(-5.5f, 0f, 0f), 90f), new CampPieceEntry("stake_wall", new Vector3(5.5f, 0f, 0f), -90f), new CampPieceEntry("piece_groundtorch_wood", new Vector3(-3f, 0f, -3.5f)), new CampPieceEntry("piece_groundtorch_wood", new Vector3(3f, 0f, -3.5f)) }; raiderFactionDefinition.WarlordBountyTable = new BountyLootEntry[5] { new BountyLootEntry("Coins", 35, 85), new BountyLootEntry("Ruby", 2, 5, 0.85f), new BountyLootEntry("AmberPearl", 2, 6, 0.85f), new BountyLootEntry("SilverNecklace", 1, 2, 0.65f), new BountyLootEntry("Iron", 4, 10, 0.75f) }; raiderFactionDefinition.InterceptedVanillaEvents = new string[3] { "army_eikthyr", "army_theelder", "skeletons" }; RegisterFaction(raiderFactionDefinition); raiderFactionDefinition = new RaiderFactionDefinition(); raiderFactionDefinition.FactionId = "draugr"; raiderFactionDefinition.DisplayName = "Draugr Reavers"; raiderFactionDefinition.RaiderPrefabName = "Draugr"; raiderFactionDefinition.WarlordPrefabName = "Draugr_Elite"; raiderFactionDefinition.UsesCustomHumanoidRig = false; raiderFactionDefinition.RequiredGlobalKeys = new string[1] { "defeated_gdking" }; raiderFactionDefinition.ForbiddenGlobalKeys = Array.Empty<string>(); raiderFactionDefinition.MinRaidSize = 3; raiderFactionDefinition.MaxRaidSize = 4; raiderFactionDefinition.CampGuardCount = 2; raiderFactionDefinition.CampPieces = new CampPieceEntry[7] { new CampPieceEntry("fire_pit", Vector3.zero), new CampPieceEntry("piece_chest", new Vector3(0f, 0f, 2.6f), 180f, isSpoilsChest: true), new CampPieceEntry("stone_wall_2x1", new Vector3(0f, 0f, 4f)), new CampPieceEntry("stone_wall_2x1", new Vector3(-3.2f, 0f, 2.5f), 60f), new CampPieceEntry("stone_wall_2x1", new Vector3(3.2f, 0f, 2.5f), -60f), new CampPieceEntry("piece_groundtorch_green", new Vector3(-2.6f, 0f, -2.6f)), new CampPieceEntry("piece_groundtorch_green", new Vector3(2.6f, 0f, -2.6f)) }; raiderFactionDefinition.WarlordBountyTable = new BountyLootEntry[4] { new BountyLootEntry("Entrails", 6, 12), new BountyLootEntry("IronScrap", 6, 14, 0.9f), new BountyLootEntry("Chain", 1, 3, 0.65f), new BountyLootEntry("Coins", 25, 60, 0.85f) }; raiderFactionDefinition.InterceptedVanillaEvents = new string[1] { "army_bonemass" }; RegisterFaction(raiderFactionDefinition); raiderFactionDefinition = new RaiderFactionDefinition(); raiderFactionDefinition.FactionId = "fulings"; raiderFactionDefinition.DisplayName = "Fuling Plunderers"; raiderFactionDefinition.RaiderPrefabName = "Goblin"; raiderFactionDefinition.WarlordPrefabName = "GoblinBrute"; raiderFactionDefinition.UsesCustomHumanoidRig = false; raiderFactionDefinition.RequiredGlobalKeys = new string[1] { "defeated_dragon" }; raiderFactionDefinition.ForbiddenGlobalKeys = Array.Empty<string>(); raiderFactionDefinition.MinRaidSize = 4; raiderFactionDefinition.MaxRaidSize = 5; raiderFactionDefinition.CampGuardCount = 3; raiderFactionDefinition.CampPieces = new CampPieceEntry[8] { new CampPieceEntry("bonfire", Vector3.zero), new CampPieceEntry("piece_chest", new Vector3(0f, 0f, 3f), 180f, isSpoilsChest: true), new CampPieceEntry("stake_wall", new Vector3(-4.2f, 0f, 4.2f), 45f), new CampPieceEntry("stake_wall", new Vector3(4.2f, 0f, 4.2f), -45f), new CampPieceEntry("stake_wall", new Vector3(-5.2f, 0f, 0f), 90f), new CampPieceEntry("stake_wall", new Vector3(5.2f, 0f, 0f), -90f), new CampPieceEntry("piece_groundtorch_wood", new Vector3(-3f, 0f, -3.2f)), new CampPieceEntry("piece_groundtorch_wood", new Vector3(3f, 0f, -3.2f)) }; raiderFactionDefinition.WarlordBountyTable = new BountyLootEntry[4] { new BountyLootEntry("BlackMetalScrap", 8, 18), new BountyLootEntry("Coins", 60, 140), new BountyLootEntry("Needle", 5, 12, 0.75f), new BountyLootEntry("LinenThread", 4, 10, 0.7f) }; raiderFactionDefinition.InterceptedVanillaEvents = new string[1] { "army_goblin" }; RegisterFaction(raiderFactionDefinition); } } } namespace VikingFactions.Core { public static class AiHelpers { private static readonly MethodInfo _moveTo = AccessTools.Method(typeof(BaseAI), "MoveTo", (Type[])null, (Type[])null); private static readonly MethodInfo _lookAt = AccessTools.Method(typeof(BaseAI), "LookAt", (Type[])null, (Type[])null); private static readonly object[] _moveArgs = new object[4]; private static readonly object[] _lookArgs = new object[1]; public static bool MoveTo(BaseAI ai, float dt, Vector3 point, float dist, bool run) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) _moveArgs[0] = dt; _moveArgs[1] = point; _moveArgs[2] = dist; _moveArgs[3] = run; if (_moveTo != null) { return (bool)_moveTo.Invoke(ai, _moveArgs); } return false; } public static void LookAt(BaseAI ai, Vector3 point) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) _lookArgs[0] = point; _lookAt?.Invoke(ai, _lookArgs); } public static void Say(Character speaker, string text) { //IL_0022: 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) if (!((Object)(object)Chat.instance == (Object)null) && !((Object)(object)speaker == (Object)null)) { Chat.instance.SetNpcText(((Component)speaker).gameObject, Vector3.up * 1.8f, 20f, 6f, speaker.m_name, text, false); } } public static bool PlayerNear(Vector3 pos, float radius) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //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) List<Player> allPlayers = Player.GetAllPlayers(); float num = radius * radius; for (int i = 0; i < allPlayers.Count; i++) { if ((Object)(object)allPlayers[i] != (Object)null) { Vector3 val = ((Component)allPlayers[i]).transform.position - pos; if (((Vector3)(ref val)).sqrMagnitude <= num) { return true; } } } return false; } } public static class FactionRoles { public const NpcRoleFlags Wayfarer = (NpcRoleFlags)65536u; public const NpcRoleFlags Marauder = (NpcRoleFlags)131072u; } public static class FactionKeys { public const string WfState = "VF_WF_State"; public const string WfTargetPos = "VF_WF_TargetPos"; public const string WfOriginPos = "VF_WF_OriginPos"; public const string WfLastMeal = "VF_WF_LastMeal"; public const string WfMood = "VF_WF_Mood"; public const string WfSince = "VF_WF_Since"; public const string CampGuid = "VF_CampGuid"; public const string CampState = "VF_CampState"; public const string CampLastRaidTime = "VF_CampLastRaid"; public const string IsSpoilsChest = "VF_IsSpoilsChest"; public const string RaiderState = "VF_Raider_State"; public const string RaiderTargetPos = "VF_Raider_TargetPos"; public const string RaiderCampPos = "VF_Raider_CampPos"; public const string RaiderFaction = "VF_Raider_Faction"; public const string RaiderStolen = "VF_Raider_Stolen"; public const string RaiderLoot = "VF_Raider_Loot"; public const string RaiderSince = "VF_Raider_Since"; } public static class LootStore { public const int Width = 6; public const int Height = 3; public static Inventory NewInventory(string name = "VF_Loot") { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown return new Inventory(name, (Sprite)null, 6, 3); } public static Inventory LoadBase64(ZDO zdo, string key) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown Inventory val = NewInventory(); string text = zdo.GetString(key, ""); if (!string.IsNullOrEmpty(text)) { try { val.Load(new ZPackage(text)); } catch (Exception ex) { VikingFactionsPlugin.Log.LogWarning((object)("[LootStore] Failed to load '" + key + "': " + ex.Message)); } } return val; } public static void SaveBase64(ZDO zdo, string key, Inventory inv) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown if (inv.NrOfItems() == 0) { zdo.Set(key, ""); return; } ZPackage val = new ZPackage(); inv.Save(val); zdo.Set(key, val.GetBase64()); } public static Inventory LoadChest(ZDO chestZdo) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown Inventory val = NewInventory("SpoilsChest"); byte[] byteArray = chestZdo.GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray != null && byteArray.Length != 0) { try { val.Load(new ZPackage(byteArray)); } catch (Exception ex) { VikingFactionsPlugin.Log.LogWarning((object)("[LootStore] Failed to load chest: " + ex.Message)); } } return val; } public static void SaveChest(ZDO chestZdo, Inventory inv) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); inv.Save(val); chestZdo.Set(ZDOVars.s_items, val.GetArray()); } public static bool AddPrefab(Inventory inv, string prefabName, int stack) { if ((Object)(object)ObjectDB.instance == (Object)null) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName); if ((Object)(object)itemPrefab == (Object)null || (Object)(object)itemPrefab.GetComponent<ItemDrop>() == (Object)null) { return false; } return inv.AddItem(itemPrefab, stack); } public static int MoveAll(Inventory source, Inventory dest) { int num = 0; List<ItemData> list = new List<ItemData>(source.GetAllItems()); for (int i = 0; i < list.Count; i++) { ItemData val = list[i].Clone(); if (!dest.AddItem(val)) { num++; } } return num; } } } namespace VikingFactions.Configuration { public class ConfigurationManagerAttributes { public bool? Browsable; public string? Category; public bool? DefaultValue; public bool? HideDefaultButton; public bool? HideSettingName; public string? Description; public int? Order; public bool? ReadOnly; public bool? IsAdvanced; public bool? IsAdminOnly; } public static class ModConfig { public static ConfigEntry<bool> ModEnabled { get; private set; } public static ConfigEntry<bool> EnableWayfarers { get; private set; } public static ConfigEntry<int> MinHospitalityComfort { get; private set; } public static ConfigEntry<int> ComfortPerWayfarerCap { get; private set; } public static ConfigEntry<float> WayfarerSpawnIntervalSeconds { get; private set; } public static ConfigEntry<float> WayfarerMealIntervalSeconds { get; private set; } public static ConfigEntry<bool> EnableMarauderHeists { get; private set; } public static ConfigEntry<bool> InterceptVanillaRaids { get; private set; } public static ConfigEntry<int> MaxStacksPerRaider { get; private set; } public static ConfigEntry<float> RaiderSiphonSecondsPerStack { get; private set; } public static ConfigEntry<float> RaiderCarrySpeedMultiplier { get; private set; } public static ConfigEntry<bool> RaidersRespectWards { get; private set; } public static ConfigEntry<float> CampSpawnMinDistance { get; private set; } public static ConfigEntry<float> CampSpawnMaxDistance { get; private set; } public static void Init(ConfigFile config) { ModEnabled = Bind(config, "1 - General", "Enabled", defaultValue: true, "Master switch to enable or disable all features of VikingFactions.", 100, null, isAdminOnly: true); EnableWayfarers = Bind(config, "2 - Wayfarers & Mead Hall", "EnableWayfarers", defaultValue: true, "Enable friendly Wayfarer travelers to visit qualifying Mead Halls and settlements.", 90, null, isAdminOnly: true); MinHospitalityComfort = Bind(config, "2 - Wayfarers & Mead Hall", "MinHospitalityComfort", 4, "Minimum comfort level near a lit hearth with roof cover required to attract Wayfarers.", 80, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 20), isAdminOnly: true); ComfortPerWayfarerCap = Bind(config, "2 - Wayfarers & Mead Hall", "ComfortPerWayfarerCap", 3, "Comfort points required per simultaneous Wayfarer guest (e.g. Comfort 12 allows up to 4 Wayfarers).", 70, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 10), isAdminOnly: true); WayfarerSpawnIntervalSeconds = Bind(config, "2 - Wayfarers & Mead Hall", "WayfarerSpawnIntervalSeconds", 300f, "Minimum interval in seconds between new Wayfarer arrivals at a qualifying Mead Hall.", 60, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(30f, 3600f), isAdminOnly: true); WayfarerMealIntervalSeconds = Bind(config, "2 - Wayfarers & Mead Hall", "WayfarerMealIntervalSeconds", 1200f, "Interval in seconds between Wayfarer meals consumed from nearby communal chests.", 50, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(120f, 7200f), isAdminOnly: true); EnableMarauderHeists = Bind(config, "3 - Marauders & Retaliation Camps", "EnableMarauderHeists", defaultValue: true, "Enable Marauder warbands, heist chest siphoning, and procedural retaliation camps.", 90, null, isAdminOnly: true); InterceptVanillaRaids = Bind(config, "3 - Marauders & Retaliation Camps", "InterceptVanillaRaids", defaultValue: true, "Convert eligible vanilla base raid events into physicalized VikingFactions camps and marching warbands.", 80, null, isAdminOnly: true); MaxStacksPerRaider = Bind(config, "3 - Marauders & Retaliation Camps", "MaxStacksPerRaider", 3, "Maximum number of item stacks a single raider can siphon before retreating to camp.", 70, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 10), isAdminOnly: true); RaiderSiphonSecondsPerStack = Bind(config, "3 - Marauders & Retaliation Camps", "RaiderSiphonSecondsPerStack", 4.5f, "Seconds a raider must channel at an unlocked chest to siphon one stack of items.", 60, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 20f), isAdminOnly: true); RaiderCarrySpeedMultiplier = Bind(config, "3 - Marauders & Retaliation Camps", "RaiderCarrySpeedMultiplier", 0.85f, "Movement speed multiplier applied to raiders carrying stolen loot.", 50, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.3f, 1f), isAdminOnly: true); RaidersRespectWards = Bind(config, "3 - Marauders & Retaliation Camps", "RaidersRespectWards", defaultValue: true, "If true, raiders will not siphon chests that lie inside an active player ward area.", 45, null, isAdminOnly: true); CampSpawnMinDistance = Bind(config, "3 - Marauders & Retaliation Camps", "CampSpawnMinDistance", 250f, "Minimum distance in meters from the target settlement to spawn a procedural raider camp.", 40, (AcceptableValueBase?)(object)new AcceptableValueRan