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 ChattyBones v0.1.1
ChattyBones.dll
Decompiled 2 weeks 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.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ChattyBones.Logic; using ChattyBones.Patches; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using YamlDotNet.Core; using YamlDotNet.RepresentationModel; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("ChattyBones")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.1.0")] [assembly: AssemblyInformationalVersion("0.1.1+e928e3962c930327c06da4003b5c19ee09e02b7a")] [assembly: AssemblyProduct("ChattyBones")] [assembly: AssemblyTitle("ChattyBones")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ChattyBones { internal static class Allies { private readonly struct Tracked { internal ZDOID Id { get; } internal float Missing { get; } internal Tracked(ZDOID id, float missing) { //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) Id = id; Missing = missing; } internal Tracked Missed(float dt) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return new Tracked(Id, Missing + dt); } } private static readonly List<Tracked> Seen = new List<Tracked>(); private static float _untilRetry; private const float RetrySeconds = 1f; internal static void Poll(float dt) { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: 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_00f3: 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_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ChatterComponent.All.Count == 0) { Seen.Clear(); Blame.Clear(); return; } if (_untilRetry > 0f) { _untilRetry -= dt; } for (int i = 0; i < Seen.Count; i++) { Seen[i] = Seen[i].Missed(dt); } float value = ModConfig.AllyGreetingDistance.Value; float num = value * value; List<Player> allPlayers = Player.GetAllPlayers(); for (int j = 0; j < allPlayers.Count; j++) { Player val = allPlayers[j]; if ((Object)(object)val == (Object)null || val == localPlayer || ((Character)val).IsDead()) { continue; } float distanceSq; ChatterComponent chatterComponent = NearestTo(((Component)val).transform.position, out distanceSq); if ((Object)(object)chatterComponent == (Object)null || distanceSq > num) { continue; } ZDOID zDOID = ((Character)val).GetZDOID(); if (zDOID == ZDOID.None) { continue; } int num2 = IndexOf(zDOID); if (num2 >= 0) { Seen[num2] = new Tracked(zDOID, 0f); } else if (!(_untilRetry > 0f)) { if (Greet(val, chatterComponent)) { Seen.Add(new Tracked(zDOID, 0f)); } else { _untilRetry = 1f; } } } float value2 = ModConfig.AllyGreetingForgetSeconds.Value; for (int num3 = Seen.Count - 1; num3 >= 0; num3--) { if (Seen[num3].Missing >= value2) { Seen.RemoveAt(num3); } } } private static bool Greet(Player ally, ChatterComponent nearest) { int subject = Summons.PrefabOf((Character)(object)ally); Player ally2 = ally; if (Chatter.TrySpeak(nearest, ChatterEvent.AllyArrived, subject, null, null, null, default(LineDetails), ally2)) { return true; } ally2 = ally; return Chatter.SpeakAny(ChatterEvent.AllyArrived, subject, null, null, null, default(LineDetails), ally2); } private static ChatterComponent NearestTo(Vector3 point, out float distanceSq) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_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) ChatterComponent result = null; distanceSq = float.MaxValue; List<ChatterComponent> all = ChatterComponent.All; for (int i = 0; i < all.Count; i++) { Character character = all[i].Character; if (!((Object)(object)character == (Object)null) && all[i].IsOwned) { Vector3 val = ((Component)character).transform.position - point; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < distanceSq) { distanceSq = sqrMagnitude; result = all[i]; } } } return result; } internal static Player Nearby(Vector3 from) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return null; } float value = ModConfig.AllyGreetingDistance.Value; float num = value * value; Player result = null; int num2 = 0; List<Player> allPlayers = Player.GetAllPlayers(); for (int i = 0; i < allPlayers.Count; i++) { Player val = allPlayers[i]; if ((Object)(object)val == (Object)null || val == localPlayer || ((Character)val).IsDead()) { continue; } Vector3 val2 = ((Component)val).transform.position - from; if (!(((Vector3)(ref val2)).sqrMagnitude > num)) { num2++; if (Random.Range(0, num2) == 0) { result = val; } } } return result; } private static int IndexOf(ZDOID id) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < Seen.Count; i++) { if (Seen[i].Id == id) { return i; } } return -1; } } internal static class Blame { private readonly struct Entry { internal Character Attacker { get; } internal float At { get; } internal Entry(Character attacker, float at) { Attacker = attacker; At = at; } } private const float RememberSeconds = 15f; private const int Crowd = 64; private static readonly Dictionary<ZDOID, Entry> Recent = new Dictionary<ZDOID, Entry>(); private static readonly List<ZDOID> Stale = new List<ZDOID>(); internal static void Note(Character victim, HitData hit) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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) if ((Object)(object)victim == (Object)null || hit == null || !hit.HaveAttacker()) { return; } Character attacker = hit.GetAttacker(); if (!((Object)(object)attacker == (Object)null)) { ZDOID zDOID = victim.GetZDOID(); if (!(zDOID == ZDOID.None)) { Tidy(); Recent[zDOID] = new Entry(attacker, Time.time); } } } internal static Character Take(Character victim) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_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_0024: 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) if ((Object)(object)victim == (Object)null) { return null; } ZDOID zDOID = victim.GetZDOID(); if (zDOID == ZDOID.None || !Recent.TryGetValue(zDOID, out var value)) { return null; } Recent.Remove(zDOID); if (!(Time.time - value.At > 15f) && !((Object)(object)value.Attacker == (Object)null)) { return value.Attacker; } return null; } internal static void Clear() { Recent.Clear(); } private static void Tidy() { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) if (Recent.Count < 64) { return; } float time = Time.time; Stale.Clear(); foreach (KeyValuePair<ZDOID, Entry> item in Recent) { if (time - item.Value.At > 15f) { Stale.Add(item.Key); } } for (int i = 0; i < Stale.Count; i++) { Recent.Remove(Stale[i]); } Stale.Clear(); } } internal static class Chatter { private static ChatterBudget _budget; private static LineChooser _chooser; private static LinePack _pack; private static Random _random; private static float _untilSweep; private static bool _wasEnabled = true; private const float SweepSeconds = 0.25f; private static string _reportedBadEvents; private static int _nextSpeaker; internal static IReadOnlyList<string> Personalities => _pack.Personalities; internal static int PackGeneration { get; private set; } internal static float IdleSeconds { get { if (!ChatterPresets.TryIdleSeconds(ModConfig.IdleChatter.Value, out var seconds)) { return ModConfig.IdleSeconds.Value; } return seconds; } } internal static void Init() { _pack = PackFile.Load(); _chooser = new LineChooser(); _budget = new ChatterBudget(SettingsFromConfig()); _random = new Random(); ChattyBonesPlugin.Log.LogInfo((object)("Line pack loaded with " + _pack.Personalities.Count + " personalities.")); ReportUncoveredEvents(); ReportUnusableContexts(); } private static void ReloadPack() { LinePack linePack = PackFile.Reload(); if (linePack != null) { _pack = linePack; PackGeneration++; ChattyBonesPlugin.Log.LogInfo((object)("Line pack reloaded with " + _pack.Personalities.Count + " personalities.")); ReportUncoveredEvents(); ReportUnusableContexts(); } } private static void ReportUncoveredEvents() { IReadOnlyList<ChatterEvent> readOnlyList = _pack.EventsWithNoLines(); if (readOnlyList.Count != 0) { ChattyBonesPlugin.Log.LogInfo((object)("Silent by omission - your pack has no lines for " + string.Join(", ", readOnlyList) + ". That is fine if you meant it. If you did not, ChattyBones.lines.default.yaml next to your own pack is what this version shipped with.")); } } private static void ReportUnusableContexts() { IReadOnlyList<string> readOnlyList = Contexts.Unusable(_pack); if (readOnlyList.Count != 0) { ChattyBonesPlugin.Log.LogWarning((object)("These groups are tagged with something that is not a real value, so nothing will ever match them: " + string.Join(", ", readOnlyList) + ". Biome names are the game's own spellings, like Meadows, BlackForest, Swamp, Mountain, Plains, Mistlands, AshLands and DeepNorth. None and All are real names in the game's list but no skeleton is ever standing in one - for lines that suit anywhere, write a plain group instead.")); } } private static ChatterSettings SettingsFromConfig() { if (!ChatterPresets.TryGaps(ModConfig.ChatterFrequency.Value, out var gaps)) { gaps = new ChatterGaps(ModConfig.MinGapSeconds.Value, ModConfig.SpeakerCooldownSeconds.Value, ModConfig.SquadEchoWindowSeconds.Value); } return new ChatterSettings(gaps.MinGapSeconds, ModConfig.PreemptGapSeconds.Value, gaps.SpeakerCooldownSeconds, gaps.SquadEchoWindowSeconds, Silenced()); } private static IReadOnlyList<ChatterEvent> Silenced() { IReadOnlyList<string> unknown; IReadOnlyList<ChatterEvent> named = EventList.Parse(ModConfig.SilencedEvents.Value, out unknown); string text = ((unknown.Count == 0) ? null : string.Join(", ", unknown.ToArray())); if (text != _reportedBadEvents) { _reportedBadEvents = text; if (text != null) { ChattyBonesPlugin.Log.LogWarning((object)("SilencedEvents names something that is not an event, so it does nothing: " + text + ". The spellings are listed at the top of ChattyBones.lines.default.yaml.")); } } return EventList.Silenced(ModConfig.ChatterFrequency.Value, ModConfig.IdleChatter.Value, named); } internal static string DescribeChoice(string personality, ChatterEvent kind, IReadOnlyList<string> contexts) { if (_pack == null || !_pack.SelectBands(personality, kind, contexts, out var space, out var own, out var shared)) { return null; } bool personal = _pack.HasOwnLines(personality, kind); string text = Describe(space, own, personal); string text2 = Describe(space, shared, personal: false); if (text == null) { return text2; } if (text2 != null) { return text + " + " + text2; } return text; } private static string Describe(LineSpace space, LineSpace.Window window, bool personal) { if (window.IsEmpty) { return null; } for (int i = 0; i < space.Groups.Count; i++) { LineSpace.Group obj = space.Groups[i]; if (obj.Offset == window.Offset && obj.Length == window.Length) { return (obj.Context ?? "plain") + ", " + obj.Length + ((obj.Length == 1) ? " line" : " lines") + ", " + ((personal && obj.Personal) ? "its own" : "shared"); } } return window.Length + " lines, the whole numbering - nothing it is tagged for matches"; } internal static void RefreshSettings() { if (_budget != null) { _budget.Settings = SettingsFromConfig(); } } internal static bool TrySpeak(ChatterComponent speaker, ChatterEvent kind, int subject, string targetName, Character companion, string companionName = null, LineDetails details = default(LineDetails), Player ally = null) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.Enabled.Value || (Object)(object)speaker == (Object)null || _budget == null) { return false; } if (!speaker.IsOwned) { return false; } Character character = speaker.Character; if ((Object)(object)character == (Object)null) { return false; } long speakerId = speaker.SpeakerId; float time = Time.time; if (!_budget.CanClaim(speakerId, kind, subject, time, out var why)) { Trace(speaker, kind, why); return false; } string companion2 = companionName ?? Summons.NameOf(companion); string ally2 = Mirror.PlayerName(ally); EventTokens.Note(kind, targetName, companion2, ally2, details); Character val = companion; if (EventTokens.ShouldFillIn(kind, TokenSet.Companion)) { val = AnotherOf(speaker); companion2 = Summons.NameOf(val); } Player val2 = ally; if (EventTokens.ShouldFillIn(kind, TokenSet.Ally)) { val2 = Allies.Nearby(((Component)character).transform.position); ally2 = Mirror.PlayerName(val2); } LineTokens tokens = new LineTokens(targetName, Mirror.PlayerName(Player.m_localPlayer), Summons.NameOf(character), companion2, ally2, Mirror.Localize(details)); if (!_chooser.TryChoose(_pack, speaker.Personality, kind, tokens, _random, out var lineRef, out var line, Contexts.For(character))) { Trace(speaker, kind, "nothing it could say as " + (speaker.Personality ?? "no personality yet")); return false; } if (Speech.Say(character, line, _pack.Colors.TagFor(kind)) == Drew.Nothing) { Trace(speaker, kind, "had \"" + line + "\" but nothing was drawn"); return false; } _budget.Commit(speakerId, kind, subject, time); speaker.OnSpoke(kind, lineRef, subject, IdOf(val), IdOf((Character)(object)val2), details); Trace(speaker, kind, "said \"" + line + "\""); return true; } private static ZDOID IdOf(Character who) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)who == (Object)null)) { return who.GetZDOID(); } return ZDOID.None; } internal static bool Hear(ChatterComponent listener, Utterance said, LineDetails raw, ZDOID companion, ZDOID ally) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.Enabled.Value || !ModConfig.HearOthers.Value || _pack == null) { return false; } if (_budget != null && _budget.Settings.IsDisabled(said.Kind)) { return false; } Character character = listener.Character; if ((Object)(object)character == (Object)null) { return false; } TokenSet tokenSet = EventTokens.PromisedFor(said.Kind); LineTokens tokens = new LineTokens(((tokenSet & TokenSet.Target) == 0) ? null : Mirror.CreatureName(said.Subject), Mirror.SummonerName(character), Summons.NameOf(character), Mirror.NameFor(companion), Mirror.NameFor(ally), Mirror.Localize(raw)); if (!_pack.TryPickRenderable(listener.Personality, said.Kind, said.LineRef, tokens, out var line)) { Trace(listener, said.Kind, "heard it, but has nothing it could say back"); return false; } if (Speech.Say(character, line, _pack.Colors.TagFor(said.Kind)) == Drew.Nothing) { return false; } Trace(listener, said.Kind, "heard \"" + line + "\""); return true; } internal static void Trace(ChatterComponent speaker, ChatterEvent kind, ChatterRefusal why) { if (ModConfig.LogChatter.Value) { Trace(speaker, kind, "turned down by " + why); } } internal static void Trace(ChatterComponent speaker, ChatterEvent kind, string what) { if (ModConfig.LogChatter.Value) { string text = (((Object)(object)speaker.Character == (Object)null) ? "?" : (Summons.NameOf(speaker.Character) ?? "?")); ChattyBonesPlugin.Log.LogInfo((object)("[chatter] " + text + " / " + kind.ToString() + ": " + what)); } } internal static bool SpeakAny(ChatterEvent kind, int subject, string targetName, Character companion, string companionName = null, LineDetails details = default(LineDetails), Player ally = null) { List<ChatterComponent> all = ChatterComponent.All; int count = all.Count; if (count == 0) { return false; } int num = (_nextSpeaker++ & 0x7FFFFFFF) % count; for (int i = 0; i < count; i++) { ChatterComponent chatterComponent = all[(num + i) % count]; if ((!((Object)(object)companion != (Object)null) || !((Object)(object)chatterComponent.Character == (Object)(object)companion)) && TrySpeak(chatterComponent, kind, subject, targetName, companion, companionName, details, ally)) { return true; } } return false; } internal static Character AnotherOf(ChatterComponent speaker) { List<ChatterComponent> all = ChatterComponent.All; if (all.Count < 2) { return null; } int num = _random.Next(0, all.Count - 1); for (int i = 0; i < all.Count; i++) { ChatterComponent chatterComponent = all[i]; if (chatterComponent != speaker && num-- == 0) { if (!((Object)(object)chatterComponent.Character == (Object)null)) { return chatterComponent.Character; } return null; } } return null; } internal static void Tick(float dt) { if (PackFile.ShouldReload(dt)) { ReloadPack(); } if (!ModConfig.Enabled.Value) { if (_wasEnabled) { _wasEnabled = false; List<ChatterComponent> all = ChatterComponent.All; for (int i = 0; i < all.Count; i++) { all[i].ForgetWhatWasHeard(); } } return; } _wasEnabled = true; Blow.FlushStale(); _untilSweep -= dt; if (_untilSweep > 0f) { return; } float dt2 = 0.25f - _untilSweep; _untilSweep = 0.25f; try { Raids.Poll(); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a raid: " + ex)); } try { Allies.Poll(dt2); } catch (Exception ex2) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over an ally: " + ex2)); } List<ChatterComponent> all2 = ChatterComponent.All; for (int j = 0; j < all2.Count; j++) { try { all2[j].Sweep(dt2); } catch (Exception ex3) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled watching a skeleton: " + ex3)); } } } } internal sealed class ChatterComponent : MonoBehaviour { private BaseAI _ai; private ZNetView _view; private bool _hadTarget; private Character _lastTarget; private int _lastTargetPrefab; private string _lastTargetName; private float _untilIdle; [CompilerGenerated] private long <SpeakerId>k__BackingField; [CompilerGenerated] private string <Personality>k__BackingField; private int _personalityGeneration = -1; private bool _atHome; private int _sweepsOutside; private const int SweepsBeforeLeaving = 4; private int _untilHomeCheck; private const int SweepsBetweenHomeChecks = 8; private readonly ListenState _heard = new ListenState(); private static readonly int PersonalityKey = StringExtensionMethods.GetStableHashCode("cb_personality"); private static readonly int UtteranceKey = StringExtensionMethods.GetStableHashCode("cb_utterance"); private static readonly int SubjectKey = StringExtensionMethods.GetStableHashCode("cb_subject"); private static readonly int DetailsKey = StringExtensionMethods.GetStableHashCode("cb_details"); private static readonly KeyValuePair<int, int> CompanionKey = ZDO.GetHashZDOID("cb_companion"); private static readonly KeyValuePair<int, int> AllyKey = ZDO.GetHashZDOID("cb_ally"); internal static List<ChatterComponent> All { get; } = new List<ChatterComponent>(); internal Character Character { get; private set; } internal long SpeakerId { get { if (<SpeakerId>k__BackingField == 0L) { return <SpeakerId>k__BackingField = ComputeSpeakerId(); } return <SpeakerId>k__BackingField; } } internal string Personality { get { if (<Personality>k__BackingField != null && _personalityGeneration == Chatter.PackGeneration) { return <Personality>k__BackingField; } string text = ResolvePersonality(); if (text != null) { <Personality>k__BackingField = text; _personalityGeneration = Chatter.PackGeneration; } return text; } } internal bool IsOwned { get { if ((Object)(object)Character != (Object)null && (Object)(object)_view != (Object)null && _view.IsValid()) { return _view.IsOwner(); } return false; } } private void Awake() { Character = ((Component)this).GetComponent<Character>(); _ai = ((Component)this).GetComponent<BaseAI>(); _view = ((Component)this).GetComponent<ZNetView>(); _untilIdle = NextIdleGap(); } private long ComputeSpeakerId() { //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) if ((Object)(object)Character == (Object)null || (Object)(object)_view == (Object)null || !_view.IsValid()) { return 0L; } ZDOID zDOID = Character.GetZDOID(); return SpeechFormat.SenderId(((ZDOID)(ref zDOID)).UserID, ((ZDOID)(ref zDOID)).ID); } private void OnEnable() { All.Add(this); } private void OnDisable() { All.Remove(this); } private void Start() { if (IsOwned && !((Object)(object)_ai == (Object)null) && !(_ai.GetTimeSinceSpawned().TotalSeconds > (double)ModConfig.SummonGreetingSeconds.Value) && Chatter.TrySpeak(this, ChatterEvent.Summoned, 0, null, null)) { Chatter.SpeakAny(ChatterEvent.CompanionSummoned, 0, null, Character); } } internal void Sweep(float dt) { bool flag = Listen(); if (!IsOwned || (Object)(object)_ai == (Object)null) { Forget(); } else { if (flag) { return; } CheckHome(); Character targetCreature = _ai.GetTargetCreature(); bool targetPresent = (Object)(object)targetCreature != (Object)null; bool sameTarget = targetCreature == _lastTarget; if (TargetWatch.LostTarget(_hadTarget, targetPresent, sameTarget)) { Settle(); } if ((Object)(object)targetCreature != (Object)null) { if (!_hadTarget) { Remember(targetCreature); Chatter.TrySpeak(this, ChatterEvent.TargetAcquired, _lastTargetPrefab, _lastTargetName, null); } _untilIdle = NextIdleGap(); } else { _untilIdle -= dt; if (_untilIdle <= 0f) { _untilIdle = NextIdleGap(); Chatter.TrySpeak(this, ChatterEvent.Idle, 0, null, null); } } } } private void CheckHome() { if (--_untilHomeCheck > 0) { return; } _untilHomeCheck = 8; if ((Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.IsSafeInHome()) { _sweepsOutside = 0; if (!_atHome) { _atHome = true; Chatter.TrySpeak(this, ChatterEvent.AtHome, 0, null, null); } } else if (_atHome) { _sweepsOutside++; if (_sweepsOutside >= 4) { _atHome = false; } } } private void Settle() { _hadTarget = false; if (ModConfig.LogChatter.Value) { Chatter.Trace(this, ChatterEvent.Killed, "stopped fighting, dead or gone: " + ((Object)(object)_lastTarget == (Object)null || _lastTarget.GetHealth() <= 0f) + " -> the kill is credited from the death itself, not from here"); } _lastTarget = null; _untilIdle = NextIdleGap(); } private void Forget() { _hadTarget = false; _lastTarget = null; _lastTargetName = null; _lastTargetPrefab = 0; } private void Remember(Character target) { _hadTarget = true; _lastTarget = target; _lastTargetPrefab = Summons.PrefabOf(target); _lastTargetName = Summons.CreatureName(target); } internal void OnSpoke(ChatterEvent kind, int lineRef, int subject, ZDOID companion, ZDOID ally, LineDetails details) { //IL_0057: 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) if (IsOwned) { ZDO zDO = _view.GetZDO(); int previous = 0; if (Utterance.TryUnpack(zDO.GetInt(UtteranceKey, 0), 0, out var utterance)) { previous = utterance.Counter; } Utterance utterance2 = new Utterance(Utterance.NextCounter(previous), kind, lineRef, subject); zDO.Set(SubjectKey, subject, false); zDO.Set(CompanionKey, companion); zDO.Set(AllyKey, ally); zDO.Set(DetailsKey, DetailWire.Pack(details)); zDO.Set(UtteranceKey, utterance2.Pack(), false); _heard.Recorded(utterance2.Counter); } } internal void ForgetWhatWasHeard() { _heard.Forget(); } private bool Listen() { //IL_0074: 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) if ((Object)(object)_view == (Object)null || !_view.IsValid()) { return false; } ZDO zDO = _view.GetZDO(); Utterance utterance; bool flag = Utterance.TryUnpack(zDO.GetInt(UtteranceKey, 0), zDO.GetInt(SubjectKey, 0), out utterance); if (!_heard.ShouldDraw(flag, flag ? utterance.Counter : 0)) { return false; } return Chatter.Hear(this, utterance, DetailsIn(zDO), zDO.GetZDOID(CompanionKey), zDO.GetZDOID(AllyKey)); } internal bool TryReadBroadcast(out Utterance said, out LineDetails details, out ZDOID companion, out ZDOID ally) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) said = default(Utterance); details = default(LineDetails); companion = ZDOID.None; ally = ZDOID.None; if ((Object)(object)_view == (Object)null || !_view.IsValid()) { return false; } ZDO zDO = _view.GetZDO(); if (!Utterance.TryUnpack(zDO.GetInt(UtteranceKey, 0), zDO.GetInt(SubjectKey, 0), out said)) { return false; } details = DetailsIn(zDO); companion = zDO.GetZDOID(CompanionKey); ally = zDO.GetZDOID(AllyKey); return true; } private static LineDetails DetailsIn(ZDO zdo) { DetailWire.TryUnpack(zdo.GetString(DetailsKey, ""), out var details); return details; } private string ResolvePersonality() { IReadOnlyList<string> personalities = Chatter.Personalities; if (personalities == null || personalities.Count == 0 || (Object)(object)_view == (Object)null || !_view.IsValid()) { return null; } ZDO zDO = _view.GetZDO(); int num = zDO.GetInt(PersonalityKey, 0); if (num <= 0) { if (!_view.IsOwner()) { return null; } num = Random.Range(0, personalities.Count) + 1; zDO.Set(PersonalityKey, num, false); } return personalities[(num - 1) % personalities.Count]; } private static float NextIdleGap() { return Chatter.IdleSeconds * Random.Range(0.75f, 1.25f); } } public sealed class ConfigurationManagerAttributes { public bool? IsAdvanced { get; set; } public int? Order { get; set; } } internal static class Contexts { private static readonly Dictionary<Biome, string> BiomeNames = new Dictionary<Biome, string>(); private static readonly Dictionary<string, string> TimeNames = BuildTimeNames(); private static Dictionary<string, string> BuildTimeNames() { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal); for (int i = 0; i < TimeOfDay.All.Length; i++) { dictionary[TimeOfDay.All[i]] = "time=" + TimeOfDay.All[i]; } return dictionary; } internal static IReadOnlyList<string> For(Character subject) { if ((Object)(object)subject == (Object)null) { return null; } List<string> list = new List<string>(); AddBiome(subject, list); AddTime(list); AddHome(list); if (list.Count != 0) { return list; } return null; } private unsafe static void AddBiome(Character subject, List<string> contexts) { //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_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) Biome val = Heightmap.FindBiome(((Component)subject).transform.position); if ((int)val != 0) { if (!BiomeNames.TryGetValue(val, out var value)) { value = "biome=" + ((object)(*(Biome*)(&val))/*cast due to .constrained prefix*/).ToString(); BiomeNames[val] = value; } contexts.Add(value); } } private static void AddTime(List<string> contexts) { if (!((Object)(object)EnvMan.instance == (Object)null)) { contexts.Add(TimeNames[TimeOfDay.Band(EnvMan.instance.GetDayFraction())]); } } private static void AddHome(List<string> contexts) { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { contexts.Add(Player.m_localPlayer.IsSafeInHome() ? "home=yes" : "home=no"); } } internal static IReadOnlyList<string> Unusable(LinePack pack) { List<string> list = new List<string>(); if (pack == null) { return list; } foreach (string item in pack.Contexts()) { int num = item.IndexOf('='); if (num >= 0) { string text = item.Substring(0, num); string text2 = item.Substring(num + 1); if (text == "biome" && (text2 == "None" || text2 == "All" || !Enum.IsDefined(typeof(Biome), text2))) { list.Add(item); } } } return list; } } internal static class DebugCommands { [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__Say; public static ConsoleEvent <1>__Who; public static ConsoleEvent <2>__Broadcast; public static ConsoleEvent <3>__Tokens; public static ConsoleEvent <4>__Gear; } private const float SearchRadius = 50f; internal static void Register() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0064: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_0099: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_0103: 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_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown object obj = <>O.<0>__Say; if (obj == null) { ConsoleEvent val = Say; <>O.<0>__Say = val; obj = (object)val; } new ConsoleCommand("cb_say", "[text] - make the nearest summoned skeleton say something", (ConsoleEvent)obj, false, false, false, true, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj2 = <>O.<1>__Who; if (obj2 == null) { ConsoleEvent val2 = Who; <>O.<1>__Who = val2; obj2 = (object)val2; } new ConsoleCommand("cb_who", "[event] - list the summoned skeletons nearby, with the group each would draw from", (ConsoleEvent)obj2, false, false, false, true, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj3 = <>O.<2>__Broadcast; if (obj3 == null) { ConsoleEvent val3 = Broadcast; <>O.<2>__Broadcast = val3; obj3 = (object)val3; } new ConsoleCommand("cb_mirror", "draw the nearest skeleton's own broadcast back at you, the way another player would see it", (ConsoleEvent)obj3, false, false, false, true, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj4 = <>O.<3>__Tokens; if (obj4 == null) { ConsoleEvent val4 = Tokens; <>O.<3>__Tokens = val4; obj4 = (object)val4; } new ConsoleCommand("cb_tokens", "what each event promises, against what it has actually supplied", (ConsoleEvent)obj4, false, false, false, true, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj5 = <>O.<4>__Gear; if (obj5 == null) { ConsoleEvent val5 = Gear; <>O.<4>__Gear = val5; obj5 = (object)val5; } new ConsoleCommand("cb_gear", "what each summoned skeleton is holding and carrying, and where {weapon} is reading from", (ConsoleEvent)obj5, false, false, false, true, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static void Gear(ConsoleEventArgs args) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_0098: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { Both(args, "No player yet."); return; } Vector3 position = ((Component)Player.m_localPlayer).transform.position; int num = 0; List<Character> allCharacters = Character.GetAllCharacters(); for (int i = 0; i < allCharacters.Count; i++) { if (!Summons.IsSummoned(allCharacters[i])) { continue; } Character obj = allCharacters[i]; Humanoid val = (Humanoid)(object)((obj is Humanoid) ? obj : null); if (val == null) { continue; } num++; Both(args, (Summons.NameOf(allCharacters[i]) ?? "(unnamed)") + " - " + Mathf.RoundToInt(Vector3.Distance(position, ((Component)allCharacters[i]).transform.position)) + "m - " + PersonalityOf(allCharacters[i])); Both(args, " right hand: " + Describe(val.m_rightItem)); Both(args, " left hand: " + Describe(val.m_leftItem)); Both(args, " unarmed: " + Describe(((Object)(object)val.m_unarmedWeapon == (Object)null) ? null : val.m_unarmedWeapon.m_itemData)); Both(args, " -> in hand: " + Describe(val.GetCurrentWeapon()) + " <- what {weapon} and {weaponskill} read today"); Inventory inventory = val.GetInventory(); List<ItemData> list = ((inventory != null) ? inventory.GetAllItems() : null); if (list == null || list.Count == 0) { Both(args, " carrying: nothing"); continue; } for (int j = 0; j < list.Count; j++) { Both(args, " carrying: " + Describe(list[j])); } } Both(args, num + " summoned skeleton(s) loaded."); } private static void Both(ConsoleEventArgs args, string line) { args.Context.AddString(line); ChattyBonesPlugin.Log.LogInfo((object)("[gear] " + line)); } private static string Describe(ItemData item) { if (item?.m_shared == null) { return "(empty)"; } string name = item.m_shared.m_name; string text = ((string.IsNullOrEmpty(name) || Localization.instance == null) ? name : Localization.instance.Localize(name)); return text + " (" + name + ") [" + ((object)Unsafe.As<ItemType, ItemType>(ref item.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString() + ", " + ((object)Unsafe.As<SkillType, SkillType>(ref item.m_shared.m_skillType)/*cast due to .constrained prefix*/).ToString() + (item.IsWeapon() ? ", weapon]" : ", not a weapon]"); } private static void Broadcast(ConsoleEventArgs args) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { args.Context.AddString("No player yet."); return; } if (!Summons.TryFindNearest(((Component)Player.m_localPlayer).transform.position, 50f, out var found)) { args.Context.AddString("No summoned skeleton within " + 50f + "m."); return; } string text = Summons.NameOf(found) ?? "It"; ChatterComponent component = ((Component)found).GetComponent<ChatterComponent>(); if ((Object)(object)component == (Object)null) { args.Context.AddString(text + " has no chatter component."); return; } if (!component.TryReadBroadcast(out var said, out var details, out var companion, out var ally)) { args.Context.AddString(text + " has not said anything yet - nothing to mirror."); return; } args.Context.AddString(text + " is broadcasting " + said.Kind.ToString() + " #" + said.Counter + ", line ref " + said.LineRef + ", about " + ((said.Subject == 0) ? "nothing" : (Mirror.CreatureName(said.Subject) ?? "an unknown prefab")) + ", to " + Who(companion, ally)); args.Context.AddString(" details: " + Describe(details)); Chatter.Hear(component, said, details, companion, ally); } private static string Who(ZDOID companion, ZDOID ally) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_0019: 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) List<string> list = new List<string>(); if (companion != ZDOID.None) { list.Add("companion " + (Mirror.NameFor(companion) ?? "not loaded here")); } if (ally != ZDOID.None) { list.Add("ally " + (Mirror.NameFor(ally) ?? "not loaded here")); } if (list.Count != 0) { return string.Join(", ", list); } return "nobody"; } private static string Describe(LineDetails details) { List<string> list = new List<string>(); if (details.Weapon != null) { list.Add("weapon=" + details.Weapon); } if (details.WeaponSkill != null) { list.Add("weaponskill=" + details.WeaponSkill); } if (details.Damage != null) { list.Add("damage=" + details.Damage); } if (details.Status != null) { list.Add("status=" + details.Status); } if (details.Biome != null) { list.Add("biome=" + details.Biome); } if (details.Item != null) { list.Add("item=" + details.Item); } if (details.Skill != null) { list.Add("skill=" + details.Skill); } if (list.Count != 0) { return string.Join(", ", list); } return "none"; } private static void Tokens(ConsoleEventArgs args) { IReadOnlyList<string> readOnlyList = EventTokens.Report(); for (int i = 0; i < readOnlyList.Count; i++) { args.Context.AddString(readOnlyList[i]); } } private static void Say(ConsoleEventArgs args) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { args.Context.AddString("No player yet."); return; } string text = ((args.ArgsAll == null) ? string.Empty : args.ArgsAll.Trim()); string text2 = ((text.Length > 0) ? text : "My bones are itchy."); if (!Summons.TryFindNearest(((Component)Player.m_localPlayer).transform.position, 50f, out var found)) { args.Context.AddString("No summoned skeleton within " + 50f + "m."); return; } string text3 = Summons.NameOf(found) ?? "It"; Drew drew = Speech.Say(found, text2); args.Context.AddString((drew == Drew.Nothing) ? (text3 + " said nothing - check Enabled, and whether the world has finished loading.") : (text3 + " says (" + drew.ToString() + "): " + text2)); } private static void Who(ConsoleEventArgs args) { //IL_007b: 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_00e9: 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) if ((Object)(object)Player.m_localPlayer == (Object)null) { args.Context.AddString("No player yet."); return; } ChatterEvent kind = ChatterEvent.Idle; string text = ((args.ArgsAll == null) ? string.Empty : args.ArgsAll.Trim()); if (text.Length > 0) { if (EventKey.TryParse(text, out var key, out var problem)) { kind = key.Kind; } else { args.Context.AddString(problem + ". Showing Idle."); } } Vector3 position = ((Component)Player.m_localPlayer).transform.position; int num = 0; List<Character> allCharacters = Character.GetAllCharacters(); for (int i = 0; i < allCharacters.Count; i++) { if (Summons.IsSummoned(allCharacters[i])) { num++; IReadOnlyList<string> readOnlyList = Contexts.For(allCharacters[i]); args.Context.AddString((Summons.NameOf(allCharacters[i]) ?? "(unnamed)") + " - " + Mathf.RoundToInt(Vector3.Distance(position, ((Component)allCharacters[i]).transform.position)) + "m - " + PersonalityOf(allCharacters[i]) + " - " + ((readOnlyList == null || readOnlyList.Count == 0) ? "no context" : string.Join(", ", readOnlyList))); ChatterComponent component = ((Component)allCharacters[i]).GetComponent<ChatterComponent>(); string text2 = (((Object)(object)component == (Object)null) ? null : Chatter.DescribeChoice(component.Personality, kind, readOnlyList)); args.Context.AddString(" " + kind.ToString() + " -> " + (text2 ?? "nothing it could say")); } } args.Context.AddString(num + " summoned skeleton(s) loaded. Style: " + ModConfig.Bubble.Value.ToString() + ", enabled: " + ModConfig.Enabled.Value); } private static string PersonalityOf(Character character) { ChatterComponent component = ((Component)character).GetComponent<ChatterComponent>(); object obj; if (!((Object)(object)component == (Object)null)) { obj = component.Personality; if (obj == null) { return "unassigned"; } } else { obj = "no chatter component"; } return (string)obj; } } internal static class Hits { private static readonly HashSet<SkillType> WeaponSkills = new HashSet<SkillType> { (SkillType)1, (SkillType)2, (SkillType)3, (SkillType)4, (SkillType)5, (SkillType)7, (SkillType)8, (SkillType)14, (SkillType)9, (SkillType)10, (SkillType)11 }; internal static LineDetails Of(HitData hit, DamageTypes damage) { //IL_0039: 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) if (hit == null) { return default(LineDetails); } Character val = (hit.HaveAttacker() ? hit.GetAttacker() : null); bool flag = Describable(val); return new LineDetails(flag ? WeaponName(val) : null, flag ? SkillName(hit.m_skill) : null, Dominant(damage)); } internal static LineDetails DamageOf(DamageTypes damage) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return new LineDetails(null, null, Dominant(damage)); } private static string Dominant(DamageTypes damage) { //IL_0000: 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_000c: 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_0018: 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_0024: 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) return DamageKind.Dominant(damage.m_blunt, damage.m_slash, damage.m_pierce, damage.m_fire, damage.m_frost, damage.m_lightning, damage.m_poison, damage.m_spirit); } internal static LineDetails WieldedBy(Character character) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)character == (Object)null)) { Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null); if (val != null && Describable(character)) { ItemData currentWeapon = val.GetCurrentWeapon(); if (currentWeapon?.m_shared != null) { return new LineDetails(NameOf(currentWeapon), SkillName(currentWeapon.m_shared.m_skillType)); } return default(LineDetails); } } return default(LineDetails); } private static bool Describable(Character wielder) { return wielder is Player; } private static string WeaponName(Character attacker) { if (!((Object)(object)attacker == (Object)null)) { Humanoid val = (Humanoid)(object)((attacker is Humanoid) ? attacker : null); if (val != null) { ItemData currentWeapon = val.GetCurrentWeapon(); if (currentWeapon?.m_shared != null) { return NameOf(currentWeapon); } return null; } } return null; } private static string NameOf(ItemData item) { string name = item.m_shared.m_name; if (!string.IsNullOrEmpty(name)) { return name; } return null; } private static string SkillName(SkillType skill) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!WeaponSkills.Contains(skill)) { return null; } return Doings.NameOf(skill); } } internal static class Mirror { internal static LineDetails Localize(LineDetails raw) { return new LineDetails(Text(raw.Weapon), Text(raw.WeaponSkill), Text(raw.Damage), Text(raw.Status), Text(raw.Biome), Text(raw.Item), Text(raw.Skill)); } internal static string CreatureName(int prefabHash) { if (prefabHash == 0 || (Object)(object)ZNetScene.instance == (Object)null) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabHash); if ((Object)(object)prefab == (Object)null) { return null; } return Summons.CreatureName(prefab.GetComponent<Character>()); } internal static string NameFor(ZDOID id) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (id == ZDOID.None || (Object)(object)ZNetScene.instance == (Object)null) { return null; } GameObject val = ZNetScene.instance.FindInstance(id); if ((Object)(object)val == (Object)null) { return null; } Player component = val.GetComponent<Player>(); if ((Object)(object)component != (Object)null) { return Blank(((Character)component).GetHoverName()); } Tameable component2 = val.GetComponent<Tameable>(); if (!((Object)(object)component2 == (Object)null)) { return Blank(component2.GetHoverName()); } return null; } internal static string PlayerName(Player player) { if (!((Object)(object)player == (Object)null)) { return Blank(((Character)player).GetHoverName()); } return null; } internal static string SummonerName(Character skeleton) { if ((Object)(object)skeleton == (Object)null) { return null; } ZNetView component = ((Component)skeleton).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return null; } string text = component.GetZDO().GetString(ZDOVars.s_follow, ""); if (string.IsNullOrEmpty(text)) { return null; } List<Player> allPlayers = Player.GetAllPlayers(); for (int i = 0; i < allPlayers.Count; i++) { if (allPlayers[i].GetPlayerName() == text) { return Blank(((Character)allPlayers[i]).GetHoverName()); } } return null; } private static string Text(string key) { if (string.IsNullOrEmpty(key)) { return null; } if (Localization.instance == null) { return key; } string text = Localization.instance.Localize(key); if (key[0] == '$' && string.Equals(text, "[" + key.Substring(1) + "]", StringComparison.Ordinal)) { if (ModConfig.LogChatter.Value) { ChattyBonesPlugin.Log.LogInfo((object)("[chatter] no translation for " + key + " - probably a mod the other player has and you do not. Passing the line over.")); } return null; } return Blank(text); } private static string Blank(string value) { if (!string.IsNullOrEmpty(value)) { return value; } return null; } } internal enum BubbleStyle { FloatingText, DialoguePanel } internal static class ModConfig { internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<BubbleStyle> Bubble; internal static ConfigEntry<float> DialoguePanelSeconds; internal static ConfigEntry<float> DialoguePanelCullDistance; internal static ConfigEntry<float> TextHeight; internal static ConfigEntry<string> TextColor; internal static ConfigEntry<ChatterAmount> ChatterFrequency; internal static ConfigEntry<ChatterAmount> IdleChatter; internal static ConfigEntry<string> SilencedEvents; internal static ConfigEntry<float> MinGapSeconds; internal static ConfigEntry<float> PreemptGapSeconds; internal static ConfigEntry<float> SpeakerCooldownSeconds; internal static ConfigEntry<float> SquadEchoWindowSeconds; internal static ConfigEntry<float> IdleSeconds; internal static ConfigEntry<float> SummonGreetingSeconds; internal static ConfigEntry<float> HurtFraction; internal static ConfigEntry<float> BigHitFraction; internal static ConfigEntry<bool> HearOthers; internal static ConfigEntry<float> AllyGreetingDistance; internal static ConfigEntry<float> AllyGreetingForgetSeconds; internal static ConfigEntry<bool> LogChatter; private static bool _pushing; internal static void Init(ConfigFile cfg) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Expected O, but got Unknown //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Expected O, but got Unknown //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Expected O, but got Unknown //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Expected O, but got Unknown //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Expected O, but got Unknown //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Expected O, but got Unknown //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Expected O, but got Unknown //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Expected O, but got Unknown //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Expected O, but got Unknown //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Expected O, but got Unknown //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03c7: Expected O, but got Unknown //IL_0400: Unknown result type (might be due to invalid IL or missing references) //IL_040a: Expected O, but got Unknown //IL_0431: Unknown result type (might be due to invalid IL or missing references) //IL_043b: Expected O, but got Unknown //IL_0474: Unknown result type (might be due to invalid IL or missing references) //IL_047e: Expected O, but got Unknown //IL_04b7: Unknown result type (might be due to invalid IL or missing references) //IL_04c1: Expected O, but got Unknown //IL_04e8: Unknown result type (might be due to invalid IL or missing references) //IL_04f2: Expected O, but got Unknown Enabled = cfg.Bind<bool>("General", "Enabled", true, new ConfigDescription("Master switch; if disabled, this mod does nothing.", (AcceptableValueBase)null, new object[1] { At(100) })); Bubble = cfg.Bind<BubbleStyle>("Appearance", "BubbleStyle", BubbleStyle.FloatingText, new ConfigDescription("How Skelett chat is rendered. FloatingText follows the Skelett's head with no background. DialoguePanel is the Hugin box, and is a little easier to read, but maybe more obtrusive.", (AcceptableValueBase)null, new object[1] { At(100) })); DialoguePanelSeconds = cfg.Bind<float>("Appearance", "DialoguePanelSeconds", 5f, new ConfigDescription("[DialoguePanel only] How long a DialoguePanel line stays up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), new object[1] { Adv(70) })); DialoguePanelCullDistance = cfg.Bind<float>("Appearance", "DialoguePanelCullDistance", 20f, new ConfigDescription("[DialoguePanel only] How far away you can still see a DialoguePanel line, in meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), new object[1] { Adv(60) })); TextHeight = cfg.Bind<float>("Appearance", "TextHeight", 0.3f, new ConfigDescription("Height above the skeleton's head, in meters. Recommend a minimum of 0.3 to avoid clashing with the name.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), new object[1] { Adv(80) })); TextColor = cfg.Bind<string>("Appearance", "TextColor", "", new ConfigDescription("One color for everything, as a hex code like #C8FFC8. Leave it empty and the line pack colors by event instead.", (AcceptableValueBase)null, new object[1] { Adv(90) })); ChatterFrequency = cfg.Bind<ChatterAmount>("Chatter", "ChatterFrequency", ChatterAmount.Often, new ConfigDescription("How often the Skeletts react to events, players, and the world around them. These are presets around several advanced settings you can feel free to tweak. Note that Never is not silence - idle chatter has its own dial.", (AcceptableValueBase)null, new object[1] { At(100) })); IdleChatter = cfg.Bind<ChatterAmount>("Chatter", "IdleChatter", ChatterAmount.Sometimes, new ConfigDescription("How often the Skeletts mutter idly when nothing is going on. These are presets around several advanced settings you can feel free to tweak.", (AcceptableValueBase)null, new object[1] { At(90) })); SilencedEvents = cfg.Bind<string>("Chatter", "SilencedEvents", "", new ConfigDescription("Events to switch off, separated by commas - for example \"Weather, PlayerAte\". The names are listed at the top of ChattyBones.lines.default.yaml.", (AcceptableValueBase)null, new object[1] { Adv(80) })); MinGapSeconds = cfg.Bind<float>("Chatter", "MinGapSeconds", 1.5f, new ConfigDescription("How long the whole group stays quiet after one of them speaks. Set by ChatterFrequency; editing it switches that to Custom.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), new object[1] { Adv(95) })); PreemptGapSeconds = cfg.Bind<float>("Chatter", "PreemptGapSeconds", 0.5f, new ConfigDescription("How long a higher-priority event waits before cutting in on something less important.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { Adv(70) })); SpeakerCooldownSeconds = cfg.Bind<float>("Chatter", "SpeakerCooldownSeconds", 5f, new ConfigDescription("How long one Skelett waits before speaking again. Set by ChatterFrequency; editing it switches that to Custom.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f), new object[1] { Adv(94) })); SquadEchoWindowSeconds = cfg.Bind<float>("Chatter", "SquadEchoWindowSeconds", 4f, new ConfigDescription("How long one remark about a 'thing' stops the others repeating it. Set by ChatterFrequency; editing it switches that to Custom.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), new object[1] { Adv(93) })); IdleSeconds = cfg.Bind<float>("Chatter", "IdleSeconds", 45f, new ConfigDescription("Roughly how often a bored Skelett says something anyway. Set by IdleChatter; editing it switches that to Custom.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 600f), new object[1] { Adv(89) })); SummonGreetingSeconds = cfg.Bind<float>("Chatter", "SummonGreetingSeconds", 5f, new ConfigDescription("How new a Skelett has to be to greet you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), new object[1] { Adv(60) })); HurtFraction = cfg.Bind<float>("Chatter", "HurtFraction", 0.15f, new ConfigDescription("How much damage a Skelett takes in a hit before it complains, as a % of its own total health. e.g. a Skelett has 400 health, if this is 0.15 it will complain at any damage over 60.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), new object[1] { Adv(50) })); BigHitFraction = cfg.Bind<float>("Chatter", "BigHitFraction", 0.15f, new ConfigDescription("How hard you have to hit something before the Skeletts are impressed, as a share of THAT creature's health.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), new object[1] { Adv(40) })); HearOthers = cfg.Bind<bool>("Multiplayer", "HearOthers", true, new ConfigDescription("Whether other players' Skeletts talk on your screen. You can suppress a little with ChatterFrequency or IdleChatter set to Never, or using SilencedEvents. Otherwise, you hear whatever the other player emits, even if their settings are different from yours.", (AcceptableValueBase)null, new object[1] { At(100) })); AllyGreetingDistance = cfg.Bind<float>("Multiplayer", "AllyGreetingDistance", 15f, new ConfigDescription("How close another player has to come before your Skeletts say hi, in meters. Measured from the Skelett rather than from you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 60f), new object[1] { Adv(90) })); AllyGreetingForgetSeconds = cfg.Bind<float>("Multiplayer", "AllyGreetingForgetSeconds", 60f, new ConfigDescription("How long in seconds somebody has to stay out of range before being greeted again.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 600f), new object[1] { Adv(80) })); LogChatter = cfg.Bind<bool>("Debug", "LogChatter", false, new ConfigDescription("Useful for debugging, this logs every moment a Skelett is considering speech, whether they decide to speak, and then renders every line they actually say. VERY noisy log, use with caution.", (AcceptableValueBase)null, new object[1] { At(100) })); } internal static void Wire() { ChatterFrequency.SettingChanged += delegate { PushReactions(); }; IdleChatter.SettingChanged += delegate { PushIdle(); }; MinGapSeconds.SettingChanged += delegate { NoticeReactionsEdited(); }; SpeakerCooldownSeconds.SettingChanged += delegate { NoticeReactionsEdited(); }; SquadEchoWindowSeconds.SettingChanged += delegate { NoticeReactionsEdited(); }; IdleSeconds.SettingChanged += delegate { NoticeIdleEdited(); }; PushReactions(); PushIdle(); } private static void PushReactions() { if (_pushing || !ChatterPresets.TryGaps(ChatterFrequency.Value, out var gaps)) { return; } _pushing = true; try { MinGapSeconds.Value = gaps.MinGapSeconds; SpeakerCooldownSeconds.Value = gaps.SpeakerCooldownSeconds; SquadEchoWindowSeconds.Value = gaps.SquadEchoWindowSeconds; } finally { _pushing = false; } } private static void PushIdle() { if (_pushing || !ChatterPresets.TryIdleSeconds(IdleChatter.Value, out var seconds)) { return; } _pushing = true; try { IdleSeconds.Value = seconds; } finally { _pushing = false; } } private static void NoticeReactionsEdited() { if (!_pushing && ChatterPresets.TryGaps(ChatterFrequency.Value, out var gaps) && (MinGapSeconds.Value != gaps.MinGapSeconds || SpeakerCooldownSeconds.Value != gaps.SpeakerCooldownSeconds || SquadEchoWindowSeconds.Value != gaps.SquadEchoWindowSeconds)) { ChatterFrequency.Value = ChatterAmount.Custom; } } private static void NoticeIdleEdited() { if (!_pushing && ChatterPresets.TryIdleSeconds(IdleChatter.Value, out var seconds) && IdleSeconds.Value != seconds) { IdleChatter.Value = ChatterAmount.Custom; } } private static ConfigurationManagerAttributes At(int order) { return new ConfigurationManagerAttributes { Order = order }; } private static ConfigurationManagerAttributes Adv(int order) { return new ConfigurationManagerAttributes { IsAdvanced = true, Order = order }; } } internal static class PackFile { internal const string FileName = "ChattyBones.lines.yaml"; internal const string ReferenceFileName = "ChattyBones.lines.default.yaml"; private const float SettleSeconds = 0.5f; private const int MaxReported = 10; private static FileSystemWatcher _watcher; private static volatile bool _changed; private static float _settle; internal static string Location => Path.Combine(Paths.ConfigPath, "ChattyBones.lines.yaml"); private static string ReferenceLocation => Path.Combine(Paths.ConfigPath, "ChattyBones.lines.default.yaml"); internal static LinePack Load() { WriteFilesIfNeeded(); LinePack linePack = Read(); if (linePack == null && File.Exists(Location)) { ChattyBonesPlugin.Log.LogWarning((object)"ChattyBones.lines.yaml could not be used, so the lines built into the mod are being used instead."); } StartWatching(); if (linePack == null) { linePack = DefaultPack.Build(); } return linePack; } internal static bool ShouldReload(float dt) { if (_changed) { _changed = false; _settle = 0.5f; } if (_settle <= 0f) { return false; } _settle -= dt; return _settle <= 0f; } internal static LinePack Reload() { LinePack linePack = Read(); if (linePack == null) { ChattyBonesPlugin.Log.LogWarning((object)"ChattyBones.lines.yaml could not be used, so the skeletons are carrying on with the pack they had."); } return linePack; } internal static void StopWatching() { _watcher?.Dispose(); _watcher = null; } private static void WriteFilesIfNeeded() { try { if (!File.Exists(Location)) { File.WriteAllText(Location, DefaultPack.Yaml); ChattyBonesPlugin.Log.LogInfo((object)("Wrote a starting line pack to " + Location + ".")); } File.WriteAllText(ReferenceLocation, DefaultPack.Yaml); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("Could not write to " + Paths.ConfigPath + ": " + ex.Message)); } } private static LinePack Read() { if (!File.Exists(Location)) { return null; } string yaml; try { yaml = File.ReadAllText(Location); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("Could not read ChattyBones.lines.yaml: " + ex.Message + " Save the file again to have another go.")); return null; } PackReader.TryRead(yaml, out var pack, out var problems); Report(problems); return pack; } private static void Report(IReadOnlyList<string> problems) { int num = Math.Min(problems.Count, 10); for (int i = 0; i < num; i++) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones.lines.yaml, " + problems[i])); } if (problems.Count > num) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones.lines.yaml has " + (problems.Count - num) + " more problems, not listed.")); } } private static void StartWatching() { StopWatching(); try { _watcher = new FileSystemWatcher(Paths.ConfigPath, "ChattyBones.lines.yaml") { NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite) }; _watcher.Changed += delegate { _changed = true; }; _watcher.Created += delegate { _changed = true; }; _watcher.Renamed += delegate { _changed = true; }; _watcher.EnableRaisingEvents = true; } catch (Exception ex) { _watcher = null; ChattyBonesPlugin.Log.LogWarning((object)("Could not watch ChattyBones.lines.yaml for changes, so edits to it need a restart: " + ex.Message)); } } } [BepInPlugin("pandincus.chattybones", "ChattyBones", "0.1.1")] [BepInProcess("valheim.exe")] public class ChattyBonesPlugin : BaseUnityPlugin { public const string PluginGuid = "pandincus.chattybones"; public const string PluginName = "ChattyBones"; public const string PluginVersion = "0.1.1"; internal static ManualLogSource Log; private Harmony _harmony; private bool _started; private void Awake() { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModConfig.Init(((BaseUnityPlugin)this).Config); ModConfig.Wire(); Speech.Resolve(); try { Chatter.Init(); } catch (Exception ex) { Log.LogError((object)("ChattyBones could not start, so your skeletons will stay quiet this session. The usual cause is a missing YamlDotNet - this mod needs the ValheimModding-YamlDotNet package, which a mod manager installs for you. " + ex)); return; } DebugCommands.Register(); ((BaseUnityPlugin)this).Config.SettingChanged += delegate { Chatter.RefreshSettings(); }; _harmony = new Harmony("pandincus.chattybones"); Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(Assembly.GetExecutingAssembly()); foreach (Type type in typesFromAssembly) { try { _harmony.CreateClassProcessor(type).Patch(); } catch (Exception ex2) { Log.LogError((object)("ChattyBones could not apply " + type.Name + ", so the reactions it carries will not fire. The usual cause is a Valheim update having moved what it hooks. " + ex2)); } } _started = true; Log.LogInfo((object)"ChattyBones v0.1.1 loaded."); } private void Update() { if (_started) { Chatter.Tick(Time.deltaTime); } } private void OnDestroy() { PackFile.StopWatching(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal enum Drew { Nothing, FloatingText, DialoguePanel } internal static class Speech { private const string AnchorName = "ChattyBonesSpeechAnchor"; private static readonly ColorTagCache Colors = new ColorTagCache(); private static MethodInfo _addInworldText; private static bool _floatingTextDisabled; internal static void Resolve() { _addInworldText = AccessTools.Method(typeof(Chat), "AddInworldText", new Type[6] { typeof(GameObject), typeof(long), typeof(Vector3), typeof(Type), typeof(UserInfo), typeof(string) }, (Type[])null); if (_addInworldText == null) { ChattyBonesPlugin.Log.LogWarning((object)"Chat.AddInworldText not found - a game update has probably moved it. Falling back to the dialogue panel, which works but looks like Haldor."); } } internal static Drew Say(Character speaker, string line, string packTag = null) { if (!ModConfig.Enabled.Value || (Object)(object)speaker == (Object)null || string.IsNullOrEmpty(line)) { return Drew.Nothing; } Chat instance = Chat.instance; if ((Object)(object)instance == (Object)null) { return Drew.Nothing; } try { string line2 = Colorize(line, packTag); return (ModConfig.Bubble.Value == BubbleStyle.FloatingText && TryFloatingText(instance, speaker, line2)) ? Drew.FloatingText : ShowPanel(instance, speaker, line2); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("Could not draw a line for a skeleton: " + ex)); return Drew.Nothing; } } private static string Colorize(string line, string packTag) { string value = ModConfig.TextColor.Value; if (Colors.TryTagFor(value, out var tag, out var newlyRejected)) { return SpeechFormat.Wrap(line, tag); } if (newlyRejected) { ChattyBonesPlugin.Log.LogWarning((object)("TextColor '" + value + "' is not a hex code like #C8FFC8, so it is being ignored.")); } return SpeechFormat.Wrap(line, packTag); } private static bool TryFloatingText(Chat chat, Character speaker, string line) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown if (_addInworldText == null || _floatingTextDisabled) { return false; } object[] parameters = new object[6] { AnchorFor(speaker), SenderIdFor(speaker), speaker.GetHeadPoint(), (object)(Type)1, (object)new UserInfo(), line }; try { _addInworldText.Invoke(chat, parameters); return true; } catch (Exception ex) { _floatingTextDisabled = true; ChattyBonesPlugin.Log.LogWarning((object)("Chat.AddInworldText threw, so the dialogue panel takes over from here: " + (ex.InnerException ?? ex))); return false; } } private static Drew ShowPanel(Chat chat, Character speaker, string line) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) chat.SetNpcText(((Component)speaker).gameObject, Vector3.up * 1.5f, ModConfig.DialoguePanelCullDistance.Value, ModConfig.DialoguePanelSeconds.Value, Summons.NameOf(speaker) ?? string.Empty, line, false); return Drew.DialoguePanel; } private static GameObject AnchorFor(Character speaker) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) float value = ModConfig.TextHeight.Value; if (value <= 0f) { return ((Component)speaker).gameObject; } Transform val = ((Component)speaker).transform.Find("ChattyBonesSpeechAnchor"); GameObject val2; if ((Object)(object)val == (Object)null) { val2 = new GameObject("ChattyBonesSpeechAnchor"); val2.transform.SetParent(((Component)speaker).transform, false); } else { val2 = ((Component)val).gameObject; } float num = speaker.GetHeadPoint().y - ((Component)speaker).transform.position.y; val2.transform.localPosition = new Vector3(0f, num + value, 0f); return val2; } private static long SenderIdFor(Character speaker) { //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) ZDOID zDOID = speaker.GetZDOID(); return SpeechFormat.SenderId(((ZDOID)(ref zDOID)).UserID, ((ZDOID)(ref zDOID)).ID); } } internal static class Summons { internal static bool IsSummoned(Character character) { if ((Object)(object)character == (Object)null) { return false; } ZNetView component = ((Component)character).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return false; } Tameable component2 = ((Component)character).GetComponent<Tameable>(); if ((Object)(object)component2 != (Object)null) { if (!(component2.m_unsummonDistance > 0f)) { return component2.m_unsummonOnOwnerLogoutSeconds > 0f; } return true; } return false; } internal static bool TryFindNearest(Vector3 point, float maxDistance, out Character found) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) found = null; float num = maxDistance * maxDistance; List<Character> allCharacters = Character.GetAllCharacters(); for (int i = 0; i < allCharacters.Count; i++) { Character val = allCharacters[i]; if (IsSummoned(val)) { Vector3 val2 = ((Component)val).transform.position - point; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; found = val; } } } return (Object)(object)found != (Object)null; } internal static string NameOf(Character character) { if ((Object)(object)character == (Object)null) { return null; } Tameable component = ((Component)character).GetComponent<Tameable>(); if (!((Object)(object)component == (Object)null)) { return component.GetHoverName(); } return null; } internal static int PrefabOf(Character character) { if ((Object)(object)character == (Object)null) { return 0; } ZNetView component = ((Component)character).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid()) { return component.GetZDO().GetPrefab(); } return 0; } internal static string CreatureName(Character character) { if ((Object)(object)character == (Object)null || string.IsNullOrEmpty(character.m_name)) { return null; } if (Localization.instance != null) { return Localization.instance.Localize(character.m_name); } return character.m_name; } } } namespace ChattyBones.Patches { [HarmonyPatch(typeof(Character), "RPC_Damage")] internal static class CharacterDamagedPatch { internal readonly struct Reading { internal float Health { get; } internal DamageTypes Damage { get; } public Reading(float health, DamageTypes damage) { //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) Health = health; Damage = damage; } } private static void Prefix(Character __instance, HitData hit, out Reading __state) { //IL_0024: 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_001b: Unknown result type (might be due to invalid IL or missing references) __state = new Reading(((Object)(object)__instance == (Object)null) ? 0f : __instance.GetHealth(), (DamageTypes)(((??)hit?.m_damage) ?? default(DamageTypes))); } private static void Postfix(Character __instance, HitData hit, Reading __state) { try { React(__instance, hit, __state); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a hit: " + ex)); } try { Blow.Flush(__instance); } catch (Exception ex2) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a blow: " + ex2)); } } private static void React(Character victim, HitData hit, Reading seen) { //IL_00be: 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_0080: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.Enabled.Value || (Object)(object)victim == (Object)null) { return; } float maxHealth = victim.GetMaxHealth(); float num = seen.Health - victim.GetHealth(); if (maxHealth <= 0f || num <= 0f) { return; } bool fatal = victim.GetHealth() <= 0f; Blame.Note(victim, hit); if (victim.IsPlayer()) { if ((Object)(object)victim == (Object)(object)Player.m_localPlayer && num / maxHealth >= ModConfig.HurtFraction.Value) { Chatter.SpeakAny(ChatterEvent.PlayerHurt, 0, null, null, null, Hits.DamageOf(seen.Damage)); } return; } ChatterComponent component = ((Component)victim).GetComponent<ChatterComponent>(); if ((Object)(object)component != (Object)null) { TheySufferedIt(victim, component, Hits.DamageOf(seen.Damage), num / maxHealth, fatal); } else { WeDealtIt(victim, hit, seen.Damage, num / maxHealth, fatal); } } private static void TheySufferedIt(Character victim, ChatterComponent ours, LineDetails details, float share, bool fatal) { if (!fatal && !(share < ModConfig.HurtFraction.Value) && !Chatter.TrySpeak(ours, ChatterEvent.Hurt, 0, null, null, null, details)) { Chatter.SpeakAny(ChatterEvent.CompanionHurt, Summons.PrefabOf(victim), null, victim, null, details); } } private static void WeDealtIt(Character victim, HitData hit, DamageTypes damage, float share, bool fatal) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (!fatal && hit != null && !(share < ModConfig.BigHitFraction.Value) && !((Object)(object)Player.m_localPlayer == (Object)null) && hit.HaveAttacker() && !((Object)(object)hit.GetAttacker() != (Object)(object)Player.m_localPlayer)) { Blow.Note(victim, ChatterEvent.PlayerLandedABigHit, Summons.PrefabOf(victim), Summons.CreatureName(victim), Hits.Of(hit, damage)); } } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class CharacterDeathPatch { private static void Prefix(Character __instance) { try { React(__instance); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a death: " + ex)); } } private static void React(Character dead) { if (!ModConfig.Enabled.Value || (Object)(object)dead == (Object)null) { return; } Character val = Blame.Take(dead); ChatterComponent component = ((Component)dead).GetComponent<ChatterComponent>(); if ((Object)(object)component != (Object)null) { Mourn(dead, component); return; } HitData lastHit = dead.m_lastHit; Character val2 = ((lastHit != null) ? lastHit.GetAttacker() : null); if ((Object)(object)val2 == (Object)null) { val2 = val; } if ((Object)(object)val2 == (Object)null) { return; } if ((Object)(object)Player.m_localPlayer != (Object)null && (object)val2 == Player.m_localPlayer) { Chatter.SpeakAny(ChatterEvent.PlayerGotAKill, Summons.PrefabOf(dead), Summons.CreatureName(dead), null, null, Hits.WieldedBy((Character)(object)Player.m_localPlayer)); return; } ChatterComponent component2 = ((Component)val2).GetComponent<ChatterComponent>(); if ((Object)(object)component2 != (Object)null && component2.IsOwned) { Gloat(dead, component2); } } private static void Gloat(Character dead, ChatterComponent killer) { int subject = Summons.PrefabOf(dead); string targetName = Summons.CreatureName(dead); if (!Chatter.TrySpeak(killer, ChatterEvent.Killed, subject, targetName, null)) { Chatter.SpeakAny(ChatterEvent.CompanionKilled, subject, targetName, killer.Character); } } private static void Mourn(Character fallen, ChatterComponent speaker) { if (Chatter.TrySpeak(speaker, ChatterEvent.Died, 0, null, null)) { Chatter.SpeakAny(ChatterEvent.CompanionDied, 0, null, fallen, Summons.NameOf(fallen)); } } } internal static class Blow { private static Character _victim; private static ChatterEvent _kind; private static int _subject; private static string _targetName; private static LineDetails _details; private static int _frame; private static Character _selfStaggered; private static int _selfStaggeredFrame; internal static void NoteSelfStagger(Character victim) { _selfStaggered = victim; _selfStaggeredFrame = Time.frameCount; } internal static bool WasSelfStaggered(Character victim) { if ((Object)(object)victim != (Object)null && victim == _selfStaggered) { return _selfStaggeredFrame == Time.frameCount; } return false; } internal static void Note(Character victim, ChatterEvent kind, int subject, string targetName, LineDetails details = default(LineDetails)) { if (!((Object)(object)victim == (Object)null) && (victim != _victim || _frame != Time.frameCount || ChatterBudget.PriorityOf(kind) > ChatterBudget.PriorityOf(_kind))) { _victim = victim; _kind = kind; _subject = subject; _targetName = targetName; _details = details; _frame = Time.frameCount; } } internal static void FlushStale() { if (!((Object)(object)_victim == (Object)null) && _frame != Time.frameCount) { Flush(_victim); } } internal static void Flush(Character victim) { if (!((Object)(object)victim == (Object)null) && victim == _victim) { ChatterEvent kind = _kind; int subject = _subject; string targetName = _targetName; LineDetails details = _details; bool num = Time.frameCount - _frame <= 1; _victim = null; _targetName = null; _details = default(LineDetails); _selfStaggered = null; if (num && victim.GetHealth() > 0f) { Chatter.SpeakAny(kind, subject, targetName, null, null, details); } } } } [HarmonyPatch(typeof(Humanoid), "BlockAttack")] internal static class HumanoidBlockPatch { private static void Postfix(Humanoid __instance, HitData hit, bool __result, ItemData ___m_leftItem, float ___m_blockTimer) { try { React(__instance, hit, __result, ___m_leftItem, ___m_blockTimer); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a block: " + ex)); } } private static void React(Humanoid blocker, HitData hit, bool blocked, ItemData leftItem, float blockTimer) { if (ModConfig.Enabled.Value && blocked && !((Object)(object)blocker == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)blocker != (Object)(object)Player.m_localPlayer) && hit != null && hit.HaveAttacker() && ((Character)blocker).HaveStamina(0f) && !Blow.WasSelfStaggered((Character)(object)blocker) && WasTimed(blocker, leftItem, blockTimer)) { Character attacker = hit.GetAttacker(); Blow.Note((Character)(object)blocker, ChatterEvent.PlayerParried, Summons.PrefabOf(attacker), Summons.CreatureName(attacker)); } } private static bool WasTimed(Humanoid blocker, ItemData leftItem, float blockTimer) { ItemData val = leftItem ?? blocker.GetCurrentWeapon(); if (val?.m_shared != null && val.m_shared.m_timedBlockBonus > 1f && blockTimer != -1f) { return blockTimer < 0.25f; } return false; } } [HarmonyPatch(typeof(Character), "AddStaggerDamage")] internal static class CharacterStaggerPatch { private static void Prefix(float ___m_staggerDamage, out float __state) { __state = ___m_staggerDamage; } private static void Postfix(Character __instance, HitData hit, bool __result, float __state) { try { bool staggered = __result && (Object)(object)__instance != (Object)null && __state < __instance.GetMaxHealth() * __instance.m_staggerDamageFactor; React(__instance, hit, staggered); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a stagger: " + ex)); } } private static void React(Character victim, HitData hit, bool staggered) { if (ModConfig.Enabled.Value && staggered && !((Object)(object)victim == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null)) { Character val = ((hit != null && hit.HaveAttacker()) ? hit.GetAttacker() : null); if (hit == null) { Blow.NoteSelfStagger(victim); } if ((Object)(object)victim == (Object)(object)Player.m_localPlayer) { Blow.Note(victim, ChatterEvent.PlayerStaggered, Summons.PrefabOf(val), Summons.CreatureName(val)); } else if ((Object)(object)val == (Object)(object)Player.m_localPlayer) { Blow.Note(victim, ChatterEvent.StaggeredIt, Summons.PrefabOf(victim), Summons.CreatureName(victim)); } } } } [HarmonyPatch(typeof(Player), "RPC_HitWhileDodging")] internal static class PlayerDodgePatch { private static void Prefix(bool ___m_beenHitWhileDodging, out bool __state) { __state = ___m_beenHitWhileDodging; } private static void Postfix(Player __instance, bool __state) { try { React(__instance, __state); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a dodge: " + ex)); } } private static void React(Player dodger, bool alreadyCounted) { if (!(!ModConfig.Enabled.Value || alreadyCounted) && !((Object)(object)dodger == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)dodger != (Object)(object)Player.m_localPlayer)) { Chatter.SpeakAny(ChatterEvent.PlayerDodged, 0, null, null); } } } internal static class Cooking { private const float NearbyMetres = 20f; internal static bool Nearby(Component what) { //IL_001c: 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) if ((Object)(object)what != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null) { return Vector3.Distance(what.transform.position, ((Component)Player.m_localPlayer).transform.position) < 20f; } return false; } internal static void React(ItemData item) { if (ModConfig.Enabled.Value && ChatterComponent.All.Count != 0) { Chatter.SpeakAny(ChatterEvent.PlayerCooked, 0, null, null, null, new LineDetails(null, null, null, null, null, Doings.NameOf(item))); } } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class CauldronCraftPatch { private static void Postfix(Recipe ___m_craftRecipe) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 try { CraftingStation val = ___m_craftRecipe?.m_craftingStation; if (!((Object)(object)val == (Object)null) && (int)val.m_craftingSkill == 105) { Cooking.React(___m_craftRecipe.m_item?.m_itemData); } } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over some cooking: " + ex)); } } } [HarmonyPatch(typeof(Fermenter), "DelayedTap")] internal static class FermenterTapPatch { private static void Postfix(Fermenter __instance, int ___m_delayedTapItem) { try { if (Cooking.Nearby((Component)(object)__instance)) { Cooking.React(__instance.GetItemConversion(___m_delayedTapItem)?.m_to?.m_itemData); } } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a fermenter: " + ex)); } } } internal static class Doings { internal static string NameOf(ItemData item) { if (item?.m_shared == null) { return null; } string name = item.m_shared.m_name; if (!string.IsNullOrEmpty(name)) { return name; } return null; } internal unsafe static string NameOf(SkillType skill) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if ((int)skill == 0) { return null; } return "$skill_" + ((object)(*(SkillType*)(&skill))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(); } } [HarmonyPatch(typeof(Character), "ShowPickupMessage")] internal static class CharacterPickupPatch { private static void Postfix(Character __instance, ItemData item) { try { React(__instance, item); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a pickup: " + ex)); } } private static void React(Character picker, ItemData item) { if (ModConfig.Enabled.Value && item?.m_shared != null && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)picker != (Object)(object)Player.m_localPlayer) && !StoreGui.IsVisible() && ChatterComponent.All.Count != 0) { Chatter.SpeakAny(ChatterEvent.Looted, 0, null, null, null, new LineDetails(null, null, null, null, null, Doings.NameOf(item))); } } } [HarmonyPatch(typeof(Player), "EatFood")] internal static class PlayerEatPatch { private static void Postfix(Player __instance, ItemData item, bool __result) { try { if (ModConfig.Enabled.Value && __result && !((Object)(object)__instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { Chatter.SpeakAny(ChatterEvent.PlayerAte, 0, null, null, null, new LineDetails(null, null, null, null, null, Doings.NameOf(item))); } } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a meal: " + ex)); } } } [HarmonyPatch(typeof(Player), "OnSkillLevelup")] internal static class PlayerSkillPatch { private static void Postfix(Player __instance, SkillType skill) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) try { if (ModConfig.Enabled.Value && !((Object)(object)__instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { Blow.Note((Character)(object)__instance, ChatterEvent.PlayerSkilledUp, 0, null, new LineDetails(null, null, null, null, null, null, Doings.NameOf(skill))); } } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a skill: " + ex)); } } } [HarmonyPatch] internal static class SEManAddStatusEffectPatch { private static MethodBase TargetMethod() { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(SEMan))) { if (!(declaredMethod.Name != "AddStatusEffect")) { ParameterInfo[] parameters = declaredMethod.GetParameters(); if (parameters.Length != 0 && parameters[0].ParameterType == typeof(StatusEffect)) { return declaredMethod; } } } return null; } private static void Postfix(SEMan __instance, StatusEffect statusEffect, StatusEffect __result) { try { React(__instance, statusEffect, __result); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a status effect: " + ex)); } } private static string Named(StatusEffect effect) { string name = effect.m_name; if (!string.IsNullOrEmpty(name)) { return name; } return null; } private static void React(SEMan seman, StatusEffect statusEffect, StatusEffect added) { if (!ModConfig.Enabled.Value || (Object)(object)added == (Object)null || seman == null || (Object)(object)statusEffect == (Object)null) { return; } Character character = seman.m_character; if (!((Object)(object)character == (Object)null)) { ChatterComponent component = ((Component)character).GetComponent<ChatterComponent>(); if (!((Object)(object)component == (Object)null)) { ChatterEvent kind = StatusKind.EventFor(((Object)statusEffect).name); Chatter.TrySpeak(component, kind, statusEffect.NameHash(), null, null, null, new LineDetails(null, null, null, Named(statusEffect))); } } } } [HarmonyPatch(typeof(Tameable), "Awake")] internal static class TameableAwakePatch { private static void Postfix(Tameable __instance) { try { Character character = __instance.m_character; if (Summons.IsSummoned(character) && (Object)(object)((Component)character).GetComponent<ChatterComponent>() == (Object)null) { ((Component)character).gameObject.AddComponent<ChatterComponent>(); } } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones could not attach to a summon: " + ex)); } } } [HarmonyPatch(typeof(Tameable), "RPC_UnSummon")] internal static class TameableUnSummonPatch { private static void Prefix(Tameable __instance) { try { Character character = __instance.m_character; ChatterComponent chatterComponent = (((Object)(object)character == (Object)null) ? null : ((Component)character).GetComponent<ChatterComponent>()); if ((Object)(object)chatterComponent != (Object)null) { Chatter.TrySpeak(chatterComponent, ChatterEvent.Unsummoned, 0, null, null); } } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over an unsummon: " + ex)); } } } internal static class Biomes { internal unsafe static string NameOf(Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if ((int)biome == 0) { return null; } return "$biome_" + ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(); } } [HarmonyPatch(typeof(EnvMan), "OnMorning")] internal static class EnvManMorningPatch { private static void Postfix(Biome biome) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) try { WorldEvents.Announce(ChatterEvent.Dawn, biome); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over sunrise: " + ex)); } } } [HarmonyPatch(typeof(EnvMan), "OnEvening")] internal static class EnvManEveningPatch { private static void Postfix(Biome biome) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) try { WorldEvents.Announce(ChatterEvent.Nightfall, biome); } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over nightfall: " + ex)); } } } [HarmonyPatch(typeof(Player), "AddKnownBiome")] internal static class PlayerBiomePatch { private static void Postfix(Player __instance, BiomeSector biome) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) try { if (biome != null && (int)biome.Biome != 0 && (Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { WorldEvents.Announce(ChatterEvent.BiomeChanged, biome.Biome); } } catch (Exception ex) { ChattyBonesPlugin.Log.LogWarning((object)("ChattyBones stumbled over a biome: " + ex)); } } } internal static class Raids { private static bool _underway; private static bool _bossUp; internal static void Poll() { bool flag = (Object)(object)RandEventSystem.instance != (Object)null && RandEventSystem.instance.GetCurrentRandomEvent() != null; if (flag != _underway) { _underway = flag; WorldEvents.Announce(flag ? ChatterEvent.Raid : ChatterEvent.RaidEnded, (Biome)0); } bool flag2 = (Object)(object)EnemyHud.instance != (Object)null && (Object)(object)EnemyHud.instance.G