Decompiled source of FateBound v0.4.4
plugins/com.quaree.fatebound.dll
Decompiled 12 hours 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.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using UnityEngine; using UnityEngine.Rendering; [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("com.quaree.fatebound")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.4.4")] [assembly: AssemblyInformationalVersion("0.4.4")] [assembly: AssemblyProduct("com.quaree.fatebound")] [assembly: AssemblyTitle("com.quaree.fatebound")] [assembly: AssemblyVersion("0.4.4.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } } namespace FateBound { public enum FateDifficulty { Easy, Normal, Hell, Custom } public enum FateChainTopology { Ring, NearestWeb, AllPairs } [BepInPlugin("com.quaree.fatebound", "命运绑定", "0.4.4")] public class Plugin : BaseUnityPlugin { public const string Guid = "com.quaree.fatebound"; public const string PluginName = "命运绑定"; public const string PluginVersion = "0.4.4"; public static Plugin Instance; public static ConfigEntry<bool> Enabled; public static ConfigEntry<bool> ToggleByHost; internal static bool ForceSyncNow; internal static ConfigFile PluginConfig; private static string cfgLastText; private static float cfgPollAt; private static FateDifficulty cfgLastDifficulty; private static bool cfgWatched; public static ConfigEntry<KeyCode> ToggleKey; public static ConfigEntry<FateDifficulty> Difficulty; public static ConfigEntry<float> MaxDistance; public static ConfigEntry<float> GraceSeconds; public static ConfigEntry<float> PoisonPerSecond; public static ConfigEntry<float> ShareRatio; public static ConfigEntry<bool> ShareHunger; public static ConfigEntry<bool> ShareInjury; public static ConfigEntry<bool> SharePoison; public static ConfigEntry<bool> DrawChain; public static ConfigEntry<float> ChainWidth; public static ConfigEntry<bool> RequireTwoPlayers; public static ConfigEntry<bool> DisableInAirport; public static ConfigEntry<bool> NotifyShared; public static ConfigEntry<KeyCode> DebugKey; internal static ConfigEntry<bool> SelfCheckPanel; internal static ConfigEntry<int> FarChainInterval; internal static ConfigEntry<int> MeshInterval; internal static ConfigEntry<bool> HardTether; internal static ConfigEntry<float> HardLimitScale; internal static ConfigEntry<float> HardPullForce; internal static ConfigEntry<bool> LooseCollect; public static ConfigEntry<bool> ChainTautFeedback; public static ConfigEntry<string> ChainColor; public static ConfigEntry<KeyCode> ChainColorCycleKey; public static bool DebugRequested; public static ConfigEntry<bool> ChainPhysics; public static ConfigEntry<float> ChainSpacing; public static ConfigEntry<float> ChainThickness; public static ConfigEntry<float> ChainSlack; public static ConfigEntry<float> ChainMaxSag; public static ConfigEntry<float> ChainHideRadius; public static ConfigEntry<int> ChainMaxPoints; public static ConfigEntry<FateChainTopology> ChainTopology; public static ConfigEntry<bool> ChainLinkTexture; public static ConfigEntry<float> ChainTextureScale; public static ConfigEntry<bool> SolidChainLinks; public static ConfigEntry<float> LinkDiameter; public static ConfigEntry<float> LinkWire; public static ConfigEntry<bool> ChainMemory; public static ConfigEntry<bool> SpringPull; public static ConfigEntry<float> PullDamping; public static ConfigEntry<bool> CampfireShuffle; public static ConfigEntry<bool> SyncFromHost; public static ConfigEntry<float> SyncIntervalSeconds; public static ConfigEntry<bool> EnableTug; public static ConfigEntry<float> TugStartDistance; public static ConfigEntry<float> ChainTautRatio; public static ConfigEntry<float> MaxPullForce; public static ConfigEntry<float> MaxDragForce; public static ConfigEntry<float> DragSpeedDiff; public static ConfigEntry<float> VerticalPullScale; public static ConfigEntry<float> DragRampSeconds; public static ConfigEntry<bool> EnablePull; public static ConfigEntry<KeyCode> PullKey; public static ConfigEntry<float> PullForce; public static ConfigEntry<float> PullStaminaPerSecond; public static ConfigEntry<float> PullMinDrop; public static ConfigEntry<float> PullMaxDistance; public static ConfigEntry<float> PullMinStamina; public static ConfigEntry<bool> PullNotify; public static ConfigEntry<bool> PullAimRequired; public static ConfigEntry<float> PullAimAngle; public static ConfigEntry<bool> AimHighlight; private static bool hostDrawChain; private static bool hostVisualsKnown; internal static readonly string[][] ChainColorPresets = new string[8][] { new string[2] { "#C08A4A", "古铜" }, new string[2] { "#9AA3AD", "钢银" }, new string[2] { "#E0C36B", "暗金" }, new string[2] { "#B22222", "深红" }, new string[2] { "#3CB371", "翠绿" }, new string[2] { "#5B8BD4", "湖蓝" }, new string[2] { "#8A6FD1", "紫晶" }, new string[2] { "#2E2E2E", "玄铁黑" } }; private static readonly Color ChainColorFallback = new Color(0.753f, 0.541f, 0.29f); private static string colorCacheKey; private static Color colorCacheVal = ChainColorFallback; private const int SYNC_MAGIC = 1178753841; internal static ConfigEntry<float>[] SyncFloat; internal static ConfigEntry<bool>[] SyncBool; internal static ConfigEntry<int>[] SyncInt; private void Awake() { //IL_0a19: Unknown result type (might be due to invalid IL or missing references) //IL_0a67: Unknown result type (might be due to invalid IL or missing references) Instance = this; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定", "启用", true, "总开关。false 时这个 mod 完全不干预游戏。也可以用下面的快捷键在游戏里切换。"); ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("命运绑定", "启停快捷键", (KeyCode)290, "切换命运锁链开关的快捷键(只影响你自己的客户端)。\n改成 None 可以彻底关掉快捷键。默认 F9 —— 原来那个 Chained 模组用的是 F10,\n两个都装的话 F9 / F10 各管一个,不会打架。"); Difficulty = ((BaseUnityPlugin)this).Config.Bind<FateDifficulty>("命运绑定", "难度", FateDifficulty.Normal, "难度档位:\nEasy = 宽松:50 米 / 中毒 0.02 / 共享伤害 0.6\nNormal = 标准:30 米 / 中毒 0.035 / 共享伤害 1.0\nHell = 硬核:15 米 / 中毒 0.07 / 共享伤害 1.0\nCustom = 自定义:不动下面那三项,完全按你自己填的值来。\n注意:选前三档时,每次启动都会用档位数值覆盖「最大锁链距离 / 中毒速度 / 共享伤害比例」。\n所以如果你想手动调这三项,请把难度设成 Custom,否则下次进游戏就被改回去。\n改完配置文件需要重启游戏才生效。"); MaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定", "最大锁链距离", 30f, "命运锁链的最大距离(米)。超过这个距离,掉队的人才会开始累积中毒。"); GraceSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定", "超距宽限秒数", 5f, "掉队后先给几秒宽限,超过这个时间才开始累积中毒。防止短暂分开就被罚。"); PoisonPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定", "中毒速度", 0.035f, "掉队时每秒累积的中毒量。注意中毒是 0~1 的量纲:0.035 = 每秒 3.5%,\n大约 29 秒累积满(游戏自身的中毒自然衰减还会再拖慢它)。\n嫌慢就调大,嫌狠就调小(例如 0.015)。"); ShareRatio = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定", "共享伤害比例", 1f, "共享强度 0~1。1 = 完全共享(那份恶化增量整份补给全队),0 = 不共享。\n注意共享只传播「恶化」:一个人变饿会把全队一起带饿,\n但谁吃东西只回自己的饥饿值(PEAK 只允许改自己的角色,没法替别人加状态)。"); ShareHunger = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定", "共享饥饿", true, "共享饱食度:一个人饿,全队一起饿。"); ShareInjury = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定", "共享受伤", true, "共享伤害:游戏里产生的受伤(摔伤等)全队同担。"); SharePoison = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定", "共享中毒", false, "是否也共享中毒。默认 false —— 掉队的中毒只由掉队的那个人承担,其余人不受影响。"); DrawChain = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定", "绘制链条", true, "在玩家之间画出命运锁链的连线。"); ChainWidth = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定", "链条粗细", 0.13f, "锁链的渲染宽度(米)。这是链条看起来像不像「一根真链条」的关键 ——\n0.08 细得像一条白线,0.13 才有链条的实体感。想要更粗就继续加,\n但它同时决定了贴图的花纹密集程度,配合下面的「贴图密度」一起调。"); RequireTwoPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定", "需两人以上", true, "少于 2 名真人玩家时,命运锁链不生效(保证必须联机才能玩)。"); DisableInAirport = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定", "大厅内不生效", true, "在机场(游戏大厅)阶段完全不生效:不画锁链、不共享状态、不掉队惩罚、不施力。\n改成 false 则大厅里也会绑上锁链。"); NotifyShared = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定", "共享时弹提示", true, "共享状态真正生效时,在游戏内弹一条提示(例:命运锁链:队友受伤,你被分摊 -12%)。\n默认开。关掉就回到以前那种「悄无声息」。\n这是每台机器自己的显示偏好,不参与主机同步。"); ToggleByHost = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定", "启停由房主统一控制", true, "开启后,「启用」这个开关由房主统一控制:\n房主按 F9 → 全队一起开 / 一起关;客机按 F9 无效(会提示是房主在控制)。\n这是为了防止有人为了跑快点,自己把锁链一关就溜了。\n关掉它 = 回到「每个玩家各管各的」。默认开。\n说明:这挡的是「手滑和赖皮」,不是反作弊 —— 铁了心改配置文件的人一样能绕过去。"); DebugKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("命运绑定", "调试读数键", (KeyCode)291, "按这个键打印一份调试读数:全队每个人的受伤/饥饿/中毒数值、以及和你的距离。\n会同时弹在游戏内通知栏,并追加写入 BepInEx\\fatebound-debug.txt。\n用途:确认「共享到底有没有生效」。填 None 可关闭。不参与主机同步。\n【v0.4.3 起】按 F10 除了写这份读数,还会开关左上角那块常驻自检面板 ——\n常驻面板才能一直看着「链条有没有画、拉人卡在哪一步」。"); SelfCheckPanel = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定", "自检面板", false, "在屏幕左上角常驻一块自检面板(按 F10 开关)。\n上面实时写着:按键走的是哪条通道、右键有没有被读到、房间几个人、\n本帧画了几条链、我连着谁、以及拉人的每一道判定分别过没过。\n一个人也能用它确认模组到底跑到哪一步了 —— 不用等第二个队友。\n这是每台机器自己的显示偏好,不参与主机同步。"); HardTether = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.硬限制", "硬性拉回", true, "超过硬限就把正在远离的那个人拽回来 —— 真的跑不掉。\n和上面的「拖拽」区别:拖拽是软性的(力度封顶、要几秒才渐满),\n同队里有人一路狂奔时,软拖拽追不上他;这一项是硬兜底。\n施力是「谁在远离,谁自己的机器就把自己拽回来」,所以躲不掉。\n刻意保留成可关:真需要爬长绳、跳远台的时候,硬拉回会觉得碍事。"); HardLimitScale = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.硬限制", "硬限倍数", 1f, "硬限 = 最大锁链距离 × 这个倍数。1.0 = 一超过锁链距离就硬拽。\n想给一点缓冲就填 1.2、1.5。"); LooseCollect = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定", "宽松采集队友", false, "【排障用】采集可连对象时,默认会剔掉三类角色:机器人(isBot)、灵魂态(Ghost)、未激活的。\n如果面板上「可连角色」永远只有 1 人、可游戏里明明有好几个队友,\n那就是这三道剔除误伤了 —— 把它打开即可全部收进来。\n正常联机时保持关闭即可;它只是给「队友收不进来」留的一个后门。"); HardPullForce = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.硬限制", "硬拉回力度", 520f, "硬拉回的力度(米/秒²量级)。越大越像一堵墙。\n520 大约是「跑起来明显被拽住、但不会被甩飞」。\n嫌不够硬就往上加,嫌人被打得乱晃就往下调。"); FarChainInterval = ((BaseUnityPlugin)this).Config.Bind<int>("命运绑定.性能", "远处链条刷新间隔", 3, "【性能】不是我参与的链条,物理求解几帧算一次。1 = 每帧(最卡)。\n3 人以上时会同时存在最多 4 条链,其中只有 1~2 条挂在自己身上 ——\n把不挂在自己身上的那些降频,是最省的一件事,画面几乎看不出差别。\n填 1 可关掉这项优化(回到最费的模式,用于排查)。"); MeshInterval = ((BaseUnityPlugin)this).Config.Bind<int>("命运绑定.性能", "链环网格刷新间隔", 2, "【性能】链环的立体模型几帧重建一次。1 = 每帧(最卡)。\n链环网格每帧重建是这个模组里最费的一件事:一条 56 节的链\n每帧要重算几千个顶点。2 帧重建一次,肉眼基本看不出差别。\n填 1 可关掉这项优化。"); ChainTautFeedback = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.链条", "绷紧反馈", true, "链条绷紧时用颜色和提示把它显出来:松弛是冷灰白,越紧越烫,绷到一半以上会弹一句提醒。\n颜色曲线和拖拽用的是同一条(6 米起、30 米满),所以「链子一发烫 = 你正在被拽」。\n这是每台机器自己的显示偏好,不参与主机同步。"); ChainColor = ((BaseUnityPlugin)this).Config.Bind<string>("命运绑定.链条", "链条颜色", "#C08A4A", "链条底色。十六进制色号,可以带 # 也可以不带(写 C08A4A 或 #C08A4A 都行)。\n默认 #C08A4A 是古铜色 —— 铁链本来是暖金属,冷灰白看着发怪。\n几个现成的值:\n C08A4A 古铜(默认) 9AA3AD 钢银 E0C36B 暗金 B22222 深红\n 3CB371 翠绿 5B8BD4 湖蓝 8A6FD1 紫晶 2E2E2E 玄铁黑\n注意别选太接近红色的底(比如深红),不然绷紧变色就看不出差别了。\n写错了不会崩:解析失败会退回古铜色。也可以在游戏里按「换色快捷键」循环切换。"); ChainColorCycleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("命运绑定.链条", "换色快捷键", (KeyCode)289, "在游戏里按这个键,按预设色板循环切换链条颜色,并立刻生效、自动存进配置。\n默认 F8。填 None 可关闭。不参与主机同步。"); ChainPhysics = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.链条", "物理链条", true, "锁链是否做地形求解(会绕开障碍物、不穿模)。关掉则退回两点直线,用于对比排查。\n【v0.2.0 起求解方式变了】不再是质点物理模拟,改成每帧纯几何求解:\n在两端连线上铺一串点 → 松弛成柔顺曲线 → 最后一步把所有点推出障碍物。\n因为「推出障碍物」跑在最后,渲染出去的每个点都刚被推出来过,\n所以在构造上就不可能穿模 —— 不再是「调参调到位就不穿」。"); ChainSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.链条", "链环间距", 0.55f, "链节间距(米)。越小越贴合地形,但链节更多、开销更大。"); ChainThickness = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.链条", "链条厚度", 0.15f, "链节的碰撞球半径(米)。调大更不容易穿进石头缝,还偶尔穿就调到 0.1。\n【注意】实际使用时会硬性钳在 0.4 米以内 —— 半径越大,从障碍物里推出的位移量就越大,\n旧版拉满后一碰到模型整条链条会直接扎进地里,所以这里必须封顶。你填 1.0 也按 0.4 算。"); ChainSlack = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.链条", "链条松弛量", 0.01f, "链条松弛比例。0 = 完全绷直,越大越松。\n注意:多余长度会带来约 √(3·距离·多余长度/8) 的下垂,所以这个值要小。\n0.12 在跨 8 米时下垂 1.7 米、链条直接垂到地上;0.01 只有 0.5 米,悬在两人之间。\n松弛量另有 0.25 米的绝对上限,避免远距离必然垂地。"); ChainMaxSag = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.链条", "最大下垂", 1.2f, "最大下垂(米):链节最多只能低于「两人中较低的那个」这么多,超出就被抬回去。\n【v0.4.0】默认从 0.6 提到 1.2 —— 链条现在会「记住」自己挂在哪,垂得多一点才像真铁链。\n嫌链条垂太低就调小;0 = 不限制(链条会自然垂到地形上)。\n这是防止链条趴到地上的兜底 —— 只靠重力+长度约束是压不住的,\n位置约束始终有残余拉伸,跨度 12 米以上实测会整条垂到地面。\n0 = 不限制(链条会自然垂到地形上)。\n【注意】填 0 或填很大时,实际按 2.0 米封顶 —— 否则链条会一路垂进地形里。"); ChainHideRadius = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.链条", "贴脸隐藏半径", 0.85f, "近摄像机裁剪半径(米)。\n链条起点就在你自己身上,第一人称下摄像机离它只有 0.2 米左右 ——\n那一节会被渲染成一个占满屏幕的白色大楔子,看起来像「下面没有人却连着一条链子」。\n这个半径内的起始链节会被丢掉,画面上的链条从稍远处开始(第一人称绳索的常规做法)。\n0 = 不裁剪,会看到贴脸的大白块。"); ChainMaxPoints = ((BaseUnityPlugin)this).Config.Bind<int>("命运绑定.链条", "链节基础数量", 56, "正常情况下链条用多少个链节。注意它现在是**下限**,不是上限 ——\n链条长的时候会自动加节,让相邻链节始终保持在「链环间距」左右。\n这正是链子不会断、又不会穿模的原因:链节间距没被拉散,每个链节都在做地形检测。\n硬上限 200 节(约 110 米),再远才会开始拉伸链节间距。\n调大 = 链节更密、更贴合地形,但更费性能。默认 56。"); ChainTopology = ((BaseUnityPlugin)this).Config.Bind<FateChainTopology>("命运绑定.链条", "链条拓扑", FateChainTopology.NearestWeb, "锁链怎么连:\nRing = 全队连成一个闭环。顺序按【空间邻近】排 —— 每条链都是挨得近的两个人。\n 每个人都有一条链,每个客户端都画出全部链条,4 人 = 4 条。\nNearestWeb = 每个人都连到离自己最近的那个人,画出全部这些链。\n 链永远最短、人人都被连上,不会出现横跨半张地图的怪链。\nAllPairs = 两两互连,4 人 = 6 条链。最「万物互联」,也最费性能。\n(v0.1.11 起配对一律看位置,不再按玩家加入房间的顺序 —— 旧版就是因此把地图两端、\n 相隔几百米的两个人连在了一起。)"); ChainLinkTexture = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.链条", "链环贴图", true, "给锁链套一张链环贴图,让它看起来是一根真正的链条,而不是一条白线。\n贴图是插件自己用代码画出来的(一节一节的金属环、中间镂空、首尾相接),\n不依赖任何外部图片文件 —— 所以这个包拖进 plugins 就能用,不用再下别的东西。\n关掉的话就是一条纯色带子(旧版就是这样,所以看着像木条、像白色楔子)。"); SolidChainLinks = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.链条", "立体链环", true, "把链条画成**真的一颗颗铁环**,环环相扣、相邻两颗互相垂直 —— 不是贴在一条带子上的图案。\n网格是插件运行时算出来的(用代码生成环面),同样不需要任何外部模型文件。\n关掉则回到「链环贴图」那条带子,两者不会同时出现。"); LinkDiameter = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.链条", "链环直径", 0.14f, "一颗铁环的外径(米)。默认 0.14 —— 大约一个拳头,远看像条结实的铁链。\n调大手感更粗犷(0.2 以上就很夸张了),调小更细密。范围 0.02 ~ 0.6。"); LinkWire = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.链条", "链环丝径", 0.035f, "铁环本身那根铁丝有多粗(米)。调粗更结实、更沉;调细更精致。\n上限会被自动压到「链环直径」的 45%,太粗会把中间的孔填死。"); ChainMemory = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.链条", "链条记忆", true, "【v0.4.0】链条以上一帧的形态为起点接着解,于是它「记得」自己刚才是从哪边绕过石头的。\n这是消掉绕障时左右翻面(抽搐)的根因修复,关掉不会让链子穿模,但会重新开始翻面。\n留这个开关是为了让你能对比两种手感:关掉就是旧版「每帧从直线重算」的行为。"); SpringPull = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.链条", "弹簧拉力", true, "【v0.4.1】拉力的力-伸长曲线换成「弹簧」:线性 —— 刚开始拉开时只是轻轻带住,越拉越沉。\n关掉则回到旧版的开方曲线(10% 伸长就出 32% 的力,起手很硬、像一根杆子)。\n弹簧曲线是 Chainbound(另一个作者的同类模组)用的那套手感,照搬它的思路。"); PullDamping = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.链条", "拉力阻尼", 0.35f, "【v0.4.1】把「你拉开的快慢」也算进拉力:猛地扯一下比慢慢挪更吃劲。\n0 = 只看拉开的距离(旧行为);0.35 表示远离速度到 5 米/秒时,额外加三成力。\n调大链条更「黏」、更不容易被猛地挣脱;调小更松。上限自动夹在 0 ~ 2。"); CampfireShuffle = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.链条", "篝火顺序重排", true, "【v0.4.1】每走完一段路(到篝火、段号推进)就把链条的顺序重排一次 —— \n和 Chainbound 的 ShuffleChainOrder 是同一个意思。只做「就近小幅换位」,\n所以不会出现一条链横跨半张地图。关掉则整个过程顺序都不变。"); ChainTextureScale = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.链条", "贴图密度", 0.5f, "贴图沿链条方向的重复密度。越小 → 链环越大、越少;越大 → 链环越小、越密。\n0.5 大约是每 0.25 米一节链环。觉得花纹太碎就调到 0.25,觉得太大就调到 1.0。\n这个值在链条被隐藏又重新显示之后才会生效(贴图是 建线时读一次的)。"); SyncFromHost = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.同步", "跟随房主参数", true, "【主机同步】只由开房间的那个人调参数,其他玩家不用动,会自动同步过去。\n开启后:房主每隔几秒把自己这份配置广播给全房间,客机收到就直接生效。\n快捷键「启停快捷键」和总开关「启用」不参与同步 —— 那两个是每个客户端自己的事,\n不该被房主覆盖(不然你按自己的键切开关会被房主一直掰回去)。\n如果你是客机、就是不想被房主覆盖,把这一项改成 false。"); SyncIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.同步", "同步间隔秒数", 5f, "房主每隔几秒广播一次参数(秒)。\n调小 → 改参数更快生效;调大 → 网络包更少。中途加入的玩家最多等这么久就拿到房主的参数。"); TugStartDistance = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拖拽", "拖拽起始距离", 6f, "拉开多少米之后锁链开始拽人(米)。\n这是本项的关键参数:旧版用的是「最大锁链距离」×「绷紧比例」= 30 × 0.75 = 22.5 米,\n正常一起爬图的队伍根本到不了,所以拖拽一次都没触发过。\n调小 = 更容易被拽(例如 4);调大 = 靠得很近也不管你(例如 10)。"); EnableTug = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.拖拽", "启用拖拽", true, "锁链拖拽:绷紧时前面的人会拉住后面的人;后面的人一直不走,前面的人会越来越吃力。"); ChainTautRatio = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拖拽", "绷紧比例(已废弃)", 0.75f, "【已废弃,v0.1.11 起不再使用】旧版用它算绷紧起点(「最大锁链距离」× 该值 = 22.5 米,太高了)。\n现在改用上面的「拖拽起始距离」,这一项留着不影响任何行为。"); MaxPullForce = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拖拽", "最大拉力", 160f, "被拽时施加的最大力。觉得拽不动就调大(例如 320),拽得太猛就调小(例如 80)。"); MaxDragForce = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拖拽", "最大拖拽力", 160f, "拖着不走的人时,施加给自己的最大阻力(负重感)。"); VerticalPullScale = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拖拽", "竖直拉力比例", 0.35f, "拉力里竖直(上下)方向占的比例。\n力是按三维方向算的(从我指向队友),所以头上有队友时,\n力里天生带一个向上的分量,全量施加会把你往上吊。\n1.0 = 按原始方向全量拉;0.35 = 竖直压到三成(默认);\n0.0 = 拉力完全落在水平面上,上面的人怎么跑都不会把你往上拽。\n这是本地手感设置、不参与主机同步 —— 它只影响你自己角色的受力。"); DragSpeedDiff = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拖拽", "拖拽速度差", 0.5f, "速度差超过多少(米/秒)才算「一个在走、一个没走」。"); DragRampSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拖拽", "拖拽渐满秒数", 4f, "后面的人一直不走时,阻力从 0 涨到满需要几秒。时间越长越能体现「越来越重」。"); EnablePull = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.拉人", "允许拉队友", true, "按住「拉人快捷键」,把锁链上比你低得够多的队友往上拉 —— 队友掉进坑里爬不上来时用。\n代价是拉的人自己掉体力(PEAK 原生体力,不是饥饿值):体力见底就拉不动,\n松开歇一会儿会自然回复。因为力是往队友那边施的,你自己也会被链条往下带一点 ——\n本模组里那股「负重感」就来自这里。"); PullKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("命运绑定.拉人", "拉人快捷键", (KeyCode)324, "按住这个键开始拉人(是按住,不是按一下)。\n默认 Mouse1 = 鼠标右键 —— 对着链条按住右键就能拉,不用另记快捷键。\n想换键就填名字:F7 / Mouse0(左键) / E / Q ... 填 None 可以关掉。\n按住时会自动挑「锁链上比你低得最多」的那名队友,不用事先选中谁。"); PullForce = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拉人", "拉人上升力", 190f, "往上拉队友的力度。拉不动就调大(例如 300),人被拉得乱飞就调小(例如 120)。\n这个力沿着「你 → 队友」的三维方向施加,竖直分量是满的,\n不受上面「竖直拉力比例」的限制 —— 那一项管的是拖拽阻力,不管拉人。"); PullStaminaPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拉人", "拉人每秒体力消耗", 20f, "拉人时每秒掉多少体力。PEAK 满体力通常是 100 —— 所以 20 大约能连续拉 5 秒。\n调到 8 可以拉很久,调到 60 就只能拽两下。"); PullMinDrop = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拉人", "拉人最小高度差", 1.2f, "你对着的这条链,另一头至少要低于你这么多米,才拉得动。\n原因是力的方向固定朝你这边(只会往上提),另一头比你高时这一拉等于把人往下拽。\n不满足时只弹提示、**不会偷偷换成别人** —— 你指着谁就是谁。"); PullMaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拉人", "拉人最远距离", 18f, "超过这个距离就拉不动了(米)。默认 18 —— 比「拖拽起始距离」6 米宽松,\n因为掉进坑里的队友经常离得很远。"); PullMinStamina = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拉人", "拉人最低体力", 8f, "体力低于这个值就拉不动了(会提示一次)。防止体力见底还能无限拉。"); PullNotify = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.拉人", "被拉时弹提示", true, "有人拉你时,你这边弹一条提示。默认开。这是每台机器自己的显示偏好,不参与主机同步。"); PullAimRequired = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.拉人", "需要对着链条", true, "开启后,**你对着哪条链就拉哪条链另一头的人** —— 目标完全由你的视线决定,\n不会替你自动挑人。多人时这一点很重要:你想拉谁就瞄谁的链。\n不对着任何链子时,右键完全不动作(不扣体力、不弹提示、不影响原版右键)。\n关掉它则退回「离我最近的那条链的另一端」,仍然不按高度挑人。"); PullAimAngle = ((BaseUnityPlugin)this).Config.Bind<float>("命运绑定.拉人", "对准角度", 35f, "「对着链条」的容忍角度(度)。数值越大越宽松:\n35 需要瞄得比较准;60 大概朝着那个方向就行;90 等于半个视野内都算对准。\n链条没有碰撞体,判定是按角度算的,所以这里给的是角度不是准心。"); AimHighlight = ((BaseUnityPlugin)this).Config.Bind<bool>("命运绑定.拉人", "瞄准高亮", true, "【v0.4.1】你视线对着的那条链会亮一档,不用按任何键 —— 于是你随时都知道\n「现在按右键会拉谁」,而不是按下去才发现拉错了人。\n这是本地显示偏好,不参与主机同步。关掉则链子只按绷紧度变色。"); SyncFloat = new ConfigEntry<float>[19] { MaxDistance, PoisonPerSecond, GraceSeconds, ShareRatio, ChainWidth, ChainThickness, ChainSpacing, ChainSlack, ChainMaxSag, ChainHideRadius, ChainTextureScale, TugStartDistance, MaxPullForce, MaxDragForce, DragSpeedDiff, DragRampSeconds, LinkDiameter, LinkWire, PullDamping }; SyncBool = new ConfigEntry<bool>[15] { ShareHunger, ShareInjury, SharePoison, DrawChain, ChainPhysics, EnableTug, ChainLinkTexture, SolidChainLinks, ChainMemory, SpringPull, CampfireShuffle, RequireTwoPlayers, DisableInAirport, Enabled, ToggleByHost }; SyncInt = new ConfigEntry<int>[1] { ChainMaxPoints }; PluginConfig = ((BaseUnityPlugin)this).Config; MigrateLegacyKeys(); ApplyDifficulty(); new Harmony("com.quaree.fatebound").PatchAll(typeof(FateBoundPatch)); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} v{1} 已加载,难度={2},切换键={3}", "命运绑定", "0.4.4", Difficulty.Value, ToggleKey.Value)); } internal static void EnforceHostVisuals() { if (hostVisualsKnown && PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient && DrawChain != null && DrawChain.Value != hostDrawChain) { DrawChain.Value = hostDrawChain; FateBoundController.FateNotify("链条显示由房主统一控制 —— 客机改不掉"); } } private void Update() { //IL_0016: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) WatchConfig(); EnforceHostVisuals(); if (ChainColorCycleKey != null && (int)ChainColorCycleKey.Value != 0 && SafeInput.GetKeyDown(ChainColorCycleKey.Value)) { CycleChainColor(); } if (DebugKey != null && (int)DebugKey.Value != 0 && SafeInput.GetKeyDown(DebugKey.Value)) { DebugRequested = true; } if (ToggleKey == null || Enabled == null) { return; } KeyCode value = ToggleKey.Value; if ((int)value != 0 && SafeInput.GetKeyDown(value)) { if (ToggleByHost != null && ToggleByHost.Value && PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { FateBoundController.FateNotify("锁链启停由房主统一控制 —— 客机按 F9 无效"); return; } Enabled.Value = !Enabled.Value; ForceSyncNow = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)(Enabled.Value ? "命运绑定:命运锁链已开启" : "命运绑定:命运锁链已关闭")); } } internal static void LogLine(string msg) { try { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)msg); } } catch { } } internal static void LogWarn(string msg) { try { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)msg); } } catch { } } private void MigrateLegacyKeys() { int migrated = 0; MigrateKey<bool>("FateBound", "Enabled", Enabled, ref migrated); MigrateKey<KeyCode>("FateBound", "ToggleKey", ToggleKey, ref migrated); MigrateKey<FateDifficulty>("FateBound", "Difficulty", Difficulty, ref migrated); MigrateKey<float>("FateBound", "MaxDistance", MaxDistance, ref migrated); MigrateKey<float>("FateBound", "GraceSeconds", GraceSeconds, ref migrated); MigrateKey<float>("FateBound", "PoisonPerSecond", PoisonPerSecond, ref migrated); MigrateKey<float>("FateBound", "ShareRatio", ShareRatio, ref migrated); MigrateKey<bool>("FateBound", "ShareHunger", ShareHunger, ref migrated); MigrateKey<bool>("FateBound", "ShareInjury", ShareInjury, ref migrated); MigrateKey<bool>("FateBound", "SharePoison", SharePoison, ref migrated); MigrateKey<bool>("FateBound", "DrawChain", DrawChain, ref migrated); MigrateKey<float>("FateBound", "ChainWidth", ChainWidth, ref migrated); MigrateKey<bool>("FateBound", "RequireTwoPlayers", RequireTwoPlayers, ref migrated); MigrateKey<bool>("FateBound", "DisableInAirport", DisableInAirport, ref migrated); MigrateKey<bool>("FateBound", "NotifyShared", NotifyShared, ref migrated); MigrateKey<KeyCode>("FateBound", "DebugKey", DebugKey, ref migrated); MigrateKey<bool>("FateBound.Chain", "ChainPhysics", ChainPhysics, ref migrated); MigrateKey<float>("FateBound.Chain", "ChainSpacing", ChainSpacing, ref migrated); MigrateKey<float>("FateBound.Chain", "ChainThickness", ChainThickness, ref migrated); MigrateKey<float>("FateBound.Chain", "ChainSlack", ChainSlack, ref migrated); MigrateKey<float>("FateBound.Chain", "ChainMaxSag", ChainMaxSag, ref migrated); MigrateKey<float>("FateBound.Chain", "ChainHideRadius", ChainHideRadius, ref migrated); MigrateKey<int>("FateBound.Chain", "ChainMaxPoints", ChainMaxPoints, ref migrated); MigrateKey<int>("命运绑定.链条", "链节上限", ChainMaxPoints, ref migrated); MigrateKey<FateChainTopology>("FateBound.Chain", "ChainTopology", ChainTopology, ref migrated); MigrateKey<bool>("FateBound.Chain", "ChainLinkTexture", ChainLinkTexture, ref migrated); MigrateKey<float>("FateBound.Chain", "ChainTextureScale", ChainTextureScale, ref migrated); MigrateKey<bool>("FateBound.Chain", "ChainTautFeedback", ChainTautFeedback, ref migrated); MigrateKey<string>("FateBound.Chain", "ChainColor", ChainColor, ref migrated); MigrateKey<KeyCode>("FateBound.Chain", "ChainColorCycleKey", ChainColorCycleKey, ref migrated); MigrateKey<bool>("FateBound.Sync", "SyncFromHost", SyncFromHost, ref migrated); MigrateKey<float>("FateBound.Sync", "SyncIntervalSeconds", SyncIntervalSeconds, ref migrated); MigrateKey<bool>("FateBound.Tug", "EnableTug", EnableTug, ref migrated); MigrateKey<float>("FateBound.Tug", "TugStartDistance", TugStartDistance, ref migrated); MigrateKey<float>("FateBound.Tug", "ChainTautRatio", ChainTautRatio, ref migrated); MigrateKey<float>("FateBound.Tug", "MaxPullForce", MaxPullForce, ref migrated); MigrateKey<float>("FateBound.Tug", "MaxDragForce", MaxDragForce, ref migrated); MigrateKey<float>("FateBound.Tug", "DragSpeedDiff", DragSpeedDiff, ref migrated); MigrateKey<float>("FateBound.Tug", "DragRampSeconds", DragRampSeconds, ref migrated); if (migrated > 0) { try { ((BaseUnityPlugin)this).Config.Save(); } catch { } LogLine($"已把 {migrated} 项旧版英文配置迁移到中文键名(旧键已从配置文件里移除)。"); } } private void MigrateKey<T>(string oldSection, string oldKey, ConfigEntry<T> target, ref int migrated) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown if (target == null) { return; } try { ConfigDefinition val = new ConfigDefinition(oldSection, oldKey); ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(oldSection, oldKey, target.Value, "(旧版英文键名,已迁移到对应的中文键,这一项可以忽略)"); if (!object.Equals(val2.Value, target.Value)) { target.Value = val2.Value; migrated++; } ((BaseUnityPlugin)this).Config.Remove(val); } catch (Exception ex) { LogWarn("旧配置迁移失败 " + oldSection + "." + oldKey + ":" + ex.Message); } } internal static Color ChainBaseColor() { //IL_0022: 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_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_0032: 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) string text = ((ChainColor != null) ? ChainColor.Value : null); if (text == colorCacheKey) { return colorCacheVal; } if (!TryParseHex(text, out var c)) { c = ChainColorFallback; } colorCacheKey = text; colorCacheVal = c; return c; } internal static bool TryParseHex(string s, out Color c) { //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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) c = Color.white; if (string.IsNullOrEmpty(s)) { return false; } s = s.Trim(); if (s.StartsWith("#")) { s = s.Substring(1); } if (s.Length != 6 && s.Length != 8) { return false; } if (!int.TryParse(s.Substring(0, 2), NumberStyles.HexNumber, null, out var result)) { return false; } if (!int.TryParse(s.Substring(2, 2), NumberStyles.HexNumber, null, out var result2)) { return false; } if (!int.TryParse(s.Substring(4, 2), NumberStyles.HexNumber, null, out var result3)) { return false; } c = new Color((float)result / 255f, (float)result2 / 255f, (float)result3 / 255f); return true; } internal static void CycleChainColor() { if (ChainColor == null) { return; } string text = (ChainColor.Value ?? "").Trim().ToUpperInvariant(); int num = -1; for (int i = 0; i < ChainColorPresets.Length; i++) { if (ChainColorPresets[i][0] == text) { num = i; break; } } int num2 = (num + 1) % ChainColorPresets.Length; ChainColor.Value = ChainColorPresets[num2][0]; try { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Config.Save(); } } catch { } FateBoundController.FateNotify("链条颜色:" + ChainColorPresets[num2][1] + "(" + ChainColorPresets[num2][0] + ")"); } internal static byte[] BuildSyncBlob() { byte[] blob = new byte[8 + SyncFloat.Length * 4 + 4 + SyncBool.Length + 4 + SyncInt.Length * 4]; int o = 0; PutI(1178753841); PutI(SyncFloat.Length); for (int i = 0; i < SyncFloat.Length; i++) { PutI(BitConverter.ToInt32(BitConverter.GetBytes(SyncFloat[i].Value), 0)); } PutI(SyncBool.Length); for (int j = 0; j < SyncBool.Length; j++) { blob[o++] = (SyncBool[j].Value ? ((byte)1) : ((byte)0)); } PutI(SyncInt.Length); for (int k = 0; k < SyncInt.Length; k++) { PutI(SyncInt[k].Value); } return blob; void PutI(int v) { blob[o++] = (byte)v; blob[o++] = (byte)(v >> 8); blob[o++] = (byte)(v >> 16); blob[o++] = (byte)(v >> 24); } } internal static bool ApplySyncBlob(byte[] blob) { if (blob == null || blob.Length < 20) { return false; } int o = 0; if (ReadI() != 1178753841) { return false; } if (ReadI() != SyncFloat.Length) { return false; } if (o + SyncFloat.Length * 4 > blob.Length) { return false; } for (int i = 0; i < SyncFloat.Length; i++) { SyncFloat[i].Value = BitConverter.ToSingle(blob, o + i * 4); } o += SyncFloat.Length * 4; if (ReadI() != SyncBool.Length) { return false; } if (o + SyncBool.Length > blob.Length) { return false; } bool value = Enabled.Value; for (int j = 0; j < SyncBool.Length; j++) { SyncBool[j].Value = blob[o + j] != 0; } o += SyncBool.Length; if (DrawChain != null) { hostDrawChain = DrawChain.Value; hostVisualsKnown = true; } if (Enabled.Value != value) { FateBoundController.FateNotify(Enabled.Value ? "命运锁链被房主开启了" : "命运锁链被房主关闭了"); } if (ReadI() != SyncInt.Length) { return false; } if (o + SyncInt.Length * 4 > blob.Length) { return false; } for (int k = 0; k < SyncInt.Length; k++) { SyncInt[k].Value = BitConverter.ToInt32(blob, o + k * 4); } return true; int ReadI() { int result = blob[o] | (blob[o + 1] << 8) | (blob[o + 2] << 16) | (blob[o + 3] << 24); o += 4; return result; } } internal static string DifficultyLabel(FateDifficulty d) { return d switch { FateDifficulty.Easy => "简单", FateDifficulty.Hell => "地狱", FateDifficulty.Custom => "自定义", _ => "普通", }; } private static void MarkConfigChanged() { if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient) { ForceSyncNow = true; } } internal static void WatchConfig() { float unscaledTime = Time.unscaledTime; if (unscaledTime < cfgPollAt) { return; } cfgPollAt = unscaledTime + 1f; try { if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient && SyncFromHost != null && SyncFromHost.Value) { cfgWatched = true; return; } if (cfgWatched && Difficulty != null && Difficulty.Value != cfgLastDifficulty) { cfgLastDifficulty = Difficulty.Value; ApplyDifficulty(); MarkConfigChanged(); FateBoundController.FateNotify("难度已切换:" + DifficultyLabel(Difficulty.Value)); } if (PluginConfig != null && !string.IsNullOrEmpty(PluginConfig.ConfigFilePath) && File.Exists(PluginConfig.ConfigFilePath)) { string text = File.ReadAllText(PluginConfig.ConfigFilePath); if (cfgWatched && text != cfgLastText) { PluginConfig.Reload(); ApplyDifficulty(); cfgLastDifficulty = Difficulty.Value; MarkConfigChanged(); FateBoundController.FateNotify("配置已重新载入\u3000难度:" + DifficultyLabel(Difficulty.Value)); text = File.ReadAllText(PluginConfig.ConfigFilePath); } cfgLastText = text; } cfgWatched = true; } catch (Exception ex) { cfgWatched = true; LogWarn("配置热重载失败:" + ex.Message); } } internal static void ApplyDifficulty() { if (Difficulty != null && Difficulty.Value != FateDifficulty.Custom) { switch (Difficulty.Value) { case FateDifficulty.Easy: MaxDistance.Value = 50f; PoisonPerSecond.Value = 0.02f; ShareRatio.Value = 0.6f; break; case FateDifficulty.Hell: MaxDistance.Value = 15f; PoisonPerSecond.Value = 0.07f; ShareRatio.Value = 1f; break; default: MaxDistance.Value = 30f; PoisonPerSecond.Value = 0.035f; ShareRatio.Value = 1f; break; } } } } [HarmonyPatch(typeof(Character), "Awake")] public static class FateBoundPatch { [HarmonyPostfix] public static void AwakePatch(Character __instance) { if ((Object)(object)((Component)__instance).GetComponent<FateBoundController>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<FateBoundController>(); } } } public class FateBoundController : MonoBehaviour { private class ChainLink { public LineRenderer line; public Character lastA; public Character lastB; public PhysicsChain chain; public Character a; public Character b; public MeshFilter linkFilter; public MeshRenderer linkRend; public Mesh linkMesh; public Material linkMat; } private const int IDX_INJURY = 0; private const int IDX_HUNGER = 1; private const int IDX_POISON = 3; private const float POISON_FLUSH = 0.02f; private const float INJURY_ADD_SCALE = 0.125f; private Character ch; private CharacterAfflictions aff; private const int MaxChainCount = 16; private const int ChainPointHardCap = 200; private readonly List<ChainLink> links = new List<ChainLink>(); private readonly List<Character> pairBuf = new List<Character>(); private readonly List<Character> ringOrder = new List<Character>(); private bool[] ringUsed = new bool[0]; private Camera cam; private readonly List<Character> players = new List<Character>(); private Character nearest; private Character pullTarget; private Character aimHint; private readonly List<Character> avoidBuf = new List<Character>(); private int shuffleSeed; private readonly List<Character> lastPairsBuf = new List<Character>(); private object lastSegment; private bool segmentKnown; private static PropertyInfo segmentProp; private static bool segmentPropTried; private float pullNoticeAt; private float pullNoticeAgainAt; private bool pullWarnedOut; private float nearestDist; private float graceTimer; private float poisonAccum; private float prevTeamMaxInjury; private float prevTeamMaxHunger; private float prevTeamMaxPoison; private bool sharePrimed; private static bool announcedThisSession; private bool shareFlagsInit; private bool lastShareInjury; private bool lastShareHunger; private bool lastSharePoison; private Vector3[] velBuf = (Vector3[])(object)new Vector3[8]; private Vector3[] lastPosBuf = (Vector3[])(object)new Vector3[8]; private bool velPrimed; private float dragAccum; private static string selfCheckText = null; private static float selfCheckAt = -99f; private static GUIStyle selfCheckStyle = null; private static GUIStyle selfCheckTitle = null; private Vector3 lastCenter; private bool lastCenterReady; private readonly int[] meshCd = new int[64]; private bool hardWarned; private float hardWarnedAt; private int lastRawCount; private int lastBotCount; private int lastGhostCount; private int lastInactiveCount; private bool soloPullLatch; private bool active; private static Texture2D chainLinkTex; private float syncTimer; private float notifyInjuryAt; private float notifyHungerAt; private float notifyPoisonAt; private static UI_Notifications uiNotify; private bool tautNotified; private float tautNotifyAt; private static readonly List<Vector3> linkVerts = new List<Vector3>(16384); private static readonly List<Vector3> linkNorms = new List<Vector3>(16384); private static readonly List<int> linkTris = new List<int>(32768); private const int LinkMajorSegs = 10; private const int LinkMinorSegs = 6; private const int MaxLinkMeshes = 220; private const float LinkPitchScale = 1.12f; private void Awake() { ch = ((Component)this).GetComponent<Character>(); } private void SyncTick() { if (Plugin.SyncFromHost == null || !Plugin.SyncFromHost.Value || (Object)(object)ch == (Object)null || !ch.IsLocal) { return; } float num = ((Plugin.SyncIntervalSeconds != null) ? Mathf.Max(1f, Plugin.SyncIntervalSeconds.Value) : 5f); syncTimer -= Time.deltaTime; if (Plugin.ForceSyncNow) { Plugin.ForceSyncNow = false; syncTimer = 0f; } if (syncTimer > 0f) { return; } syncTimer = num; try { if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient) { PhotonView component = ((Component)this).GetComponent<PhotonView>(); if (!((Object)(object)component == (Object)null)) { component.RPC("RPCA_FateBoundSync", (RpcTarget)1, new object[1] { Plugin.BuildSyncBlob() }); } } } catch { } } [PunRPC] public void RPCA_FateBoundSync(byte[] blob) { Plugin.ApplySyncBlob(blob); } private void Update() { active = false; if (Plugin.Enabled == null || !Plugin.Enabled.Value) { Deactivate(); Hide(); return; } if ((Object)(object)ch == (Object)null) { ch = ((Component)this).GetComponent<Character>(); } if ((Object)(object)ch == (Object)null || ch.isBot || (Object)(object)ch.Ghost != (Object)null) { Deactivate(); Hide(); return; } SyncTick(); HandleDebugKey(); if (Plugin.DisableInAirport != null && Plugin.DisableInAirport.Value && ch.inAirport) { Deactivate(); Hide(); return; } CollectPlayers(); if (ch.IsLocal) { ProbePullKey(); } if ((Plugin.RequireTwoPlayers == null || Plugin.RequireTwoPlayers.Value) && players.Count < 2) { Deactivate(); Hide(); return; } EnsureAfflictions(); if ((Object)(object)aff == (Object)null) { Deactivate(); Hide(); return; } active = true; if (ch.IsLocal && !announcedThisSession) { announcedThisSession = true; FateNotify("命运绑定 v0.4.4 已生效\u3000F9 开关\u3000F10 读数"); Plugin.LogLine("命运绑定 v0.4.4 已在关卡内生效"); } if (ch.IsLocal) { ShareStatuses(); } if (ch.IsLocal) { HandlePull(); } DistancePoison(); if (Plugin.DrawChain != null && Plugin.DrawChain.Value) { Draw(); } else { Hide(); } } private void CollectPlayers() { players.Clear(); lastRawCount = (lastBotCount = (lastGhostCount = (lastInactiveCount = 0))); List<Character> allCharacters = Character.AllCharacters; if (allCharacters == null) { return; } lastRawCount = allCharacters.Count; bool flag = Plugin.LooseCollect != null && Plugin.LooseCollect.Value; for (int i = 0; i < allCharacters.Count; i++) { Character val = allCharacters[i]; if ((Object)(object)val == (Object)null) { lastInactiveCount++; } else if (flag) { players.Add(val); } else if (val.isBot) { lastBotCount++; } else if ((Object)(object)val.Ghost != (Object)null) { lastGhostCount++; } else if (!((Component)val).gameObject.activeInHierarchy) { lastInactiveCount++; } else { players.Add(val); } } } private void ProbePullKey() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) bool flag = false; try { flag = Plugin.PullKey != null && SafeInput.GetKey(Plugin.PullKey.Value); } catch { } bool num = flag && !soloPullLatch; soloPullLatch = flag; if (num && players.Count < 2) { FateNotify("命运绑定:现在只采集到 " + players.Count + " 人,拉人需要至少 2 人(链条也没画)。按 F10 看自检面板。"); } } private void EnsureAfflictions() { if ((Object)(object)aff == (Object)null) { aff = ((Component)ch).GetComponent<CharacterAfflictions>(); } } private void ShareStatuses() { if (!sharePrimed) { prevTeamMaxInjury = TeamMax(0); prevTeamMaxHunger = TeamMax(1); prevTeamMaxPoison = TeamMax(3); sharePrimed = true; return; } bool flag = Plugin.ShareInjury != null && Plugin.ShareInjury.Value; bool flag2 = Plugin.ShareHunger != null && Plugin.ShareHunger.Value; bool flag3 = Plugin.SharePoison != null && Plugin.SharePoison.Value; if (!shareFlagsInit) { shareFlagsInit = true; lastShareInjury = flag; lastShareHunger = flag2; lastSharePoison = flag3; } else { if (flag != lastShareInjury) { prevTeamMaxInjury = TeamMax(0); lastShareInjury = flag; } if (flag2 != lastShareHunger) { prevTeamMaxHunger = TeamMax(1); lastShareHunger = flag2; } if (flag3 != lastSharePoison) { prevTeamMaxPoison = TeamMax(3); lastSharePoison = flag3; } } if (flag) { CatchUp((STATUSTYPE)0, ref prevTeamMaxInjury); } if (flag2) { CatchUp((STATUSTYPE)1, ref prevTeamMaxHunger); } if (flag3) { CatchUp((STATUSTYPE)3, ref prevTeamMaxPoison); } } private float TeamMax(int idx) { float num = 0f; for (int i = 0; i < players.Count; i++) { CharacterAfflictions component = ((Component)players[i]).GetComponent<CharacterAfflictions>(); if (!((Object)(object)component == (Object)null) && component.currentStatuses != null && idx < component.currentStatuses.Length) { float num2 = component.currentStatuses[idx]; if (num2 > num) { num = num2; } } } return num; } private void CatchUp(STATUSTYPE type, ref float prevMax) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown //IL_0021: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) int idx = (int)type; float num = TeamMax(idx); float num2 = num - prevMax; prevMax = num; if (num2 <= 0.0001f) { return; } float currentStatus = aff.GetCurrentStatus(type); if (!(currentStatus >= num)) { float num3 = Mathf.Clamp01((Plugin.ShareRatio != null) ? Plugin.ShareRatio.Value : 1f); float num4 = Mathf.Min(num2, num - currentStatus) * num3; if (!(num4 <= 0.0001f)) { float num5 = (((int)type == 0) ? 0.125f : 1f); aff.AddStatus(type, num4 * num5, false, false, false, false); NotifyShared(type, num4); } } } private void DistancePoison() { //IL_0038: 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_00a2: 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) Character val = nearest; Character val2 = null; float num = float.MaxValue; for (int i = 0; i < players.Count; i++) { Character val3 = players[i]; if (!((Object)(object)val3 == (Object)(object)ch)) { float num2 = Vector3.Distance(ch.Center, val3.Center); if ((Object)(object)val3 == (Object)(object)val) { num2 -= 2f; } if (num2 < num) { num = num2; val2 = val3; } } } nearest = val2; nearestDist = (((Object)(object)nearest != (Object)null) ? Vector3.Distance(ch.Center, nearest.Center) : float.MaxValue); if ((Object)(object)nearest == (Object)null) { ResetPenalty(); return; } SampleSpeeds(); float num3 = ((Plugin.MaxDistance != null) ? Plugin.MaxDistance.Value : 30f); if (nearestDist <= num3) { ResetPenalty(); return; } float num4 = ((Plugin.GraceSeconds != null) ? Plugin.GraceSeconds.Value : 5f); graceTimer += Time.deltaTime; if (!(graceTimer < num4)) { float num5 = ((Plugin.PoisonPerSecond != null) ? Plugin.PoisonPerSecond.Value : 0.035f); poisonAccum += num5 * Time.deltaTime; if (poisonAccum >= 0.02f) { ApplyPoison(poisonAccum); poisonAccum = 0f; } } } private void SampleSpeeds() { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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_00a9: 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_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) int count = players.Count; if (count == 0) { return; } if (velBuf.Length < count) { velBuf = (Vector3[])(object)new Vector3[count]; lastPosBuf = (Vector3[])(object)new Vector3[count]; } float deltaTime = Time.deltaTime; if (deltaTime <= 0f) { return; } bool flag = !velPrimed; velPrimed = true; for (int i = 0; i < count; i++) { Character val = players[i]; if ((Object)(object)val == (Object)null) { velBuf[i] = Vector3.zero; continue; } Vector3 center = val.Center; if (flag) { lastPosBuf[i] = center; velBuf[i] = Vector3.zero; continue; } Vector3 val2 = (center - lastPosBuf[i]) / deltaTime; if (((Vector3)(ref val2)).sqrMagnitude > 400f) { val2 = Vector3.zero; } velBuf[i] = Vector3.Lerp(velBuf[i], val2, 0.35f); lastPosBuf[i] = center; } } private Vector3 VelOf(Character who) { //IL_0009: 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_0038: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)who == (Object)null) { return Vector3.zero; } int num = players.IndexOf(who); if (num < 0 || num >= velBuf.Length) { return Vector3.zero; } return velBuf[num]; } private static float PullCurve(float taut) { if (Plugin.SpringPull != null && !Plugin.SpringPull.Value) { return taut; } return taut * taut; } private static float DampGain() { return Mathf.Clamp((Plugin.PullDamping != null) ? Plugin.PullDamping.Value : 0.35f, 0f, 2f); } private Vector3 TugPair(Character other, out float gap) { //IL_001e: 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_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) //IL_0053: 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_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_0075: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_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_0198: Unknown result type (might be due to invalid IL or missing references) //IL_0178: 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_0148: 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) gap = 0f; if ((Object)(object)other == (Object)null || (Object)(object)other == (Object)(object)ch) { return Vector3.zero; } float num = ((Plugin.TugStartDistance != null) ? Plugin.TugStartDistance.Value : 6f); Vector3 val = other.Center - ch.Center; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude <= num || magnitude < 0.001f) { return Vector3.zero; } Vector3 val2 = val / magnitude; float taut = Tautness(ch.Center, other.Center); float num2 = 0f - Vector3.Dot(VelOf(ch), val2); float num3 = Vector3.Dot(VelOf(other), val2); gap = num2 - num3; float num4 = ((Plugin.DragSpeedDiff != null) ? Plugin.DragSpeedDiff.Value : 0.5f); if (gap > num4) { float num5 = Mathf.Max(0.1f, (Plugin.DragRampSeconds != null) ? Plugin.DragRampSeconds.Value : 4f); float num6 = Mathf.Clamp01(dragAccum / num5); float num7 = ((Plugin.MaxDragForce != null) ? Plugin.MaxDragForce.Value : 160f) * PullCurve(taut); float num8 = num7 * DampGain() * Mathf.Clamp01(gap / 5f); return val2 * ((num7 + num8) * num6); } if (0f - gap > num4) { float num9 = ((Plugin.MaxPullForce != null) ? Plugin.MaxPullForce.Value : 160f); return val2 * (num9 * PullCurve(taut) * Mathf.Clamp01((0f - gap) / 3f)); } return Vector3.zero; } private void HandlePull() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0231: 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_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0278: 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_0287: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) if (Plugin.EnablePull == null || !Plugin.EnablePull.Value) { pullTarget = null; return; } if (Plugin.PullKey == null || (int)Plugin.PullKey.Value == 0) { pullTarget = null; return; } if (pullNoticeAt > 0f) { pullNoticeAt -= Time.deltaTime; } if (!SafeInput.GetKey(Plugin.PullKey.Value)) { pullTarget = null; pullWarnedOut = false; pullNoticeAgainAt = 0f; return; } Character val = PickAimedPartner(); if ((Object)(object)val == (Object)null) { pullTarget = null; return; } if ((Object)(object)val != (Object)(object)pullTarget) { pullTarget = val; pullNoticeAgainAt = 0f; pullWarnedOut = false; } float num = ((Plugin.PullMinDrop != null) ? Plugin.PullMinDrop.Value : 1.2f); if (ch.Center.y - pullTarget.Center.y < num) { if (pullNoticeAt <= 0f) { pullNoticeAt = 2.5f; FateNotify("这条链另一头不比你低 " + num.ToString("F1") + " 米,拉不上来(拉人只会往上提)"); } return; } float num2 = ((Plugin.PullMaxDistance != null) ? Plugin.PullMaxDistance.Value : 18f); if (Vector3.Distance(ch.Center, pullTarget.Center) > num2) { if (pullNoticeAt <= 0f) { pullNoticeAt = 2.5f; FateNotify("离得太远,拉不动(上限 " + num2.ToString("F0") + " 米)"); } return; } float num3 = ((Plugin.PullMinStamina != null) ? Plugin.PullMinStamina.Value : 8f); if (ch.GetTotalStamina() <= num3) { if (!pullWarnedOut) { pullWarnedOut = true; FateNotify("体力见底,拉不动了 —— 松手歇一会儿会自动回"); } return; } pullWarnedOut = false; float num4 = ((Plugin.PullStaminaPerSecond != null) ? Plugin.PullStaminaPerSecond.Value : 20f); ch.AddStamina((0f - num4) * Time.deltaTime); Vector3 val2 = ch.Center - pullTarget.Center; float magnitude = ((Vector3)(ref val2)).magnitude; if (magnitude < 0.05f) { return; } float num5 = ((Plugin.PullForce != null) ? Plugin.PullForce.Value : 190f); float num6 = Mathf.Clamp01(magnitude / 3f); Vector3 val3 = val2 / magnitude * (num5 * num6); try { ((MonoBehaviourPun)pullTarget).photonView.RPC("RPCA_AddForceAtPosition", (RpcTarget)0, new object[3] { val3, pullTarget.Center, 6f }); if (Plugin.PullNotify != null && Plugin.PullNotify.Value && Time.time >= pullNoticeAgainAt) { pullNoticeAgainAt = Time.time + 3f; ((MonoBehaviourPun)pullTarget).photonView.RPC("RPCA_FateBoundPullNotice", (RpcTarget)0, Array.Empty<object>()); } } catch { } } [PunRPC] public void RPCA_FateBoundPullNotice() { if (!((Object)(object)ch == (Object)null) && ch.IsLocal && (Plugin.PullNotify == null || Plugin.PullNotify.Value)) { FateNotify("有人在拉你上来 —— 对方正在消耗体力"); } } private Character PickAimedPartner() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_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_012c: 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) if (Plugin.PullAimRequired != null && !Plugin.PullAimRequired.Value) { return PickNearestPartner(); } Vector3 val = CameraPosition(); Vector3 val2 = CameraForward(); if (((Vector3)(ref val2)).sqrMagnitude < 0.001f) { return PickNearestPartner(); } float num = Mathf.Cos(Mathf.Clamp((Plugin.PullAimAngle != null) ? Plugin.PullAimAngle.Value : 35f, 5f, 90f) * (MathF.PI / 180f)); Character result = null; float num2 = float.MaxValue; for (int i = 0; i < links.Count; i++) { ChainLink chainLink = links[i]; if (chainLink == null || chainLink.chain == null) { continue; } Character val3 = (((Object)(object)chainLink.a == (Object)(object)ch) ? chainLink.b : (((Object)(object)chainLink.b == (Object)(object)ch) ? chainLink.a : null)); if (!IsPullable(val3)) { continue; } Vector3[] points = chainLink.chain.Points; if (points == null) { continue; } for (int j = 0; j < points.Length; j += 2) { Vector3 val4 = points[j] - val; float num3; if (((Vector3)(ref val4)).sqrMagnitude < 0.04f) { num3 = 0f; } else { float num4 = Vector3.Dot(val2, ((Vector3)(ref val4)).normalized); if (num4 < num) { continue; } num3 = Mathf.Acos(Mathf.Clamp(num4, -1f, 1f)); } if (num3 < num2) { num2 = num3; result = val3; } } } return result; } private Character PickNearestPartner() { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) Character val = null; float num = float.MaxValue; for (int i = 0; i + 1 < pairBuf.Count; i += 2) { Character val2 = pairBuf[i]; Character val3 = pairBuf[i + 1]; Character val4 = (((Object)(object)val2 == (Object)(object)ch) ? val3 : (((Object)(object)val3 == (Object)(object)ch) ? val2 : null)); if (IsPullable(val4)) { float num2 = Vector3.Distance(ch.Center, val4.Center); if (num2 < num) { num = num2; val = val4; } } } if ((Object)(object)val == (Object)null && IsPullable(nearest)) { val = nearest; } return val; } private Vector3 CameraForward() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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) if ((Object)(object)cam != (Object)null) { return ((Component)cam).transform.forward; } Vector3 val = ch.Head - ch.Center; if (!(((Vector3)(ref val)).sqrMagnitude > 0.0001f)) { return Vector3.zero; } return ((Vector3)(ref val)).normalized; } private bool IsPullable(Character c) { if ((Object)(object)c == (Object)null || (Object)(object)c == (Object)(object)ch) { return false; } if ((Object)(object)c.Ghost != (Object)null) { return false; } if (players.Count > 0 && !players.Contains(c)) { return false; } return true; } private void ApplyPoison(float amount) { if (!(amount <= 0f)) { CharacterRefs refs = ch.refs; if (refs != null && !((Object)(object)refs.afflictions == (Object)null)) { refs.afflictions.AddStatus((STATUSTYPE)3, amount, false, false, true, false); } } } internal static void FateNotify(string msg) { try { if ((Object)(object)uiNotify == (Object)null) { UI_Notifications[] array = Resources.FindObjectsOfTypeAll<UI_Notifications>(); if (array != null && array.Length != 0) { uiNotify = array[0]; } } if ((Object)(object)uiNotify != (Object)null) { uiNotify.AddNotification(msg); } } catch { } } private void NotifyShared(STATUSTYPE type, float amount) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Invalid comparison between Unknown and I4 if (Plugin.NotifyShared == null || !Plugin.NotifyShared.Value) { return; } float time = Time.time; string msg; if ((int)type == 0) { if (time - notifyInjuryAt < 1.2f) { return; } notifyInjuryAt = time; msg = "命运锁链:队友受伤,你被分摊 -" + Mathf.RoundToInt(amount * 100f) + "%"; } else if ((int)type == 1) { if (time - notifyHungerAt < 1.2f) { return; } notifyHungerAt = time; msg = "命运锁链:队友饿了,你的饥饿也上来了 +" + Mathf.RoundToInt(amount * 100f) + "%"; } else { if (time - notifyPoisonAt < 1.2f) { return; } notifyPoisonAt = time; msg = "命运锁链:队友中毒,毒素传给你了 +" + Mathf.RoundToInt(amount * 100f) + "%"; } FateNotify(msg); } private void HandleDebugKey() { if (!Plugin.DebugRequested || (Object)(object)ch == (Object)null || !ch.IsLocal) { return; } Plugin.DebugRequested = false; try { if (Plugin.SelfCheckPanel != null) { Plugin.SelfCheckPanel.Value = !Plugin.SelfCheckPanel.Value; } } catch { } try { DumpDebug(); } catch (Exception ex) { Warn("调试读数失败 - " + ex.Message); } } private static void Warn(string msg) { Plugin.LogWarn(msg); } private void DumpDebug() { //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) if (players.Count == 0) { CollectPlayers(); } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("===== 命运绑定 v0.4.4 调试读数 " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " ====="); stringBuilder.AppendLine("本机生效=" + active + " 场上玩家=" + players.Count + " 最近队友距离=" + (((Object)(object)nearest != (Object)null) ? (nearestDist.ToString("F1") + "m") : "无")); stringBuilder.AppendLine("拖拽开关=" + (Plugin.EnableTug != null && Plugin.EnableTug.Value) + " 绷紧度=" + (((Object)(object)nearest != (Object)null) ? Tautness(ch.Center, nearest.Center).ToString("F2") : "-") + " (0.00 松 / 1.00 满;≥0.5 就会弹绷紧提示)"); FateNotify("命运绑定 v0.4.4\u3000生效=" + active + "\u3000读到玩家=" + players.Count); if (players.Count == 0) { FateNotify("没读到玩家:大厅内不生效,请进关卡、两人以上再按 F10"); } for (int i = 0; i < players.Count; i++) { Character val = players[i]; if (!((Object)(object)val == (Object)null)) { CharacterAfflictions component = ((Component)val).GetComponent<CharacterAfflictions>(); string text = ((!((Object)(object)component != (Object)null) || component.currentStatuses == null) ? (" 玩家 " + i + ":状态不可读") : ((((Object)(object)val == (Object)(object)ch) ? "★我 " : " 队友 ") + "受伤 " + St(component, (STATUSTYPE)0) + " 饥饿 " + St(component, (STATUSTYPE)1) + " 中毒 " + St(component, (STATUSTYPE)3) + " 距离 " + Vector3.Distance(ch.Center, val.Center).ToString("F1") + "m")); stringBuilder.AppendLine(text); FateNotify(text); } } string text2 = stringBuilder.ToString(); Plugin.LogLine(text2); try { string text3 = null; try { text3 = Paths.BepInExRootPath; } catch { } if (string.IsNullOrEmpty(text3)) { text3 = Path.Combine(Environment.CurrentDirectory, "BepInEx"); } Directory.CreateDirectory(text3); File.AppendAllText(Path.Combine(text3, "fatebound-debug.txt"), text2 + Environment.NewLine); } catch (Exception ex) { Warn("写调试文件失败 - " + ex.Message); } } private static string St(CharacterAfflictions a, STATUSTYPE t) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return a.GetCurrentStatus(t).ToString("F3"); } private void FixedUpdate() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_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_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0309: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Unknown result type (might be due to invalid IL or missing references) //IL_0384: 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_0155: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) if (!active || (Object)(object)ch == (Object)null) { dragAccum = 0f; return; } if (Plugin.EnableTug == null || !Plugin.EnableTug.Value) { dragAccum = 0f; return; } if (lastCenterReady && Vector3.Distance(lastCenter, ch.Center) > 60f) { lastCenterReady = false; } if (!lastCenterReady) { lastCenter = ch.Center; lastCenterReady = true; return; } Vector3 val = Vector3.zero; Vector3 val2 = Vector3.zero; float num = 0f; int num2 = 0; for (int i = 0; i + 1 < pairBuf.Count; i += 2) { Character val3 = pairBuf[i]; Character val4 = pairBuf[i + 1]; Character val5 = (((Object)(object)val3 == (Object)(object)ch) ? val4 : (((Object)(object)val4 == (Object)(object)ch) ? val3 : null)); if ((Object)(object)val5 == (Object)null || (Object)(object)val5 == (Object)(object)ch) { continue; } num2++; val += TugPair(val5, out var gap); if (gap > num) { num = gap; } if (Plugin.HardTether == null || !Plugin.HardTether.Value) { continue; } float num3 = Vector3.Distance(ch.Center, val5.Center); float num4 = ((Plugin.MaxDistance != null) ? Plugin.MaxDistance.Value : 30f) * ((Plugin.HardLimitScale != null) ? Mathf.Max(0.2f, Plugin.HardLimitScale.Value) : 1f); if (!(num3 > num4) || !lastCenterReady) { continue; } Vector3 val6 = ch.Center - val5.Center; if (!(((Vector3)(ref val6)).sqrMagnitude > 0.01f)) { continue; } ((Vector3)(ref val6)).Normalize(); if (Vector3.Dot((ch.Center - lastCenter) / Mathf.Max(0.0001f, Time.fixedDeltaTime), val6) > 0.4f) { float num5 = num3 - num4; float num6 = ((Plugin.HardPullForce != null) ? Plugin.HardPullForce.Value : 520f); Vector3 val7 = val2; Vector3 val8 = val5.Center - ch.Center; val2 = val7 + ((Vector3)(ref val8)).normalized * (num6 * (1f + Mathf.Clamp01(num5 / 8f) * 3f)); if (!hardWarned && Time.time - hardWarnedAt > 6f) { hardWarned = true; hardWarnedAt = Time.time; FateNotify("命运锁链绷到底了 —— 跑不出 " + num4.ToString("F0") + " 米"); } } } if (num2 == 0 && (Object)(object)nearest != (Object)null) { val += TugPair(nearest, out var gap2); if (gap2 > num) { num = gap2; } } if (num > 0f) { dragAccum += Time.fixedDeltaTime; } else { dragAccum = 0f; } float num7 = ((Plugin.VerticalPullScale != null) ? Mathf.Clamp01(Plugin.VerticalPullScale.Value) : 0.35f); val.y *= num7; if (((Vector3)(ref val2)).sqrMagnitude > 0.0001f) { val += val2; hardWarned = false; } if (!(((Vector3)(ref val)).sqrMagnitude <= 0.0001f)) { ch.RPCA_AddForceAtPosition(val, ch.Center, 8f); } } private void Deactivate() { ResetPenalty(); sharePrimed = false; } private void ResetPenalty() { graceTimer = 0f; poisonAccum = 0f; dragAccum = 0f; } private void Draw() { if (!ch.IsLocal) { Hide(); return; } CheckCampfireAdvance(); BuildPairs(); bool flag = Plugin.PullAimRequired == null || Plugin.PullAimRequired.Value; bool flag2 = Plugin.AimHighlight == null || Plugin.AimHighlight.Value; aimHint = ((flag && flag2) ? PickAimedPartner() : null); int num = pairBuf.Count / 2; while (links.Count < num) { links.Add(new ChainLink()); } for (int i = num; i < links.Count; i++) { HideLink(links[i]); } bool usePhysics = Plugin.ChainPhysics == null || Plugin.ChainPhysics.Value; for (int j = 0; j < num; j++) { DrawOne(links[j], pairBuf[j * 2], pairBuf[j * 2 + 1], usePhysics); } } private void CheckCampfireAdvance() { if (Plugin.CampfireShuffle == null || !Plugin.CampfireShuffle.Value || !PhotonNetwork.InRoom) { return; } if (!segmentPropTried) { segmentPropTried = true; Type type = Type.GetType("MapHandler, Assembly-CSharp"); if (type == null) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { if (!(type == null)) { break; } try { type = assemblies[i].GetType("MapHandler", throwOnError: false); } catch { } } } if (type != null) { segmentProp = type.GetProperty("CurrentSegmentNumber", BindingFlags.Static | BindingFlags.Public); } } if (segmentProp == null) { return; } object value; try { value = segmentProp.GetValue(null, null); } catch { segmentProp = null; return; } if (value == null) { return; } if (!segmentKnown) { segmentKnown = true; lastSegment = value; } else if (!value.Equals(lastSegment)) { lastSegment = value; avoidBuf.Clear(); for (int j = 0; j < pairBuf.Count; j++) { avoidBuf.Add(pairBuf[j]); } shuffleSeed = value.GetHashCode() & 0x7FFFFFFF; if (shuffleSeed == 0) { shuffleSeed = 1; } FateNotify("命运锁链顺序重排 —— 这一段路重新连过了"); } } private bool WasPaired(Character x, Character y) { for (int i = 0; i + 1 < lastPairsBuf.Count; i += 2) { if (((Object)(object)lastPairsBuf[i] == (Object)(object)x && (Object)(object)lastPairsBuf[i + 1] == (Object)(object)y) || ((Object)(object)lastPairsBuf[i] == (Object)(object)y && (Object)(object)lastPairsBuf[i + 1] == (Object)(object)x)) { return true; } } return false; } private bool Avoids(Character x, Character y) { for (int i = 0; i + 1 < avoidBuf.Count; i += 2) { if (((Object)(object)avoidBuf[i] == (Object)(object)x && (Object)(object)avoidBuf[i + 1] == (Object)(object)y) || ((Object)(object)avoidBuf[i] == (Object)(object)y && (Object)(object)avoidBuf[i + 1] == (Object)(object)x)) { return true; } } return false; } private void BuildPairs() { //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0246: 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) lastPairsBuf.Clear(); for (int i = 0; i < pairBuf.Count; i++) { lastPairsBuf.Add(pairBuf[i]); } pairBuf.Clear(); bool flag = false; for (int j = 0; j < players.Count; j++) { if ((Object)(object)players[j] == (Object)(object)ch) { flag = true; break; } } if (!flag) { return; } switch ((Plugin.ChainTopology != null) ? Plugin.ChainTopology.Value : FateChainTopology.Ring) { case FateChainTopology.AllPairs: { for (int k = 0; k < players.Count; k++) { if (pairBuf.Count >= 32) { break; } for (int l = k + 1; l < players.Count; l++) { if (pairBuf.Count >= 32) { break; } pairBuf.Add(players[k]); pairBuf.Add(players[l]); } } return; } case FateChainTopology.NearestWeb: { for (int m = 0; m < players.Count; m++) { if (pairBuf.Count >= 32) { break; } int num = -1; float num2 = float.MaxValue; int num3 = -1; float num4 = float.MaxValue; for (int n = 0; n < players.Count; n++) { if (n != m) { float num5 = Vector3.Distance(players[m].Center, players[n].Center); if (num5 < num2) { num2 = num5; num = n; } if (avoidBuf.Count > 0 && !Avoids(players[m], players[n]) && num5 < num4) { num4 = num5; num3 = n; } } } int num6 = -1; for (int num7 = 0; num7 < players.Count; num7++) { if (num7 != m && num7 != num && WasPaired(players[m], players[num7])) { num6 = num7; break; } } if (num6 >= 0 && Vector3.Distance(players[m].Center, players[num6].Center) < num2 * 1.15f) { num = num6; } if (num3 >= 0) { num = num3; } if (num >= 0) { int num8 = ((m < num) ? m : num); int num9 = ((m < num) ? num : m); if (!PairExists(num8, num9)) { pairBuf.Add(players[num8]); pairBuf.Add(players[num9]); } } } return; } } BuildRingOrder(); int count = ringOrder.Count; for (int num10 = 0; num10 < count; num10++) { if (pairBuf.Count >= 32) { break; } Character item = ringOrder[num10]; Character item2 = ringOrder[(num10 + 1) % count]; pairBuf.Add(item); pairBuf.Add(item2); } } private bool PairExists(int a, int b) { for (int i = 0; i + 1 < pairBuf.Count; i += 2) { int num = players.IndexOf(pairBuf[i]); int num2 = players.IndexOf(pairBuf[i + 1]); if ((num == a && num2 == b) || (num == b && num2 == a)) { return true; } } return false; } private void BuildRingOrder() { //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) ringOrder.Clear(); int count = players.Count; if (count == 0) { return; } if (shuffleSeed != 0) { for (int i = 0; i < count; i++) { ringOrder.Add(players[i]); } ringOrder.Sort((Character p, Character q) => (Mathf.Abs(p.Center.x - q.Center.x) > 0.001f) ? p.Center.x.CompareTo(q.Center.x) : p.Center.z.CompareTo(q.Center.z)); Random random = new Random(shuffleSeed); for (int num = 0; num + 1 < count; num++) { if (random.NextDouble() < 0.65) { Character value = ringOrder[num]; ringOrder[num] = ringOrder[num + 1]; ringOrder[num + 1] = value; } } return; } int num2 = 0; for (int num3 = 1; num3 < count; num3++) { if (((Object)players[num3]).GetInstanceID() < ((Object)players[num2]).GetInstanceID()) { num2 = num3; } } if (ringUsed.Length < count) { ringUsed = new bool[count]; } for (int num4 = 0; num4 < count; num4++) { ringUsed[num4] = false; } int num5 = num2; for (int num6 = 0; num6 < count; num6++) { ringOrder.Add(players[num5]); ringUsed[num5] = true; int num7 = -1; float num8 = float.MaxValue; for (int num9 = 0; num9 < count; num9++) { if (!ringUsed[num9]) { float num10 = Vector3.Distance(players[num5].Center, players[num9].Center); if (num10 < num8) { num8 = num10; num7 = num9; } } } if (num7 >= 0) { num5 = num7; continue; } break; } } private void DrawOne(ChainLink L, Character a, Character b, bool usePhysics) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_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_00bd: 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_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_0355: Unknown result type (might be due to invalid IL or missing references) //IL_03f5: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Unknown result type (might be due to invalid IL or missing references) Vector3 center = a.Center; Vector3 center2 = b.Center; EnsureLine(L); if (L.chain != null && ((Object)(object)L.lastA != (Object)(object)a || (Object)(object)L.lastB != (Object)(object)b)) { L.chain.Reset(center, center2); } L.lastA = a; L.lastB = b; if (!usePhysics) { ((Renderer)L.line).enabled = true; L.line.positionCount = 2; L.line.SetPosition(0, NearCameraTrim(center, center2, Vector3.Distance(center, center2) * 0.5f)); L.line.SetPosition(1, center2); if ((Object)(object)L.linkRend != (Object)null) { ((Renderer)L.linkRend).enabled = false; } ApplyChainColor(L, center, center2, (Object)(object)a == (Object)(object)ch || (Object)(object)b == (Object)(object)ch, (Object)(object)aimHint != (Object)null && ((Object)(object)a == (Object)(object)aimHint || (Object)(object)b == (Object)(object)aimHint)); return; } if (L.chain == null) { int num = ((Plugin.ChainMaxPoints != null) ? Plugin.ChainMaxPoints.Value : 56); L.chain = new PhysicsChain((Plugin.ChainSpacing != null) ? Plugin.ChainSpacing.Value : 0.55f, (Plugin.ChainThickness != null) ? Plugin.ChainThickness.Value : 0.15f, Mathf.Max(num, 200), (Plugin.ChainSlack != null) ? Plugin.ChainSlack.Value : 0.01f, 0.65f, 0.15f, (Plugin.ChainMaxSag != null) ? Plugin.ChainMaxSag.Value : 0.6f); } if ((Object)(object)L.a != (Object)(object)a || (Object)(object)L.b != (Object)(object)b) { L.chain.Reset(center, center2); L.a = a; L.b = b; } int num2 = (((Object)(object)a == (Object)(object)ch || (Object)(object)b == (Object)(object)ch) ? 1 : ((Plugin.FarChainInterval != null) ? Mathf.Clamp(Plugin.FarChainInterval.Value, 1, 10) : 3)); if (num2 <= 1 || (Time.frameCount + L.GetHashCode()) % num2 == 0) { L.chain.Step(center, center2, Time.deltaTime * (float)num2); } Vector3[] points = L.chain.Points; int i = 0; int num3 = points.Length - 1; float num4 = ((Plugin.ChainHideRadius != null) ? Plugin.ChainHideRadius.Value : 0.85f); if (num4 > 0f) { Vector3 val; for (val = CameraPosition(); i < num3 - 1 && Vector3.Distance(points[i], val) <= num4; i++) { } while (num3 > i + 1 && Vector3.Distance(points[num3], val) <= num4) { num3--; } } int num5 = num3 - i + 1; if (num5 < 2) { HideLink(L); return; } bool flag = (Plugin.SolidChainLinks == null || Plugin.SolidChainLinks.Value) && (Object)(object)L.linkRend != (Object)null && (Object)(object)L.linkMat != (Object)null; ((Renderer)L.line).enabled = !flag; if (!flag) { L.line.positionCount = num5; for (int j = 0; j < num5; j++) { L.line.SetPosition(j, points[i + j]); } } else { int num6 = ((Plugin.MeshInterval != null) ? Mathf.Clamp(Plugin.MeshInterval.Value, 1, 6) : 2); int num7 = links.IndexOf(L); if (num7 >= 0 && num7 < meshCd.Length) { if (meshCd[num7] <= 0) { BuildLinkMesh(L, points, i, num3); meshCd[num7] = num6 - 1; } else { meshCd[num7]--; } } else { BuildLinkMesh(L, points, i, num3); } } ApplyChainColor(L, center, center2, (Object)(object)a == (Object)(object)ch || (Object)(object)b == (Object)(object)ch, (Object)(object)aimHint != (Object)null && ((Object)(object)a == (Object)(object)aimHint || (Object)(object)b == (Object)(object)aimHint)); } private Vector3 NearCameraTrim(Vector3 from, Vector3 to, float maxAdvance) { //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_002a: 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_0021: 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_004b: 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) float num = ((Plugin.ChainHideRadius != null) ? Plugin.ChainHideRadius.Value : 0.85f); if (num <= 0f) { return from; } Vector3 val = CameraPosition(); if (Vector3.Distance(from, val) > num) { return from; } float num2 = Mathf.Min(num, Mathf.Max(0f, maxAdvance)); return Vector3.MoveTowards(from, to, num2); } private Vector3 CameraPosition() { //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_008f: 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_0074: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)cam == (Object)null) { cam = Camera.main; if ((Object)(object)cam == (Object)null) { Camera[] allCameras = Camera.allCameras; for (int i = 0; i < allCameras.Length; i++) { if ((Object)(object)allCameras[i] != (Object)null && ((Behaviour)allCameras[i]).enabled) { cam = allCameras[i]; break; } } } } if ((Object)(object)cam != (Object)null) { return ((Component)cam).transform.position; } return ch.Center + Vector3.up * 0.6f; } private void ApplyChainColor(ChainLink L, Vector3 a, Vector3 b, bool involvesMe, bool aimed)