using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Gathering")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Gathering")]
[assembly: AssemblyTitle("Gathering")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Gathering;
[BepInPlugin("tchernobill.gathering", "Gathering", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
public const string GUID = "tchernobill.gathering";
public const string NAME = "Gathering";
public const string VERSION = "1.0.0";
internal static ManualLogSource Log;
internal static ConfigEntry<float> Distance;
internal static ConfigEntry<float> Angle;
internal static ConfigEntry<float> HoldTime;
internal static ConfigEntry<bool> NearestWins;
internal static ConfigEntry<bool> FaceSource;
internal static ConfigEntry<bool> Mining;
internal static ConfigEntry<bool> Fishing;
internal static ConfigEntry<bool> Chests;
private void Awake()
{
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Expected O, but got Unknown
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Expected O, but got Unknown
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00bd: Expected O, but got Unknown
//IL_0167: Unknown result type (might be due to invalid IL or missing references)
//IL_016c: Unknown result type (might be due to invalid IL or missing references)
//IL_0193: Unknown result type (might be due to invalid IL or missing references)
//IL_019f: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
Distance = ((BaseUnityPlugin)this).Config.Bind<float>("Gathering", "Distance", 3f, new ConfigDescription("Maximum horizontal distance (m) between the character and a source. Vanilla reaches about 2 m in front of the character and much less on the sides.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>()));
Angle = ((BaseUnityPlugin)this).Config.Bind<float>("Gathering", "Angle", 180f, new ConfigDescription("Maximum angle (degrees) between where the character faces and the source. 180 = any direction, back included. Vanilla is roughly 60 at 1 m.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 180f), Array.Empty<object>()));
HoldTime = ((BaseUnityPlugin)this).Config.Bind<float>("Gathering", "Hold Time", 0.2f, new ConfigDescription("How long (s) the interact button must be held to gather (or to open a chest that opens on hold). 0 = on press. Vanilla: 1.25 (3 for some chests).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
NearestWins = ((BaseUnityPlugin)this).Config.Bind<bool>("Gathering", "Nearest Wins", true, "When a source is among several interactables in reach, the nearest one becomes the active one (vanilla favours what the character faces).");
FaceSource = ((BaseUnityPlugin)this).Config.Bind<bool>("Gathering", "Face Source", true, "Turn the character toward the source when gathering (or opening) starts, so the animation reaches for it.");
Mining = ((BaseUnityPlugin)this).Config.Bind<bool>("Sources", "Mining", true, "Apply to mining spots (ore veins, which need a mining pick). Plants and dead wood always are.");
Fishing = ((BaseUnityPlugin)this).Config.Bind<bool>("Sources", "Fishing", true, "Apply to fishing spots (which need a fishing harpoon).");
Chests = ((BaseUnityPlugin)this).Config.Bind<bool>("Sources", "Chests", true, "Apply to chests and the other loot containers of the world (TreasureChest: chests, junk piles, stashes...).");
Harmony val = new Harmony("tchernobill.gathering");
val.PatchAll(typeof(GatherPatches));
val.Patch(GatherPatches.InteractCoroutine(), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(GatherPatches), "HoldTimes", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
TestHarness.StartIfRequested(((Component)this).gameObject);
}
}
internal enum SourceKind
{
None,
Plant,
Mining,
Fishing,
Chest
}
[HarmonyPatch]
internal static class GatherPatches
{
private const float VanillaHoldThreshold = 0.4f;
private const float TorsoHeight = 0.9f;
private const float VerticalReach = 2f;
private const int MiningPick = 2120050;
private const int FishingHarpoon = 2130130;
private static readonly Collider[] s_hits = (Collider[])(object)new Collider[256];
private static readonly Dictionary<InteractionTriggerBase, float> s_found = new Dictionary<InteractionTriggerBase, float>();
private static readonly FieldRef<Character, InteractionTriggerBase> s_lastInteractable = AccessTools.FieldRefAccess<Character, InteractionTriggerBase>("m_lastInteractable");
private static readonly FieldRef<InteractionTriggerBase, EventActivator> s_activator = AccessTools.FieldRefAccess<InteractionTriggerBase, EventActivator>("m_defaultTriggerManager");
private static readonly FieldRef<InteractionTriggerBase, List<Collider>> s_extraColliders = AccessTools.FieldRefAccess<InteractionTriggerBase, List<Collider>>("m_extraInteractionColliders");
private static readonly Func<Character, Collider, InteractionTriggerBase, bool> s_isBlocked = AccessTools.MethodDelegate<Func<Character, Collider, InteractionTriggerBase, bool>>(AccessTools.Method(typeof(Character), "GetIsBlockedByEnvironment", (Type[])null, (Type[])null), (object)null, true);
private static readonly Action<Character> s_vanillaPick = AccessTools.MethodDelegate<Action<Character>>(AccessTools.Method(typeof(Character), "FindInteractableItem", (Type[])null, (Type[])null), (object)null, true);
internal static bool Vanilla;
internal static SourceKind KindOf(InteractionTriggerBase trigger)
{
Item val = (((Object)(object)trigger != (Object)null) ? trigger.ParentItem : null);
Gatherable val2 = (Gatherable)(object)((val is Gatherable) ? val : null);
if (val2 != null)
{
return (((Object)(object)val2.RequiredItem != (Object)null) ? val2.RequiredItem.ItemID : 0) switch
{
2130130 => SourceKind.Fishing,
2120050 => SourceKind.Mining,
_ => SourceKind.Plant,
};
}
if (!(val is TreasureChest))
{
return SourceKind.None;
}
return SourceKind.Chest;
}
internal static bool Applies(InteractionTriggerBase trigger)
{
if (Vanilla)
{
return false;
}
return KindOf(trigger) switch
{
SourceKind.Plant => true,
SourceKind.Mining => Plugin.Mining.Value,
SourceKind.Fishing => Plugin.Fishing.Value,
SourceKind.Chest => Plugin.Chests.Value,
_ => false,
};
}
[HarmonyPostfix]
[HarmonyPatch(typeof(Character), "UpdateWorldItemDetection")]
private static void AfterDetection(Character __instance, List<InteractionTriggerBase> ___m_closeInteractionTriggers, ref int ___m_mostRelevantInteractableIndex, float ___m_lastItemDectionTime)
{
if (___m_lastItemDectionTime != Time.time || __instance.IsDead || !__instance.IsLocalPlayer)
{
return;
}
___m_closeInteractionTriggers.RemoveAll(Applies);
FindSources(__instance);
foreach (KeyValuePair<InteractionTriggerBase, float> item in s_found)
{
___m_closeInteractionTriggers.Add(item.Key);
}
___m_mostRelevantInteractableIndex = -1;
if (NearestWinsAmong(___m_closeInteractionTriggers))
{
___m_mostRelevantInteractableIndex = PickNearest(__instance, ___m_closeInteractionTriggers);
}
else
{
s_vanillaPick(__instance);
}
s_found.Clear();
}
internal static void FindSources(Character character)
{
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: 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_00f5: Unknown result type (might be due to invalid IL or missing references)
//IL_011c: Unknown result type (might be due to invalid IL or missing references)
//IL_011d: Unknown result type (might be due to invalid IL or missing references)
s_found.Clear();
float value = Plugin.Distance.Value;
float value2 = Plugin.Angle.Value;
Vector3 val = ((Component)character).transform.position + Vector3.up * 0.9f;
Vector3 val2 = Flat(((Component)character).transform.forward);
int num = Physics.OverlapSphereNonAlloc(val, value + 2f, s_hits, Global.WorldItemsMask);
for (int i = 0; i < num; i++)
{
Collider val3 = s_hits[i];
TriggerColliderFlag component = ((Component)val3).GetComponent<TriggerColliderFlag>();
InteractionTriggerBase val4 = (InteractionTriggerBase)(((Object)(object)component != (Object)null && component.Trigger != null) ? ((object)/*isinst with value type is only supported in some contexts*/) : ((object)((Component)val3).GetComponentInParent<InteractionTriggerBase>()));
if (!Applies(val4))
{
continue;
}
Bounds bounds = val3.bounds;
Vector3 val5 = ((Bounds)(ref bounds)).ClosestPoint(val);
Vector3 val6 = Flat(val5 - val);
float magnitude = ((Vector3)(ref val6)).magnitude;
if (magnitude > value || Mathf.Abs(val5.y - val.y) > 2f || (value2 < 180f && magnitude > 0.3f && Vector3.Angle(val2, val6) > value2))
{
continue;
}
if (s_found.TryGetValue(val4, out var value3))
{
if (magnitude < value3)
{
s_found[val4] = magnitude;
}
}
else if (!s_isBlocked(character, val3, val4) && (val4.IsBag || val4.GetBasicCanBeTriggered(character, val4.NotifyOnInvalidConditions) || val4.GetHoldCanBeTriggered(character, val4.NotifyOnInvalidConditions)))
{
s_found[val4] = magnitude;
}
}
}
private static bool NearestWinsAmong(List<InteractionTriggerBase> triggers)
{
if (!Plugin.NearestWins.Value || triggers.Count < 2)
{
return false;
}
foreach (InteractionTriggerBase trigger in triggers)
{
if (Applies(trigger))
{
return true;
}
}
return false;
}
private static int PickNearest(Character character, List<InteractionTriggerBase> triggers)
{
//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_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: 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)
Vector3 torso = ((Component)character).transform.position + Vector3.up * 0.9f;
int result = -1;
int num = int.MaxValue;
float num2 = float.PositiveInfinity;
for (int i = 0; i < triggers.Count; i++)
{
InteractionTriggerBase val = triggers[i];
float value;
float num3 = (s_found.TryGetValue(val, out value) ? value : DistanceTo(val, torso));
if (val.DetectionPriority < num || (val.DetectionPriority == num && num3 < num2))
{
result = i;
num = val.DetectionPriority;
num2 = num3;
}
}
return result;
}
internal static float DistanceTo(InteractionTriggerBase trigger, Vector3 torso)
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
Collider interactionCollider = trigger.InteractionCollider;
Vector3 val;
if (!((Object)(object)interactionCollider != (Object)null) || !interactionCollider.enabled)
{
val = ((Component)trigger).transform.position;
}
else
{
Bounds bounds = interactionCollider.bounds;
val = ((Bounds)(ref bounds)).ClosestPoint(torso);
}
Vector3 val2 = Flat(val - torso);
return ((Vector3)(ref val2)).magnitude;
}
private static Vector3 Flat(Vector3 v)
{
//IL_0000: 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_0011: Unknown result type (might be due to invalid IL or missing references)
return new Vector3(v.x, 0f, v.z);
}
internal static MethodBase InteractCoroutine()
{
return AccessTools.Method(typeof(Character).GetNestedTypes(AccessTools.all).First((Type t) => t.Name.Contains("InteractButtonCoroutine")), "MoveNext", (Type[])null, (Type[])null);
}
internal static IEnumerable<CodeInstruction> HoldTimes(IEnumerable<CodeInstruction> instructions)
{
//IL_0196: Unknown result type (might be due to invalid IL or missing references)
//IL_01a8: Expected O, but got Unknown
//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0111: Expected O, but got Unknown
//IL_0131: Unknown result type (might be due to invalid IL or missing references)
//IL_013b: Expected O, but got Unknown
//IL_0151: Unknown result type (might be due to invalid IL or missing references)
//IL_0157: Expected O, but got Unknown
FieldInfo fieldInfo = null;
MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(InteractionTriggerBase), "HoldActivationTime");
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
foreach (CodeInstruction item in list)
{
if (item.opcode == OpCodes.Ldfld && item.operand is FieldInfo fieldInfo2 && fieldInfo2.FieldType == typeof(Character) && fieldInfo2.Name.Contains("4__this"))
{
fieldInfo = fieldInfo2;
}
}
if (fieldInfo == null)
{
Plugin.Log.LogError((object)"InteractButtonCoroutine: no <>4__this field, hold time unchanged");
return list;
}
int num = 0;
int num2 = 0;
for (int i = 0; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Ldc_R4 && (float)list[i].operand == 0.4f)
{
CodeInstruction val = CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Ldarg_0, (object)null), list[i]);
list[i] = new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(GatherPatches), "HoldThreshold", (Type[])null, (Type[])null));
list.InsertRange(i, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[2]
{
val,
new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo)
});
i += 2;
num++;
}
else if (CodeInstructionExtensions.Calls(list[i], methodInfo))
{
list[i] = CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(GatherPatches), "HoldActivationTime", (Type[])null, (Type[])null)), list[i]);
num2++;
}
}
if (num != 4 || num2 != 1)
{
Plugin.Log.LogWarning((object)$"InteractButtonCoroutine: replaced {num} thresholds (expected 4) and {num2} hold times (expected 1)");
}
return list;
}
internal static float HoldThreshold(Character character)
{
InteractionTriggerBase val = s_lastInteractable.Invoke(character);
if (!Applies(val))
{
return 0.4f;
}
EventActivator val2 = s_activator.Invoke(val);
if (!((Object)(object)val2 != (Object)null) || !val2.CanDoBasic)
{
return 0f;
}
return Mathf.Min(0.4f, Plugin.HoldTime.Value * 0.5f);
}
internal static float HoldActivationTime(InteractionTriggerBase trigger)
{
if (!Applies(trigger))
{
return trigger.HoldActivationTime;
}
return Plugin.HoldTime.Value;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(InteractionTriggerBase), "ActivateBasicAction", new Type[]
{
typeof(Character),
typeof(int)
})]
private static void FaceOnBasic(InteractionTriggerBase __instance, Character _character)
{
Face(__instance, _character);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(InteractionTriggerBase), "ActivateHoldAction")]
private static void FaceOnHold(InteractionTriggerBase __instance, Character _character)
{
Face(__instance, _character);
}
private static void Face(InteractionTriggerBase trigger, Character character)
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
if (Plugin.FaceSource.Value && !((Object)(object)character == (Object)null) && character.IsLocalPlayer && Applies(trigger))
{
Vector3 val = ((Component)character).transform.position + Vector3.up * 0.9f;
Vector3 val2 = Flat(NearestPoint(trigger, val) - val);
if (((Vector3)(ref val2)).sqrMagnitude > 0.01f)
{
((Component)character).transform.rotation = Quaternion.LookRotation(val2);
}
}
}
internal static Vector3 NearestPoint(InteractionTriggerBase trigger, Vector3 from)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
Vector3 best = ((Component)trigger).transform.position;
float bestDistance = float.PositiveInfinity;
Consider(trigger.InteractionCollider);
foreach (Collider item in s_extraColliders.Invoke(trigger))
{
Consider(item);
}
return best;
void Consider(Collider collider)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)collider == (Object)null) && collider.enabled && ((Component)collider).gameObject.activeInHierarchy)
{
Bounds bounds = collider.bounds;
Vector3 val = ((Bounds)(ref bounds)).ClosestPoint(from);
Vector3 val2 = Flat(val - from);
float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
if (sqrMagnitude < bestDistance)
{
best = val;
bestDistance = sqrMagnitude;
}
}
}
}
}
public class TestHarness : MonoBehaviour
{
private static readonly FieldRef<Item, InteractionTriggerBase> s_itemTrigger = AccessTools.FieldRefAccess<Item, InteractionTriggerBase>("m_interactionTrigger");
private static readonly FieldRef<InteractionTriggerBase, EventActivator> s_activator = AccessTools.FieldRefAccess<InteractionTriggerBase, EventActivator>("m_defaultTriggerManager");
private static readonly FieldRef<CharacterCamera, Transform> s_cameraPitch = AccessTools.FieldRefAccess<CharacterCamera, Transform>("m_cameraVertHolder");
private static readonly FieldRef<CharacterUI, InteractionDisplay> s_prompt = AccessTools.FieldRefAccess<CharacterUI, InteractionDisplay>("m_interactionPanel");
private static Gatherable s_activated;
private static float s_activatedAt;
private static Gatherable s_gathered;
private static float s_gatheredAt;
private static InteractionTriggerBase s_held;
private static float s_heldAt;
private int m_failures;
private static bool s_driving;
private static Vector2 s_move;
private bool m_spotFound;
private Vector3 m_spotAway;
private static readonly FieldRef<LocalCharacterControl, Transform> s_horiControl = AccessTools.FieldRefAccess<LocalCharacterControl, Transform>("m_horiControl");
private bool m_recording;
public static void StartIfRequested(GameObject host)
{
if (UnityEngineExtensions.Contains<string>(Environment.GetCommandLineArgs(), "--gathering-test"))
{
host.AddComponent<TestHarness>();
}
}
private void Start()
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Expected O, but got Unknown
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Expected O, but got Unknown
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Expected O, but got Unknown
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Unknown result type (might be due to invalid IL or missing references)
//IL_010d: Expected O, but got Unknown
new Harmony("tchernobill.gathering.test").Patch((MethodBase)AccessTools.Method(typeof(Gatherable), "OnGatherInteraction", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(TestHarness), "OnGathered", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
new Harmony("tchernobill.gathering.test").Patch((MethodBase)AccessTools.Method(typeof(GatherableInteraction), "Activate", new Type[1] { typeof(Character) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(TestHarness), "OnActivated", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
new Harmony("tchernobill.gathering.test").Patch((MethodBase)AccessTools.Method(typeof(InteractionTriggerBase), "ActivateHoldAction", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(TestHarness), "OnHeld", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
new Harmony("tchernobill.gathering.test").Patch((MethodBase)AccessTools.Method(typeof(LocalCharacterControl), "DetectMovementInputs", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(TestHarness), "DriveMovement", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
((MonoBehaviour)this).StartCoroutine(Run());
}
private static void DriveMovement(ref Vector2 ___m_moveInput, ref Vector3 ___m_modifMoveInput)
{
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
if (s_driving)
{
___m_moveInput = s_move;
___m_modifMoveInput = Vector2.op_Implicit(s_move);
}
}
private static void OnGathered(Gatherable __instance)
{
s_gathered = __instance;
s_gatheredAt = Time.realtimeSinceStartup;
}
private static void OnActivated(GatherableInteraction __instance)
{
s_activated = __instance.Gatherable;
s_activatedAt = Time.realtimeSinceStartup;
}
private static void OnHeld(InteractionTriggerBase __instance)
{
s_held = __instance;
s_heldAt = Time.realtimeSinceStartup;
}
private static void Log(string message)
{
Plugin.Log.LogMessage((object)("gathering-test: " + message));
}
private void Check(bool ok, string what)
{
if (ok)
{
Log("ok " + what);
return;
}
m_failures++;
Plugin.Log.LogError((object)("gathering-test: KO " + what));
}
private static IEnumerator WaitRealtime(float seconds)
{
float end = Time.realtimeSinceStartup + seconds;
while (Time.realtimeSinceStartup < end)
{
yield return null;
}
}
private static string Arg(string name)
{
string[] commandLineArgs = Environment.GetCommandLineArgs();
int num = Array.IndexOf(commandLineArgs, name);
if (num < 0 || num + 1 >= commandLineArgs.Length)
{
return null;
}
return commandLineArgs[num + 1];
}
private static InteractionTriggerBase TriggerOf(Gatherable g)
{
return s_itemTrigger.Invoke((Item)(object)g);
}
private static bool Usable(Gatherable g)
{
InteractionTriggerBase val = TriggerOf(g);
EventActivator val2 = (((Object)(object)val != (Object)null) ? s_activator.Invoke(val) : null);
if (((Behaviour)g).isActiveAndEnabled && (Object)(object)g.RequiredItem == (Object)null && (Object)(object)val2 != (Object)null && val2.CanDoHold)
{
return val.CanBeTriggered;
}
return false;
}
private static Vector3 Flat(Vector3 v)
{
//IL_0000: 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_0011: Unknown result type (might be due to invalid IL or missing references)
return new Vector3(v.x, 0f, v.z);
}
private static Vector3 Centre(Gatherable g)
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
InteractionTriggerBase obj = TriggerOf(g);
Collider val = ((obj != null) ? obj.InteractionCollider : null);
if (!((Object)(object)val != (Object)null))
{
return ((Component)g).transform.position;
}
Bounds bounds = val.bounds;
return ((Bounds)(ref bounds)).center;
}
private static bool Place(Character character, Vector3 spot, Vector3 facing)
{
//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_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_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
RaycastHit val = default(RaycastHit);
if (!Physics.Raycast(spot + Vector3.up * 4f, Vector3.down, ref val, 10f, Global.GeneralEnvironmentMask))
{
return false;
}
character.Teleport(((RaycastHit)(ref val)).point + Vector3.up * 0.05f, Quaternion.LookRotation(Flat(facing)));
return true;
}
private IEnumerator Settle()
{
yield return WaitRealtime(0.6f);
}
private static float Measured(Character character, Gatherable g)
{
//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_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)
return GatherPatches.DistanceTo(TriggerOf(g), ((Component)character).transform.position + Vector3.up * 0.9f);
}
private static string OutputDir()
{
string text = Path.Combine(Paths.BepInExRootPath, "GatheringTest");
Directory.CreateDirectory(text);
return text;
}
private static void Capture(string file)
{
Texture2D val = ScreenCapture.CaptureScreenshotAsTexture();
string text = Path.Combine(OutputDir(), file);
File.WriteAllBytes(text, ImageConversion.EncodeToPNG(val));
Log($"capture {((Texture)val).width}x{((Texture)val).height} saved to {text}");
}
private static Vector3 PointOf(InteractionTriggerBase trigger)
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: 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)
Collider interactionCollider = trigger.InteractionCollider;
if (!((Object)(object)interactionCollider != (Object)null) || !interactionCollider.enabled)
{
return ((Component)trigger).transform.position;
}
Bounds bounds = interactionCollider.bounds;
return ((Bounds)(ref bounds)).center;
}
private IEnumerator FindSpot(Character character, InteractionTriggerBase trigger, float distance)
{
m_spotFound = false;
Vector3 centre = PointOf(trigger);
int[] array = new int[8] { 0, 90, 180, 270, 45, 135, 225, 315 };
foreach (int num in array)
{
Vector3 dir = Quaternion.Euler(0f, (float)num, 0f) * Vector3.forward;
Collider interactionCollider = trigger.InteractionCollider;
float num2;
if (!((Object)(object)interactionCollider != (Object)null) || !interactionCollider.enabled)
{
num2 = 0f;
}
else
{
Bounds bounds = interactionCollider.bounds;
Vector3 val = Flat(centre - ((Bounds)(ref bounds)).ClosestPoint(centre + dir * 10f));
num2 = ((Vector3)(ref val)).magnitude;
}
float num3 = num2;
if (Place(character, centre + dir * (num3 + distance), dir))
{
yield return Settle();
if ((Object)(object)character.Interactable == (Object)(object)trigger)
{
m_spotFound = true;
m_spotAway = dir;
break;
}
}
}
}
private IEnumerator HoldTest(Character character, InteractionTriggerBase trigger, float hold, string label)
{
Plugin.HoldTime.Value = hold;
s_held = null;
float pressed = Time.realtimeSinceStartup;
character.OnInteractButtonDown();
while ((Object)(object)s_held == (Object)null && Time.realtimeSinceStartup - pressed < 3f)
{
yield return null;
}
float elapsed = (((Object)(object)s_held != (Object)null) ? (s_heldAt - pressed) : float.NaN);
yield return WaitRealtime(3f);
character.OnInteractButtonUp();
yield return WaitRealtime(0.5f);
Vector3 val = ((Component)character).transform.position + Vector3.up * 0.9f;
float num = Vector3.Angle(Flat(((Component)character).transform.forward), Flat(GatherPatches.NearestPoint(trigger, val) - val));
TestHarness testHarness = this;
bool ok = (Object)(object)s_held == (Object)(object)trigger && elapsed >= hold - 0.02f && elapsed <= hold + 0.15f;
object[] obj = new object[4] { label, hold, null, null };
object obj2;
if (!((Object)(object)s_held != (Object)null))
{
obj2 = "nothing";
}
else
{
Item parentItem = s_held.ParentItem;
obj2 = ((parentItem != null) ? parentItem.Name : null);
}
obj[2] = obj2;
obj[3] = elapsed;
testHarness.Check(ok, string.Format("{0}: hold time {1} s, hold done on {2} after {3:F2} s", obj));
Check(num < 25f, $"{label}: faces the source after the hold ({num:F0} deg off)");
Plugin.HoldTime.Value = (float)((ConfigEntryBase)Plugin.HoldTime).DefaultValue;
}
private static float MeasuredTo(Character character, Vector3 point)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
Vector3 val = Flat(point - (((Component)character).transform.position + Vector3.up * 0.9f));
return ((Vector3)(ref val)).magnitude;
}
private static string Describe(Character character)
{
InteractionTriggerBase interactable = character.Interactable;
if ((Object)(object)interactable == (Object)null)
{
return "nothing";
}
if (!((Object)(object)interactable.ParentItem != (Object)null))
{
return ((Object)interactable).name;
}
return interactable.ParentItem.Name + " (" + interactable.ParentItem.UID + ")";
}
private static List<Vector3> Walkable(Vector3 from, Vector3 to)
{
//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_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: 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_0034: Unknown result type (might be due to invalid IL or missing references)
//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)
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0138: Unknown result type (might be due to invalid IL or missing references)
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
//IL_00af: Unknown result type (might be due to invalid IL or missing references)
//IL_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_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_0107: Unknown result type (might be due to invalid IL or missing references)
//IL_010e: Unknown result type (might be due to invalid IL or missing references)
//IL_0113: Unknown result type (might be due to invalid IL or missing references)
//IL_011d: Unknown result type (might be due to invalid IL or missing references)
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
List<Vector3> list = new List<Vector3>();
Vector3 val = Flat(to - from);
int num = Mathf.CeilToInt(((Vector3)(ref val)).magnitude / 0.5f);
RaycastHit val2 = default(RaycastHit);
for (int i = 0; i <= num; i++)
{
if (!Physics.Raycast(Vector3.Lerp(from, to, (float)i / (float)num) + Vector3.up * 3f, Vector3.down, ref val2, 8f, Global.GeneralEnvironmentMask) || (list.Count > 0 && Mathf.Abs(((RaycastHit)(ref val2)).point.y - list[0].y) > 0.8f))
{
return null;
}
if (list.Count > 0 && (Physics.Linecast(list[list.Count - 1] + Vector3.up * 0.5f, ((RaycastHit)(ref val2)).point + Vector3.up * 0.5f, Global.GeneralEnvironmentMask) || Physics.Linecast(list[list.Count - 1] + Vector3.up * 1.6f, ((RaycastHit)(ref val2)).point + Vector3.up * 1.6f, Global.GeneralEnvironmentMask)))
{
return null;
}
list.Add(((RaycastHit)(ref val2)).point);
}
return list;
}
private static void Steer(Character character, Vector3 direction)
{
//IL_0017: 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_001d: 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_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: 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)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
FieldRef<LocalCharacterControl, Transform> obj = s_horiControl;
CharacterControl characterControl = character.CharacterControl;
Transform val = obj.Invoke((LocalCharacterControl)(object)((characterControl is LocalCharacterControl) ? characterControl : null));
Vector3 val2 = Flat(direction);
Vector3 normalized = ((Vector3)(ref val2)).normalized;
val2 = Flat(val.right);
float num = Vector3.Dot(normalized, ((Vector3)(ref val2)).normalized);
val2 = Flat(val.forward);
s_move = new Vector2(num, Vector3.Dot(normalized, ((Vector3)(ref val2)).normalized));
}
private static IEnumerator WaitGame(float seconds)
{
float end = Time.time + seconds;
while (Time.time < end)
{
yield return null;
}
}
private IEnumerator Video(Character character, Gatherable target, bool vanilla)
{
InteractionTriggerBase trigger = TriggerOf(target);
Vector3 bush = Centre(target);
Vector3 away = Vector3.zero;
Vector3 side = Vector3.zero;
List<Vector3> list = null;
int[] array = new int[8] { 0, 45, 90, 135, 180, 225, 270, 315 };
foreach (int num in array)
{
float[] array2 = new float[2] { 1f, -1f };
foreach (float num2 in array2)
{
Vector3 val = Quaternion.Euler(0f, (float)num, 0f) * Vector3.forward;
Vector3 val2 = Vector3.Cross(Vector3.up, val) * num2;
Vector3 val3 = bush + val * 2.3f;
list = Walkable(val3 - val2 * 5f, val3 + val2 * 2f);
if (list != null && Walkable(val3, bush + val * 0.9f) != null && !Physics.Linecast(val3 + val * 3f + Vector3.up * 2.2f, bush + Vector3.up * 0.6f, Global.GeneralEnvironmentMask))
{
away = val;
side = val2;
break;
}
list = null;
}
if (list != null)
{
break;
}
}
if (list == null)
{
Check(ok: false, "video: no clear path past the bush");
yield break;
}
Vector3 passPoint = bush + away * 2.3f;
Log(string.Format("video {0}: {1} at {2}, path {3} -> {4} (side {5}), away {6}", vanilla ? "vanilla" : "mod", ((Item)target).Name, bush, list[0], list[list.Count - 1], side, away));
GatherPatches.Vanilla = vanilla;
Place(character, list[0], side);
yield return WaitRealtime(1.5f);
CharacterCamera camera = character.CharacterCamera;
Quaternion yaw = Quaternion.LookRotation(-away + side * 0.5f);
((Component)camera).transform.rotation = yaw;
((Component)s_prompt.Invoke(character.CharacterUI)).transform.localScale = Vector3.one * 1.5f;
s_cameraPitch.Invoke(camera).localRotation = Quaternion.Euler(18f, 0f, 0f);
yield return WaitRealtime(1.5f);
string text = Path.Combine(OutputDir(), "video_" + (vanilla ? "vanilla" : "mod"));
if (Directory.Exists(text))
{
Directory.Delete(text, recursive: true);
}
Directory.CreateDirectory(text);
Time.captureFramerate = 30;
m_recording = true;
((MonoBehaviour)this).StartCoroutine(Record(text));
float start = Time.time;
yield return WaitGame(0.7f);
s_driving = true;
Steer(character, side);
while ((Object)(object)character.Interactable != (Object)(object)trigger && Vector3.Dot(((Component)character).transform.position - passPoint, side) < 0f && Time.time - start < 10f)
{
KeepCamera();
yield return null;
}
bool onTheWay = (Object)(object)character.Interactable == (Object)(object)trigger;
while ((Object)(object)character.Interactable != (Object)(object)trigger && Time.time - start < 12f)
{
Steer(character, bush - ((Component)character).transform.position);
KeepCamera();
yield return null;
}
s_move = Vector2.zero;
float reached = Time.time - start;
for (float t = 0f; t < 0.3f; t += Time.deltaTime)
{
KeepCamera();
yield return null;
}
s_activated = (s_gathered = null);
float pressed = Time.time;
character.OnInteractButtonDown();
while ((Object)(object)s_activated == (Object)null && Time.time - pressed < 4f)
{
KeepCamera();
yield return null;
}
float held = Time.time - pressed;
character.OnInteractButtonUp();
while ((Object)(object)s_gathered == (Object)null && Time.time - pressed < 8f)
{
KeepCamera();
yield return null;
}
float gathered = Time.time - start;
for (float t = 0f; t < 2f; t += Time.deltaTime)
{
KeepCamera();
yield return null;
}
s_driving = false;
m_recording = false;
yield return null;
Time.captureFramerate = 0;
GatherPatches.Vanilla = false;
Log(string.Format("video {0}: prompt {1} at {2:F2} s, held {3:F2} s, gathered at {4:F2} s (from the start of the walk at 0.70 s)", vanilla ? "vanilla" : "mod", onTheWay ? "on the way" : "after turning", reached, held, gathered));
TestHarness testHarness = this;
bool ok = (Object)(object)s_gathered == (Object)(object)target;
string[] obj = new string[5]
{
"video: ",
((Item)target).Name,
" gathered (",
null,
null
};
Gatherable obj2 = s_gathered;
obj[3] = ((obj2 != null) ? ((Item)obj2).Name : null) ?? "nothing";
obj[4] = ")";
testHarness.Check(ok, string.Concat(obj));
void KeepCamera()
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
((Component)camera).transform.rotation = yaw;
}
}
private IEnumerator Record(string dir)
{
int frame = 0;
WaitForEndOfFrame endOfFrame = new WaitForEndOfFrame();
while (m_recording)
{
yield return endOfFrame;
Texture2D val = ScreenCapture.CaptureScreenshotAsTexture();
File.WriteAllBytes(Path.Combine(dir, $"frame_{frame++:D4}.jpg"), ImageConversion.EncodeToJPG(val, 92));
Object.Destroy((Object)(object)val);
}
Log($"video: {frame} frames in {dir}");
}
private IEnumerator Run()
{
string scene = Arg("--gathering-scene");
float deadline = Time.realtimeSinceStartup + 900f;
Character character;
Scene activeScene;
while (true)
{
if (Time.realtimeSinceStartup > deadline)
{
Plugin.Log.LogError((object)"gathering-test: FAILED not in the scene after 900 s");
yield break;
}
try
{
CharacterManager instance = CharacterManager.Instance;
character = ((instance != null) ? instance.GetFirstLocalCharacter() : null);
}
catch (NullReferenceException)
{
character = null;
}
NetworkLevelLoader instance2 = NetworkLevelLoader.Instance;
if ((Object)(object)character != (Object)null && (Object)(object)instance2 != (Object)null && instance2.IsOverallLoadingDone && !instance2.IsGameplayLoading)
{
if (scene == null)
{
break;
}
activeScene = SceneManager.GetActiveScene();
if (((Scene)(ref activeScene)).name == scene)
{
break;
}
}
yield return WaitRealtime(1f);
}
yield return WaitRealtime(3f);
MenuManager.Instance.MasterCanvas.alpha = 1f;
MenuManager.Instance.MasterCanvas.interactable = true;
object[] array = new object[4];
activeScene = SceneManager.GetActiveScene();
array[0] = ((Scene)(ref activeScene)).name;
array[1] = Plugin.Distance.Value;
array[2] = Plugin.Angle.Value;
array[3] = Plugin.HoldTime.Value;
Log(string.Format("in {0}, settings distance {1}, angle {2}, hold {3}", array));
Gatherable[] array2 = Object.FindObjectsOfType<Gatherable>();
List<Gatherable> list = array2.Where(Usable).ToList();
Log($"{array2.Length} gatherables, {list.Count} usable without tool: " + string.Join(", ", from val4 in list
group val4 by ((Item)val4).Name into grp
select $"{grp.Key} x{grp.Count()}"));
List<(Gatherable g, Vector3 at)> sources = new List<(Gatherable, Vector3)>();
foreach (Gatherable item2 in list)
{
CentralGatherable central = (CentralGatherable)(object)((item2 is CentralGatherable) ? item2 : null);
if (central != null)
{
sources.AddRange(central.AccessPoints.Where((CentralGatherableAccessPoint ap) => (Object)(object)ap != (Object)null && (Object)(object)ap.InteractionCollider != (Object)null).Select(delegate(CentralGatherableAccessPoint ap)
{
//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_0014: Unknown result type (might be due to invalid IL or missing references)
CentralGatherable item = central;
Bounds bounds2 = ap.InteractionCollider.bounds;
return ((Gatherable)(object)item, center: ((Bounds)(ref bounds2)).center);
}));
}
else
{
sources.Add((item2, Centre(item2)));
}
}
Log($"{sources.Count} sources ({sources.Count(((Gatherable g, Vector3 at) src) => src.g is CentralGatherable)} dead wood access points)");
Gatherable target = null;
Vector3 away = Vector3.zero;
foreach (Gatherable g in from val4 in list
where !(val4 is CentralGatherable)
orderby (!((Item)val4).Name.Contains("Gaberry")) ? 1 : 0
select val4)
{
int[] array3 = new int[8] { 0, 90, 180, 270, 45, 135, 225, 315 };
foreach (int num2 in array3)
{
Vector3 dir = Quaternion.Euler(0f, (float)num2, 0f) * Vector3.forward;
if (Place(character, Centre(g) + dir * 1.5f, dir))
{
yield return Settle();
if ((Object)(object)character.Interactable == (Object)(object)TriggerOf(g))
{
target = g;
away = dir;
break;
}
}
}
if ((Object)(object)target != (Object)null)
{
break;
}
}
if ((Object)(object)target == (Object)null)
{
Plugin.Log.LogError((object)"gathering-test: FAILED no gatherable detected from behind at 1.5 m");
yield break;
}
InteractionTriggerBase targetTrigger = TriggerOf(target);
object[] obj = new object[5]
{
((Item)target).Name,
((Item)target).UID,
Centre(target),
((object)targetTrigger.InteractionCollider)?.GetType().Name,
null
};
Collider interactionCollider = targetTrigger.InteractionCollider;
Vector3? obj2;
Bounds bounds;
if (interactionCollider == null)
{
obj2 = null;
}
else
{
bounds = interactionCollider.bounds;
obj2 = ((Bounds)(ref bounds)).size;
}
obj[4] = obj2;
Log(string.Format("target {0} {1} at {2}, collider {3} {4}, ", obj) + $"priority {targetTrigger.DetectionPriority}, basic {s_activator.Invoke(targetTrigger).CanDoBasic}");
float[] array4;
if (UnityEngineExtensions.Contains<string>(Environment.GetCommandLineArgs(), "--gathering-icon"))
{
int num = 0;
Transform prompt = ((Component)s_prompt.Invoke(character.CharacterUI)).transform;
array4 = new float[2] { 1f, 2f };
foreach (float scale in array4)
{
float[] array5 = new float[2] { 2.4f, 2.8f };
foreach (float back in array5)
{
float[] array6 = new float[2] { 12f, 18f };
foreach (float pitch in array6)
{
prompt.localScale = Vector3.one * scale;
Vector3 dir = -away;
Place(character, Centre(target) - dir * back, dir);
yield return WaitRealtime(1f);
CharacterCamera characterCamera = character.CharacterCamera;
((Component)characterCamera).transform.rotation = Quaternion.LookRotation(dir);
s_cameraPitch.Invoke(characterCamera).localRotation = Quaternion.Euler(pitch, 0f, 0f);
yield return WaitRealtime(2f);
Log($"icon shot {num}: {back} m, pitch {pitch}, prompt x{scale}, active = {Describe(character)}, hold '{character.HoldText}'");
yield return (object)new WaitForEndOfFrame();
Capture($"icon_{num++}.png");
}
}
}
Plugin.Log.LogMessage((object)"gathering-test: done");
yield break;
}
string text = Arg("--gathering-video");
if (text != null)
{
yield return Video(character, target, text == "vanilla");
Plugin.Log.LogMessage((object)((m_failures == 0) ? "gathering-test: done" : $"gathering-test: FAILED {m_failures} check(s)"));
yield break;
}
Vector3 val = Centre(target);
bounds = targetTrigger.InteractionCollider.bounds;
Vector3 val2 = Flat(val - ((Bounds)(ref bounds)).ClosestPoint(Centre(target) + away * 10f));
float extent = Mathf.Max(0f, ((Vector3)(ref val2)).magnitude);
array4 = new float[2] { 2.5f, 3.4f };
foreach (float num6 in array4)
{
Place(character, Centre(target) + away * (extent + num6), away);
yield return Settle();
float num7 = Measured(character, target);
bool flag = num7 <= Plugin.Distance.Value;
Check((Object)(object)character.Interactable == (Object)(object)targetTrigger == flag, string.Format("behind at {0:F2} m (limit {1}): {2}expected, active = {3}", num7, Plugin.Distance.Value, flag ? "" : "not ", Describe(character)));
}
Place(character, Centre(target) + away * (extent + 1.5f), away);
Plugin.Angle.Value = 90f;
yield return Settle();
Check((Object)(object)character.Interactable != (Object)(object)targetTrigger, "behind with angle 90: not expected, active = " + Describe(character));
Place(character, Centre(target) + away * (extent + 1.5f), -away);
yield return Settle();
Check((Object)(object)character.Interactable == (Object)(object)targetTrigger, "in front with angle 90: expected, active = " + Describe(character));
Plugin.Angle.Value = 180f;
(Gatherable target, Vector3) self = (target: target, Centre(target));
(Gatherable g, Vector3 at) first = default((Gatherable, Vector3));
(Gatherable g, Vector3 at) pair = default((Gatherable, Vector3));
float num8 = float.PositiveInfinity;
foreach (var x in sources.Where(((Gatherable g, Vector3 at) src) => !(src.g is CentralGatherable)))
{
foreach (var item3 in sources.Where(((Gatherable g, Vector3 at) src) => (Object)(object)src.g != (Object)(object)x.g))
{
val2 = Flat(item3.at - x.at);
float magnitude = ((Vector3)(ref val2)).magnitude;
if (magnitude >= 2f && magnitude <= 9f && Mathf.Abs(magnitude - 4f) < Mathf.Abs(num8 - 4f))
{
first = x;
pair = item3;
num8 = magnitude;
}
}
}
if ((Object)(object)pair.g == (Object)null)
{
Log("no two gatherables 2 to 9 m apart: nearest test skipped");
}
else
{
Plugin.Distance.Value = 7f;
Log($"pair {((Item)first.g).Name} at {first.at} and {((Item)pair.g).Name} at {pair.at}, {num8:F2} m apart, distance set to 7");
((Gatherable g, Vector3 at), (Gatherable g, Vector3 at), float)[] array7 = new((Gatherable, Vector3), (Gatherable, Vector3), float)[2]
{
(first, pair, 0.35f),
(pair, first, 0.65f)
};
for (int num = 0; num < array7.Length; num++)
{
(Gatherable g, Vector3 at) near;
(Gatherable g, Vector3 at) far;
float num9;
(near, far, num9) = array7[num];
Place(character, Vector3.Lerp(first.at, pair.at, num9), far.at - near.at);
yield return Settle();
Check((Object)(object)character.Interactable == (Object)(object)TriggerOf(near.g), $"nearest {((Item)near.g).Name} at {MeasuredTo(character, near.at):F2} m (behind) vs {((Item)far.g).Name} at {MeasuredTo(character, far.at):F2} m (in front), active = {Describe(character)}");
}
Plugin.Distance.Value = (float)((ConfigEntryBase)Plugin.Distance).DefaultValue;
}
Plugin.HoldTime.Value = 1f;
Place(character, Centre(target) + away * (extent + 1.5f), away);
yield return Settle();
s_gathered = null;
character.OnInteractButtonDown();
yield return WaitRealtime(0.5f);
character.OnInteractButtonUp();
yield return WaitRealtime(1f);
TestHarness testHarness = this;
bool ok = (Object)(object)s_gathered == (Object)null;
Gatherable obj3 = s_gathered;
testHarness.Check(ok, "hold time 1 s, released at 0.5 s: no gather (gathered " + (((obj3 != null) ? ((Item)obj3).Name : null) ?? "nothing") + ")");
array4 = new float[2] { 0.2f, 0f };
int num3;
foreach (float scale in array4)
{
Gatherable g;
Vector3 dir;
if (scale > 0f)
{
(g, dir) = self;
}
else
{
(g, dir) = (from src in sources
where (Object)(object)src.g != (Object)(object)target && Usable(src.g)
orderby (!(src.g is CentralGatherable)) ? 1 : 0
select src).ThenBy(delegate((Gatherable g, Vector3 at) src)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
Vector3 val4 = Flat(src.at - self.Item2);
return ((Vector3)(ref val4)).magnitude;
}).FirstOrDefault();
}
if ((Object)(object)g == (Object)null)
{
Log("no other usable gatherable: hold 0 test skipped");
break;
}
Plugin.HoldTime.Value = scale;
if (!(scale > 0f))
{
_ = Vector3.forward;
}
bool placed = false;
int[] array3 = new int[4] { 0, 90, 180, 270 };
foreach (int num10 in array3)
{
Vector3 val3 = Quaternion.Euler(0f, (float)num10, 0f) * ((scale > 0f) ? away : Vector3.forward);
if (Place(character, dir + val3 * 1.5f, val3))
{
yield return Settle();
if ((Object)(object)character.Interactable == (Object)(object)TriggerOf(g))
{
placed = true;
break;
}
}
}
if (!placed)
{
Check(ok: false, $"hold {scale}: {((Item)g).Name} not detected from behind");
continue;
}
num3 = character.Inventory.Pouch.ItemCount;
s_gathered = (s_activated = null);
float back = Time.realtimeSinceStartup;
character.OnInteractButtonDown();
while ((Object)(object)s_gathered == (Object)null && Time.realtimeSinceStartup - back < 3f)
{
yield return null;
}
yield return WaitRealtime(0.5f);
float pitch = (((Object)(object)s_activated != (Object)null) ? (s_activatedAt - back) : float.NaN);
float animated = (((Object)(object)s_gathered != (Object)null) ? (s_gatheredAt - back) : float.NaN);
yield return WaitRealtime(2.5f);
character.OnInteractButtonUp();
yield return WaitRealtime(0.5f);
float num11 = Vector3.Angle(Flat(((Component)character).transform.forward), Flat(dir - ((Component)character).transform.position));
TestHarness testHarness2 = this;
bool ok2 = (Object)(object)s_activated == (Object)(object)g && (Object)(object)s_gathered == (Object)(object)g && pitch >= scale - 0.02f && pitch <= scale + 0.15f;
object[] obj4 = new object[4] { scale, null, null, null };
Gatherable obj5 = s_activated;
obj4[1] = ((obj5 != null) ? ((Item)obj5).Name : null) ?? "nothing";
obj4[2] = pitch;
obj4[3] = animated;
testHarness2.Check(ok2, string.Format("hold time {0} s: hold done on {1} after {2:F2} s, items after {3:F2} s", obj4));
Check(num11 < 25f, $"hold time {scale} s: faces {((Item)g).Name} at {dir} after gathering ({num11:F0} deg off)");
Log($"inventory {num3} -> {character.Inventory.Pouch.ItemCount} items");
}
Dictionary<SourceKind, int> tools = new Dictionary<SourceKind, int>
{
[SourceKind.Mining] = 2120050,
[SourceKind.Fishing] = 2130130
};
List<Item> items = Resources.FindObjectsOfTypeAll<Item>().Where(delegate(Item i)
{
//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)
Scene scene2 = ((Component)i).gameObject.scene;
return ((Scene)(ref scene2)).IsValid() && (i is Gatherable || i is TreasureChest);
}).ToList();
(SourceKind, ConfigEntry<bool>)[] array8 = new(SourceKind, ConfigEntry<bool>)[3]
{
(SourceKind.Mining, Plugin.Mining),
(SourceKind.Fishing, Plugin.Fishing),
(SourceKind.Chest, Plugin.Chests)
};
for (int num = 0; num < array8.Length; num++)
{
(SourceKind, ConfigEntry<bool>) tuple4 = array8[num];
SourceKind kind = tuple4.Item1;
ConfigEntry<bool> option = tuple4.Item2;
Vector3 here = ((Component)character).transform.position;
List<InteractionTriggerBase> list2 = (from i in items
select s_itemTrigger.Invoke(i) into val4
where (Object)(object)val4 != (Object)null && GatherPatches.KindOf(val4) == kind
orderby (kind != SourceKind.Chest || !((Object)(object)s_activator.Invoke(val4) != (Object)null) || s_activator.Invoke(val4).CanDoBasic) ? 1 : 0
select val4).ThenBy(delegate(InteractionTriggerBase val5)
{
//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_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
Vector3 val4 = ((Component)val5).transform.position - here;
return ((Vector3)(ref val4)).sqrMagnitude;
}).ToList();
Log($"{kind}: {list2.Count} sources: " + string.Join(", ", from val4 in list2
group val4 by val4.ParentItem.Name into grp
select $"{grp.Key} x{grp.Count()}"));
InteractionTriggerBase source = null;
foreach (InteractionTriggerBase t in list2.Take(10))
{
if (!((Behaviour)t.ParentItem).isActiveAndEnabled || !t.CanBeTriggered)
{
Place(character, PointOf(t) + Vector3.forward * 3f, Vector3.forward);
yield return WaitRealtime(3f);
if (!((Behaviour)t.ParentItem).isActiveAndEnabled || !t.CanBeTriggered)
{
continue;
}
}
yield return FindSpot(character, t, 1.6f);
if (m_spotFound)
{
source = t;
break;
}
}
if ((Object)(object)source == (Object)null)
{
Check(ok: false, $"{kind}: no source detected from behind at 1.6 m");
continue;
}
EventActivator activator = s_activator.Invoke(source);
Log($"{kind}: {source.ParentItem.Name} {source.ParentItem.UID} at {PointOf(source)}, basic {activator.CanDoBasic}, hold {activator.HasHoldInteraction} ({source.HoldActivationTime} s vanilla)");
Check(ok: true, $"{kind}: {source.ParentItem.Name} detected from behind with the option on");
option.Value = false;
yield return Settle();
Check((Object)(object)character.Interactable != (Object)(object)source, $"{kind}: not detected from behind with the option off, active = {Describe(character)}");
option.Value = true;
yield return Settle();
Check((Object)(object)character.Interactable == (Object)(object)source, $"{kind}: detected again with the option back on, active = {Describe(character)}");
if (tools.TryGetValue(kind, out num3))
{
if ((Object)(object)character.Inventory.GetCompatibleGatherableTool(num3) == (Object)null)
{
character.Inventory.ReceiveItemReward(num3, 1, false);
}
yield return WaitRealtime(0.5f);
object arg = kind;
Item compatibleGatherableTool = character.Inventory.GetCompatibleGatherableTool(num3);
Log(string.Format("{0}: tool {1}", arg, ((compatibleGatherableTool != null) ? compatibleGatherableTool.Name : null) ?? "missing"));
}
if (activator.HasHoldInteraction && activator.CanDoHold)
{
yield return HoldTest(character, source, 0.2f, kind.ToString());
}
else
{
Log($"{kind}: opens on press, no hold to test");
}
character.CharacterUI.CloseAllMenus((MenuScreens[])null);
yield return WaitRealtime(0.5f);
}
Plugin.HoldTime.Value = (float)((ConfigEntryBase)Plugin.HoldTime).DefaultValue;
Plugin.Log.LogMessage((object)((m_failures == 0) ? "gathering-test: done" : $"gathering-test: FAILED {m_failures} check(s)"));
}
}