using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; using UnityEngine.InputSystem; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Valheim Build Camera")] [assembly: AssemblyFileVersion("0.1.11.0")] [assembly: AssemblyVersion("0.1.11.0")] namespace Lesly.ValheimBuildCamera; [BepInPlugin("lesly.valheim.buildcamera", "Valheim Build Camera", "0.1.11")] [BepInProcess("valheim.exe")] [DefaultExecutionOrder(1000)] public sealed class BuildCamera : BaseUnityPlugin { [HarmonyPatch(typeof(Player), "Update")] private static class BeforePlayerUpdate { private static void Prefix(Player __instance) { BuildCamera instance = Instance; if (Object.op_Implicit((Object)(object)instance) && instance.IsLocal(__instance)) { instance.Tick(fromPlayerUpdate: true); } } } [HarmonyPatch(typeof(GameCamera), "LateUpdate")] private static class DetachedCameraLateUpdate { private static void Postfix(GameCamera __instance) { BuildCamera instance = Instance; if (!Object.op_Implicit((Object)(object)instance) || !instance._active || (Object)(object)__instance != (Object)(object)instance._gameCamera) { return; } try { instance.ApplyPose(); } catch (Exception error) { instance.Fail(error); } } } [HarmonyPatch(typeof(GameCamera), "UpdateCamera")] private static class DetachedCamera { private static bool Prefix(GameCamera __instance) { BuildCamera instance = Instance; if (!Object.op_Implicit((Object)(object)instance) || !instance._active || (Object)(object)__instance != (Object)(object)instance._gameCamera) { return true; } try { instance.ApplyPose(); return false; } catch (Exception error) { instance.Fail(error); return true; } } } [HarmonyPatch(typeof(Player), "SetControls")] private static class KeepBodyStill { private static void Prefix(Player __instance, ref Vector3 movedir, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold, ref bool jump, ref bool crouch, ref bool run, ref bool autoRun, ref bool dodge) { //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) BuildCamera instance = Instance; if (Object.op_Implicit((Object)(object)instance) && instance.ControlsOwned(__instance)) { movedir = Vector3.zero; attack = (attackHold = (secondaryAttack = (secondaryAttackHold = (block = (blockHold = false))))); jump = (crouch = (run = (autoRun = (dodge = false)))); __instance.m_autoRun = false; if (instance._active) { ((Character)__instance).StopMovement(); } } } } [HarmonyPatch(typeof(Player), "SetMouseLook")] private static class KeepBodyLook { private static bool Prefix(Player __instance) { BuildCamera instance = Instance; if (Object.op_Implicit((Object)(object)instance)) { return !instance.ControlsOwned(__instance); } return true; } } [HarmonyPatch(typeof(Player), "TakeInput")] private static class ProtectToggleAndDialogs { private static void Postfix(Player __instance, ref bool __result) { BuildCamera instance = Instance; if (Object.op_Implicit((Object)(object)instance) && instance.IsLocal(__instance) && (instance._chordHeld || Time.unscaledTime < instance._suppressUntil || (instance._active && (UnifiedPopup.IsVisible() || IsGamePausedSafe())))) { __result = false; } } } [HarmonyPatch(typeof(Player), "StartGuardianPower")] private static class ProtectGuardianPowerChord { private static bool Prefix(Player __instance) { BuildCamera instance = Instance; if (!Object.op_Implicit((Object)(object)instance) || !instance.IsLocal(__instance)) { return true; } if (!instance._active) { return !instance._chordHeld; } return false; } } [HarmonyPatch(typeof(Player), "CheckPlacementGhostVSPlayers")] private static class PlacementCollisionScope { private static void Prefix(Player __instance, out bool __state) { BuildCamera instance = Instance; __state = Object.op_Implicit((Object)(object)instance) && instance._active && instance.IsLocal(__instance); if (__state) { CollisionQueries++; } } private static Exception Finalizer(Exception __exception, bool __state) { if (__state) { CollisionQueries = Math.Max(0, CollisionQueries - 1); } return __exception; } } [HarmonyPatch(typeof(Character), "GetCharactersInRange")] private static class PreservePlacementCollisionCoverage { private static void Prefix(ref float radius) { if (CollisionQueries > 0) { radius = Mathf.Max(radius, 60f); } } } public const string PluginId = "lesly.valheim.buildcamera"; public const string PluginVersion = "0.1.11"; internal static BuildCamera Instance; internal static int CollisionQueries; private ConfigEntry _enabled; private ConfigEntry _controllerToggle; private ConfigEntry _hidePlayer; private ConfigEntry _showIndicator; private ConfigEntry _exitOnDamage; private ConfigEntry _toggleKey; private ConfigEntry _resetKey; private ConfigEntry _reportKey; private ConfigEntry _speed; private ConfigEntry _fastMultiplier; private ConfigEntry _radius; private ConfigEntry _reach; private ConfigEntry _startPitch; private ConfigEntry _startFov; private Harmony _harmony; private bool _ready; private bool _active; private bool _failed; private bool _chordHeld; private bool _keyboardTogglePending; private Player _player; private ItemData _buildTool; private GameCamera _gameCamera; private Camera _camera; private Camera _skyCamera; private Vector3 _position; private Vector3 _bodyOrigin; private Vector3 _savedCameraPosition; private Quaternion _savedCameraRotation; private float _yaw; private float _pitch; private float _fov; private float _savedFov; private float _savedSkyFov; private float _savedNearClip; private float _savedReach; private float _appliedReach; private float _suppressUntil; private float _nextRendererScan; private float _activationGraceUntil; private int _tickFrame = -1; private int _keyboardToggleRequestFrame = -1; private string _lastExit = "Not activated yet"; private readonly ButtonChordToggle _controllerChord = new ButtonChordToggle(); private readonly Dictionary _renderers = new Dictionary(); private GUIStyle _indicatorStyle; internal bool Active => _active; internal bool IsLocal(Player player) { if (Object.op_Implicit((Object)(object)player)) { return (Object)(object)player == (Object)(object)Player.m_localPlayer; } return false; } internal bool ControlsOwned(Player player) { if (IsLocal(player)) { if (!_active && !_chordHeld) { return Time.unscaledTime < _suppressUntil; } return true; } return false; } private void Awake() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Expected O, but got Unknown if (Application.isBatchMode) { return; } Instance = this; _enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Enable the build camera on this client."); _toggleKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "ToggleKey", new KeyboardShortcut((KeyCode)287, Array.Empty()), "Enter/leave build camera with a hammer or another building tool equipped."); _resetKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "ResetViewKey", new KeyboardShortcut((KeyCode)288, Array.Empty()), "Reset the active camera to its initial angled view."); _reportKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "ReloadAndReportKey", new KeyboardShortcut((KeyCode)291, Array.Empty()), "Write camera diagnostics, exit camera mode, and reload configuration. Separate from Larger HUD's F8."); _controllerToggle = ((BaseUnityPlugin)this).Config.Bind("Controls", "ControllerStickToggle", true, "Press both stick buttons together to enter/leave. A building tool is needed to enter, but not to leave. Release both before toggling again."); _hidePlayer = ((BaseUnityPlugin)this).Config.Bind("View", "HideLocalPlayer", true, "Hide your character's renderers on your own screen while active. Your body remains in the world."); _showIndicator = ((BaseUnityPlugin)this).Config.Bind("View", "ShowIndicator", true, "Small top-centre reminder while the build camera is active."); _exitOnDamage = ((BaseUnityPlugin)this).Config.Bind("Safety", "ExitOnDamage", true, "Exit immediately when the physical player takes damage. Set false only if you deliberately want environmental damage to be tolerated; death still exits."); _speed = FloatSetting("Camera", "MoveSpeed", 6f, 1f, 15f, "Camera movement in metres per second."); _fastMultiplier = FloatSetting("Camera", "FastMultiplier", 2f, 1f, 3f, "Hold your Run control for faster camera movement."); _radius = FloatSetting("Camera", "CameraRadius", 25f, 8f, 25f, "Maximum camera distance from your body, in metres. First build remains near the loaded building site."); _reach = FloatSetting("Building", "BuildReach", 25f, 5f, 25f, "Temporary building reach from your character's eyes. Native materials, stamina, stations, wards and structural rules still apply."); _startPitch = FloatSetting("View", "StartingPitch", 50f, 20f, 75f, "Downward angle when entering/resetting the camera. Mouse/right stick can adjust it afterward."); _startFov = FloatSetting("View", "FieldOfView", 45f, 25f, 75f, "Starting perspective field of view. Page Up/Down zoom while active; mouse wheel keeps rotating pieces."); try { ValidateHooks(); _harmony = new Harmony("lesly.valheim.buildcamera"); _harmony.PatchAll(typeof(BuildCamera).Assembly); _ready = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Build Camera 0.1.11 loaded for Steam build 25185596. F6 toggle; F7 reset; F10 report/reload."); } catch (Exception ex) { _failed = true; if (_harmony != null) { _harmony.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogError((object)("Build Camera could not install its game hooks and remains inactive. " + ex)); } WriteDiagnostics("Startup"); } private ConfigEntry FloatSetting(string section, string key, float value, float min, float max, string text) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown return ((BaseUnityPlugin)this).Config.Bind(section, key, value, new ConfigDescription(text, (AcceptableValueBase)(object)new AcceptableValueRange(min, max), Array.Empty())); } private void Update() { Tick(); } internal void Tick(bool fromPlayerUpdate = false) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) if (!_ready || !((Behaviour)this).isActiveAndEnabled || _failed || _tickFrame == Time.frameCount) { return; } _tickFrame = Time.frameCount; try { KeyboardShortcut value = _reportKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { WriteDiagnostics("F10 before reload"); StopCamera("Configuration reload", message: false); ((BaseUnityPlugin)this).Config.Reload(); Notify("Build camera settings reloaded; diagnostic report saved."); return; } if (!_enabled.Value) { StopCamera("Disabled in configuration", message: false); _chordHeld = false; _controllerChord.Reset(); return; } Player localPlayer = Player.m_localPlayer; bool flag = MenuBlocksCamera(); bool flag2 = CameraMath.ControllerToggleEligible(_controllerToggle.Value, (Object)(object)localPlayer != (Object)null, flag, _active, (Object)(object)localPlayer != (Object)null && HasBuildingTool(localPlayer)); bool flag3 = IsControllerStickHeld("JoyLStick", left: true); bool flag4 = IsControllerStickHeld("JoyRStick", left: false); _chordHeld = flag2 && flag3 && flag4; bool flag5 = _controllerChord.Step(flag2 && flag3, flag2 && flag4, Time.unscaledDeltaTime); value = _toggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { _keyboardTogglePending = true; _keyboardToggleRequestFrame = Time.frameCount; } bool flag6 = _keyboardTogglePending && Time.frameCount > _keyboardToggleRequestFrame; if (flag6 || flag5) { if (flag6) { _keyboardTogglePending = false; } if (_active) { StopCamera("Toggled off", message: true); } else if (!flag) { StartCamera(localPlayer); } } else { if (!_active) { return; } string text = InvalidActiveReason(); if (text != null) { StopCamera(text, message: true); return; } if (ZInput.GetKeyDown((KeyCode)27, true)) { StopCamera("Escape", message: true); return; } value = _resetKey.Value; if (((KeyboardShortcut)(ref value)).IsDown() && !flag) { ResetView(); _suppressUntil = Time.unscaledTime + 0.2f; } if (!flag && !_chordHeld && Time.unscaledTime >= _suppressUntil) { MoveCamera(); } ApplyPose(); } } catch (Exception error) { Fail(error); } } private void LateUpdate() { if (!_active) { return; } try { string text = InvalidActiveReason(); if (text != null) { StopCamera(text, message: true); return; } KeepBodyAtAnchor(); ApplyPose(); if (_hidePlayer.Value && Time.unscaledTime >= _nextRendererScan) { HideRenderers(); _nextRendererScan = Time.unscaledTime + 0.25f; } } catch (Exception error) { Fail(error); } } private static bool HasBuildingTool(Player player) { return IsBuildTool(((Humanoid)player).RightItem); } private static bool IsBuildTool(ItemData item) { if (item != null && item.m_shared != null) { return (Object)(object)item.m_shared.m_buildPieces != (Object)null; } return false; } private static bool IsControllerStickHeld(string zInputName, bool left) { bool button = ZInput.GetButton(zInputName); try { Gamepad current = Gamepad.current; if (current == null) { return button; } return button || (left ? current.leftStickButton.isPressed : current.rightStickButton.isPressed); } catch (Exception) { return button; } } internal static bool MenuBlocksCamera() { try { return (Object.op_Implicit((Object)(object)Chat.instance) && Chat.instance.HasFocus()) || Console.IsVisible() || TextInput.IsVisible() || InventoryGui.IsVisible() || Menu.IsVisible() || StoreGui.IsVisible() || Minimap.IsOpen() || (Object.op_Implicit((Object)(object)TextViewer.instance) && TextViewer.instance.IsVisible()) || UnifiedPopup.IsVisible() || Hud.IsPieceSelectionVisible() || Hud.InRadial() || PlayerCustomizaton.IsBarberGuiVisible() || IsGamePausedSafe(); } catch (NullReferenceException) { return false; } } private static bool IsGamePausedSafe() { if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } try { return Game.IsPaused(); } catch (NullReferenceException) { return false; } } private void StartCamera(Player local) { //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_00fd: 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_01b7: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)local) || !HasBuildingTool(local)) { Notify("Equip your hammer or another building tool first."); return; } if (((Character)local).IsDead() || ((Character)local).InCutscene() || ((Character)local).IsTeleporting() || ((Character)local).IsAttached() || local.GetDoodadController() != null || !((Character)local).IsOnGround() || ((Character)local).IsSwimming()) { Notify("Stand on solid ground to enter the build camera."); return; } if (!Object.op_Implicit((Object)(object)GameCamera.instance) || GameCamera.InFreeFly()) { Notify("Return to the normal game camera first."); return; } _gameCamera = GameCamera.instance; _camera = ((Component)_gameCamera).GetComponent(); if (!Object.op_Implicit((Object)(object)_camera)) { Notify("The game camera is not ready yet."); _gameCamera = null; return; } _skyCamera = _gameCamera.m_skyCamera; _player = local; _buildTool = ((Humanoid)local).RightItem; _bodyOrigin = ((Component)local).transform.position; _savedCameraPosition = ((Component)_gameCamera).transform.position; _savedCameraRotation = ((Component)_gameCamera).transform.rotation; _savedFov = _camera.fieldOfView; _savedNearClip = _camera.nearClipPlane; _savedSkyFov = (Object.op_Implicit((Object)(object)_skyCamera) ? _skyCamera.fieldOfView : _savedFov); _savedReach = local.m_maxPlaceDistance; _appliedReach = CameraMath.Clamp(_reach.Value, 25f, 5f, 25f); _active = true; _activationGraceUntil = Time.unscaledTime + 0.75f; local.m_autoRun = false; ((Character)local).StopMovement(); local.SetControls(Vector3.zero, false, false, false, false, false, false, false, false, false, false, false); local.m_maxPlaceDistance = _appliedReach; ((Character)local).m_onDamaged = (Action)Delegate.Combine(((Character)local).m_onDamaged, new Action(OnPlayerDamaged)); ResetView(); if (_hidePlayer.Value) { HideRenderers(); } _suppressUntil = Time.unscaledTime + 0.2f; _nextRendererScan = Time.unscaledTime + 0.25f; ApplyPose(); Notify("Build camera ON. Jump: up; Crouch: down. F6 or press both stick clicks: exit."); WriteDiagnostics("Activated"); } private void KeepBodyAtAnchor() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)_player)) { if (Vector3.Distance(((Component)_player).transform.position, _bodyOrigin) > 0.01f) { ((Component)_player).transform.position = _bodyOrigin; Physics.SyncTransforms(); } ((Character)_player).StopMovement(); } } private string InvalidActiveReason() { if (!Object.op_Implicit((Object)(object)_player) || (Object)(object)_player != (Object)(object)Player.m_localPlayer) { return "Player changed"; } if (!Object.op_Implicit((Object)(object)_gameCamera) || (Object)(object)GameCamera.instance != (Object)(object)_gameCamera || !Object.op_Implicit((Object)(object)_camera)) { return "Camera changed"; } if (((Character)_player).IsDead()) { return "Player died"; } if (((Character)_player).InCutscene() || ((Character)_player).IsTeleporting() || ((Character)_player).IsAttached() || _player.GetDoodadController() != null) { return "Player changed activity"; } if (Time.unscaledTime < _activationGraceUntil) { return null; } KeepBodyAtAnchor(); return null; } private void OnPlayerDamaged(float damage, Character attacker) { if (_active && _exitOnDamage.Value && damage > 0f) { StopCamera("You took damage", message: true); } } private void ResetView() { //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) //IL_007e: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: 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_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_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) Quaternion rotation = ((Character)_player).m_eye.rotation; _yaw = ((Quaternion)(ref rotation)).eulerAngles.y; _pitch = CameraMath.Clamp(_startPitch.Value, 50f, 20f, 75f); _fov = CameraMath.Clamp(_startFov.Value, 45f, 25f, 75f); Quaternion val = Quaternion.Euler(_pitch, _yaw, 0f); Vector3 val2 = ((Component)_player).transform.position + Quaternion.Euler(0f, _yaw, 0f) * Vector3.forward * 5f + Vector3.up; _position = val2 - val * Vector3.forward * 12f; ClampCameraPosition(); } private void MoveCamera() { //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018e: 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_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) float num = CameraMath.Clamp(Time.unscaledDeltaTime, 0f, 0f, 0.05f); float x = (ZInput.GetButton("Right") ? 1f : 0f) - (ZInput.GetButton("Left") ? 1f : 0f) + ZInput.GetJoyLeftStickX(false); float z = (ZInput.GetButton("Forward") ? 1f : 0f) - (ZInput.GetButton("Backward") ? 1f : 0f) - ZInput.GetJoyLeftStickY(true); float y = ((ZInput.GetButton("Jump") || ZInput.GetButton("JoyJump")) ? 1f : 0f) - ((ZInput.GetButton("Crouch") || ZInput.GetButton("JoyCrouch")) ? 1f : 0f); CameraMath.NormalizeInput(ref x, ref y, ref z); float num2 = CameraMath.Clamp(_speed.Value, 6f, 1f, 15f); if (ZInput.GetButton("Run") || ZInput.GetButton("JoyRun")) { num2 *= CameraMath.Clamp(_fastMultiplier.Value, 2f, 1f, 3f); } MovementVector movementVector = CameraMath.ViewRelativeMovement(_yaw, _pitch, x, y, z); float x2 = movementVector.X; float y2 = movementVector.Y; float z2 = movementVector.Z; CameraMath.NormalizeInput(ref x2, ref y2, ref z2); _position += new Vector3(x2, y2, z2) * num2 * num; ClampCameraPosition(); Vector2 val = Vector2.zero; if (ZInput.IsMouseActive()) { val = ZInput.GetMouseDelta() * (ZInput.IsGamepadMouseActive() ? PlayerController.m_switchMouseSens : PlayerController.m_mouseSens); if (PlayerController.m_invertMouse) { val.y *= -1f; } } else if (ZInput.IsGamepadActive() && !ZInput.GetButton("JoyRotate") && !ZInput.GetButton("JoyRotateRight")) { val = new Vector2(ZInput.GetJoyRightStickX(true), 0f - ZInput.GetJoyRightStickY(true)) * 110f * num * PlayerController.m_gamepadSens; if (PlayerController.m_invertCameraX) { val.x *= -1f; } if (PlayerController.m_invertCameraY) { val.y *= -1f; } } _yaw = Mathf.Repeat(_yaw + val.x, 360f); _pitch = Mathf.Clamp(_pitch - val.y, -80f, 85f); if (ZInput.GetKey((KeyCode)280, true)) { _fov -= 25f * num; } if (ZInput.GetKey((KeyCode)281, true)) { _fov += 25f * num; } _fov = Mathf.Clamp(_fov, 25f, 75f); } private void ClampCameraPosition() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_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_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) //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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) Vector3 val = _bodyOrigin + Vector3.up; Vector3 val2 = _position - val; float x = val2.x; float y = val2.y; float z = val2.z; CameraMath.ClampToSphere(ref x, ref y, ref z, CameraMath.Clamp(_radius.Value, 25f, 8f, 25f)); _position = val + new Vector3(x, y, z); } internal void ApplyPose() { //IL_002f: 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 (_active && Object.op_Implicit((Object)(object)_camera) && Object.op_Implicit((Object)(object)_gameCamera)) { ((Component)_gameCamera).transform.SetPositionAndRotation(_position, Quaternion.Euler(_pitch, _yaw, 0f)); _camera.fieldOfView = _fov; _camera.nearClipPlane = Mathf.Min(_savedNearClip, 0.1f); if (Object.op_Implicit((Object)(object)_skyCamera)) { _skyCamera.fieldOfView = _fov; } } } private void HideRenderers() { GameObject val = (Object.op_Implicit((Object)(object)_player) ? ((Character)_player).GetVisual() : null); if (!Object.op_Implicit((Object)(object)val)) { return; } Renderer[] componentsInChildren = val.GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren) { if (Object.op_Implicit((Object)(object)val2)) { if (!_renderers.ContainsKey(val2)) { _renderers.Add(val2, val2.forceRenderingOff); } val2.forceRenderingOff = true; } } } private void StopCamera(string reason, bool message) { //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) if (!_active) { return; } _active = false; _lastExit = reason; try { if (Object.op_Implicit((Object)(object)_player)) { Player player = _player; ((Character)player).m_onDamaged = (Action)Delegate.Remove(((Character)player).m_onDamaged, new Action(OnPlayerDamaged)); if (Mathf.Approximately(_player.m_maxPlaceDistance, _appliedReach)) { _player.m_maxPlaceDistance = _savedReach; } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Build reach cleanup: " + ex.Message)); } foreach (KeyValuePair renderer in _renderers) { try { if (Object.op_Implicit((Object)(object)renderer.Key)) { renderer.Key.forceRenderingOff = renderer.Value; } } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Renderer cleanup: " + ex2.Message)); } } _renderers.Clear(); try { if (Object.op_Implicit((Object)(object)_camera)) { _camera.fieldOfView = _savedFov; _camera.nearClipPlane = _savedNearClip; } if (Object.op_Implicit((Object)(object)_skyCamera)) { _skyCamera.fieldOfView = _savedSkyFov; } if (Object.op_Implicit((Object)(object)_gameCamera) && (Object)(object)_gameCamera == (Object)(object)GameCamera.instance && !GameCamera.InFreeFly()) { ((Component)_gameCamera).transform.SetPositionAndRotation(_savedCameraPosition, _savedCameraRotation); } } catch (Exception ex3) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Camera cleanup: " + ex3.Message)); } _player = null; _buildTool = null; _gameCamera = null; _camera = null; _skyCamera = null; _activationGraceUntil = 0f; _keyboardTogglePending = false; _keyboardToggleRequestFrame = -1; float suppressUntil; switch (reason) { default: suppressUntil = 0f; break; case "Toggled off": case "Escape": case "Configuration reload": suppressUntil = Time.unscaledTime + 0.2f; break; } _suppressUntil = suppressUntil; if (message) { Notify("Build camera OFF: " + reason + "."); } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Build Camera stopped: " + reason)); } internal void Fail(Exception error) { StopCamera("Stopped after an error", message: false); _failed = true; _chordHeld = false; ((BaseUnityPlugin)this).Logger.LogError((object)("Build Camera stopped and restored its local changes. " + error)); WriteDiagnostics("Error: " + error.GetType().Name + ": " + error.Message); } private static void Notify(string text) { if (Object.op_Implicit((Object)(object)MessageHud.instance)) { MessageHud.instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, true); } } private void OnGUI() { //IL_00c4: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (_active && _showIndicator.Value && !MenuBlocksCamera()) { if (_indicatorStyle == null) { _indicatorStyle = new GUIStyle(GUI.skin.box); _indicatorStyle.alignment = (TextAnchor)4; _indicatorStyle.normal.textColor = Color.white; } float num = Mathf.Clamp((float)Screen.height / 1080f, 0.75f, 2f); _indicatorStyle.fontSize = Mathf.RoundToInt(17f * num); float num2 = Mathf.Min(510f * num, (float)Screen.width * 0.65f); GUI.Box(new Rect(((float)Screen.width - num2) * 0.5f, 12f * num, num2, 31f * num), "BUILD CAMERA | " + ((object)_toggleKey.Value/*cast due to .constrained prefix*/).ToString() + " / press both stick clicks to exit", _indicatorStyle); } } private void OnDisable() { StopCamera("Plugin disabled", message: false); _chordHeld = false; _controllerChord.Reset(); } private void OnDestroy() { StopCamera("Plugin unloaded", message: false); if (_harmony != null) { _harmony.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void WriteDiagnostics(string reason) { //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) try { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Valheim Build Camera 0.1.11 - client diagnostics"); stringBuilder.AppendLine("Reason: " + reason); stringBuilder.AppendLine("No player names, server addresses, chat, item descriptions or world coordinates included."); stringBuilder.AppendLine("Unity=" + Application.unityVersion + "; screen=" + Screen.width + "x" + Screen.height); stringBuilder.AppendLine("Ready=" + _ready + "; failed=" + _failed + "; active=" + _active + "; last exit=" + _lastExit); stringBuilder.AppendLine("Game assembly=" + typeof(Player).Assembly.GetName().Version?.ToString() + "; Harmony=" + typeof(Harmony).Assembly.GetName().Version); if (Object.op_Implicit((Object)(object)_player)) { stringBuilder.AppendLine("Building tool=" + HasBuildingTool(_player) + "; grounded=" + ((Character)_player).IsOnGround() + "; menu paused=" + MenuBlocksCamera()); stringBuilder.AppendLine("Camera distance from body=" + Vector3.Distance(_position, _bodyOrigin).ToString("F2", CultureInfo.InvariantCulture)); stringBuilder.AppendLine("Pitch=" + _pitch + "; FOV=" + _fov + "; temporary reach=" + _player.m_maxPlaceDistance + "; original reach=" + _savedReach); stringBuilder.AppendLine("Tracked local renderers=" + _renderers.Count + "; collision query depth=" + CollisionQueries); } stringBuilder.AppendLine("No debug fly, god mode, no-cost mode, workbench bypass, ward bypass or network hooks installed."); File.WriteAllText(Path.Combine(Paths.ConfigPath, "lesly.valheim.buildcamera.diagnostics.txt"), stringBuilder.ToString()); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not write build camera diagnostics: " + ex.Message)); } } private static void ValidateHooks() { Require(typeof(Player), "Update", Type.EmptyTypes); Require(typeof(Player), "SetControls", new Type[12] { typeof(Vector3), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool) }); Require(typeof(Player), "SetMouseLook", new Type[1] { typeof(Vector2) }); Require(typeof(Player), "TakeInput", Type.EmptyTypes); Require(typeof(Player), "StartGuardianPower", Type.EmptyTypes); Require(typeof(GameCamera), "UpdateCamera", new Type[1] { typeof(float) }); Require(typeof(GameCamera), "LateUpdate", Type.EmptyTypes); Require(typeof(Player), "CheckPlacementGhostVSPlayers", Type.EmptyTypes); Require(typeof(Character), "GetCharactersInRange", new Type[3] { typeof(Vector3), typeof(float), typeof(List) }); } private static void Require(Type type, string name, Type[] arguments) { if (AccessTools.DeclaredMethod(type, name, arguments, (Type[])null) == null) { throw new MissingMethodException(type.FullName, name); } } } internal struct MovementVector { internal float X; internal float Y; internal float Z; internal MovementVector(float x, float y, float z) { X = x; Y = y; Z = z; } } internal static class CameraMath { internal static MovementVector ViewRelativeMovement(float yawDegrees, float pitchDegrees, float strafe, float vertical, float forward) { double num = (double)yawDegrees * Math.PI / 180.0; double num2 = (double)pitchDegrees * Math.PI / 180.0; float num3 = (float)Math.Sin(num); float num4 = (float)Math.Cos(num); float num5 = (float)Math.Cos(num2); float num6 = (float)Math.Sin(num2); float num7 = num4; float num8 = 0f - num3; float num9 = num3 * num5; float num10 = 0f - num6; float num11 = num4 * num5; return new MovementVector(num7 * strafe + num9 * forward, vertical + num10 * forward, num8 * strafe + num11 * forward); } internal static float Clamp(float value, float fallback, float min, float max) { if (!float.IsNaN(value) && !float.IsInfinity(value)) { return Math.Max(min, Math.Min(max, value)); } return fallback; } internal static void NormalizeInput(ref float x, ref float y, ref float z) { float num = (float)Math.Sqrt(x * x + y * y + z * z); if (!(num <= 1f)) { x /= num; y /= num; z /= num; } } internal static void ClampToSphere(ref float x, ref float y, ref float z, float radius) { float num = (float)Math.Sqrt(x * x + y * y + z * z); if (!(num <= radius) && !(num < 1E-05f)) { float num2 = radius / num; x *= num2; y *= num2; z *= num2; } } internal static bool ControllerToggleEligible(bool enabled, bool localPlayerPresent, bool modal, bool active, bool hasBuildingTool) { if (enabled && localPlayerPresent && !modal) { return active || hasBuildingTool; } return false; } } internal sealed class HoldToggle { private float _held; private bool _fired; internal bool Step(bool pressed, float dt) { if (!pressed) { _held = 0f; _fired = false; return false; } _held += CameraMath.Clamp(dt, 0f, 0f, 0.1f); if (_fired || _held < 0.5f) { return false; } _fired = true; return true; } internal void Reset() { _held = 0f; _fired = false; } } internal sealed class ButtonChordToggle { private float _window; private float _bothHeld; private bool _fired; internal bool Step(bool left, bool right, float dt) { dt = CameraMath.Clamp(dt, 0f, 0f, 0.1f); if (!left && !right) { Reset(); return false; } _window = Math.Min(2f, _window + dt); if (left && right) { _bothHeld = Math.Min(1f, _bothHeld + dt); if (!_fired && (_window <= 0.35f || _bothHeld >= 0.25f)) { _fired = true; return true; } } else { _bothHeld = 0f; } return false; } internal void Reset() { _window = 0f; _bothHeld = 0f; _fired = false; } }