using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Logging; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("0.0.0.0")] namespace HowToFishFlyingBoat; [BepInPlugin("chadi7bark.howtofish.flyingboat", "Skyboat Bomber", "0.5.4")] [BepInProcess("How to Fish.exe")] public sealed class FlyingBoatMod : BaseUnityPlugin { private sealed class PendingBomb { public Component Explosive; public object PlayerWhoDropped; public float DetonateAt; } private const float RiseSpeed = 12f; private const float DescendSpeed = 9f; private const float VerticalAcceleration = 28f; private const float HoverAcceleration = 18f; private const float MaximumFlightSpeed = 50f; private const float EngineAcceleration = 15f; private const float BrakingAcceleration = 20f; private const float VelocityAlignment = 3.5f; private const float ThrottleIncreasePerSecond = 0.35f; private const float ThrottleDecreasePerSecond = 0.45f; private const float MouseFollowDegreesPerSecond = 85f; private const float MaximumAimPitchDegrees = 65f; private const float RollDegreesPerSecond = 115f; private const float RollAutoLevelDegreesPerSecond = 55f; private const float MouseSensitivity = 0.11f; private const float ThirdPersonDistance = 11f; private const float ThirdPersonHeight = 4f; private const float ThirdPersonPivotHeight = 1.5f; private const float CameraPitchDownDegrees = 8f; private const int CarpetBombCount = 10; private const float CarpetBombInterval = 0.14f; private const float CarpetBombCooldown = 3f; private const float CarpetBombFuseSeconds = 3.5f; private const float CarpetBombDropDistance = 2.2f; private const float CarpetBombSideSpread = 3.2f; private const float CarpetBombDownSpeed = 8f; private Type _boatType; private Component _activeBoat; private Rigidbody _physicsRig; private bool _flightEnabled; private bool _savedUseGravity; private float _savedDrag; private float _savedAngularDrag; private RigidbodyConstraints _savedConstraints; private float _nextTypeSearchTime; private float _nextMissingBoatMessageTime; private float _throttle; private float _manualRollAngle; private float _aimYaw; private float _aimPitch; private Vector3 _mouseAimDirection; private Camera _flightCamera; private Vector3 _savedCameraPosition; private Quaternion _savedCameraRotation; private bool _cameraStateSaved; private bool _cameraPreCullHooked; private Type _itemManagerType; private Type _explosiveType; private Type _serverType; private MethodInfo _spawnNewItemMethod; private MethodInfo _activateExplosiveMethod; private MethodInfo _setItemHolderMethod; private MethodInfo _handOverItemSimulationMethod; private Component _dynamitePrefab; private bool _bombBarrageActive; private int _bombsRemaining; private float _nextBombDropTime; private float _nextBombBarrageAllowedTime; private bool _loggedForcedSpawnThisBarrage; private readonly List _pendingBombs = new List(); private void Awake() { PluginLog.Bind(((BaseUnityPlugin)this).Logger); ResolveBoatType(); ResolveBombingSystems(); PluginLog.Msg("Flying Boat v0.5.4 loaded."); PluginLog.Msg("F6 = flight + fixed third-person chase camera | mouse aim = point the bow | W/S = throttle | A/D = roll only | Space/Ctrl = rise/descend"); PluginLog.Msg("LEFT CLICK = drop a 10-dynamite carpet-bomb barrage while flight is active."); PluginLog.Msg("Flight requires the local player to be the current boat driver and stops immediately when the driver seat is left."); PluginLog.Msg("Flying and carpet bombing are authoritative in solo games and for the host. The Item Spawner force-release route is embedded; no Item Spawner dependency is used."); LogBombingRouteStatus(); } private void OnDestroy() { DisableFlight(null); _pendingBombs.Clear(); } private void Update() { try { if (_boatType == null && Time.unscaledTime >= _nextTypeSearchTime) { _nextTypeSearchTime = Time.unscaledTime + 2f; ResolveBoatType(); } if (Input.GetKeyDown((KeyCode)287)) { if (_flightEnabled) { DisableFlight("Flight disabled. Gravity restored."); } else { TryEnableFlight(); } } if (_flightEnabled && !IsActiveBoatStillValid()) { DisableFlight("Flight disabled because you left the driver seat or changed boats."); } if (_flightEnabled) { UpdateMouseAim(); if (Mouse.current != null && Mouse.current.leftButton.wasPressedThisFrame) { TryStartCarpetBombBarrage(); } UpdateCarpetBombBarrage(); } UpdatePendingBombs(); } catch (Exception ex) { PluginLog.Error("Flying Boat update error: " + ex); DisableFlight("Flight disabled after an update error."); } } private void FixedUpdate() { if (!_flightEnabled || (Object)(object)_physicsRig == (Object)null) { return; } try { ApplyFlightPhysics(); } catch (Exception ex) { PluginLog.Error("Flying Boat physics error: " + ex); DisableFlight("Flight disabled after a physics error."); } } private void LateUpdate() { if (!_flightEnabled) { return; } try { UpdateThirdPersonCamera(); } catch (Exception ex) { PluginLog.Warning("Third-person camera update failed: " + ex.GetType().Name); } } private void TryEnableFlight() { //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) if (_boatType == null) { ResolveBoatType(); if (_boatType == null) { PluginLog.Warning("The game's Boat type is not loaded yet. Enter the playable world and try F6 again."); return; } } Component val = FindLocallyDrivenBoat(); if ((Object)(object)val == (Object)null) { if (Time.unscaledTime >= _nextMissingBoatMessageTime) { _nextMissingBoatMessageTime = Time.unscaledTime + 1f; PluginLog.Warning("Flight was not enabled: you must be sitting in the driver seat, then press F6."); } return; } if (IsClientOnly(val)) { PluginLog.Warning("Flight was not enabled: this first build requires solo play or the host. A client-only boat would be corrected by the server."); return; } object obj = ReadMember(val, "HiddenPhysicsRig"); Rigidbody val2 = (Rigidbody)((obj is Rigidbody) ? obj : null); if ((Object)(object)val2 == (Object)null) { object obj2 = ReadMember(val, "k__BackingField"); val2 = (Rigidbody)((obj2 is Rigidbody) ? obj2 : null); } if ((Object)(object)val2 == (Object)null) { PluginLog.Warning("The current boat's HiddenPhysicsRig was not found."); return; } _activeBoat = val; _physicsRig = val2; _savedUseGravity = _physicsRig.useGravity; _savedDrag = _physicsRig.drag; _savedAngularDrag = _physicsRig.angularDrag; _savedConstraints = _physicsRig.constraints; _physicsRig.useGravity = false; _physicsRig.drag = 0.2f; _physicsRig.angularDrag = 2.5f; float num = Mathf.Max(0f, Vector3.Dot(_physicsRig.velocity, ((Component)_physicsRig).transform.forward)); _throttle = Mathf.Clamp01(num / 50f); _manualRollAngle = 0f; _flightEnabled = true; BeginThirdPersonCamera(); PluginLog.Msg("FLIGHT ENABLED: fixed third-person chase view active. Mouse aims the bow; W/S change throttle; A/D roll only."); } private void DisableFlight(string message) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_physicsRig != (Object)null) { try { _physicsRig.useGravity = _savedUseGravity; _physicsRig.drag = _savedDrag; _physicsRig.angularDrag = _savedAngularDrag; _physicsRig.constraints = _savedConstraints; } catch { } } bool flightEnabled = _flightEnabled; EndThirdPersonCamera(); _flightEnabled = false; _activeBoat = null; _physicsRig = null; _throttle = 0f; _manualRollAngle = 0f; _bombBarrageActive = false; _bombsRemaining = 0; if (flightEnabled && !string.IsNullOrEmpty(message)) { PluginLog.Msg(message); } } private void ApplyFlightPhysics() { //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(Time.fixedDeltaTime, 0.001f); if (Input.GetKey((KeyCode)119) || Input.GetKey((KeyCode)273)) { _throttle = Mathf.Clamp01(_throttle + 0.35f * num); } if (Input.GetKey((KeyCode)115) || Input.GetKey((KeyCode)274)) { _throttle = Mathf.Clamp01(_throttle - 0.45f * num); } float num2 = 0f; if (Input.GetKey((KeyCode)97) || Input.GetKey((KeyCode)276)) { num2 -= 1f; } if (Input.GetKey((KeyCode)100) || Input.GetKey((KeyCode)275)) { num2 += 1f; } if (Mathf.Abs(num2) > 0.01f) { _manualRollAngle -= num2 * 115f * num; _manualRollAngle = Mathf.Repeat(_manualRollAngle + 180f, 360f) - 180f; } else { _manualRollAngle = Mathf.MoveTowardsAngle(_manualRollAngle, 0f, 55f * num); } Vector3 mouseAimDirection = GetMouseAimDirection(); Quaternion val = Quaternion.LookRotation(mouseAimDirection, Vector3.up); Quaternion val2 = Quaternion.AngleAxis(_manualRollAngle, Vector3.forward); Quaternion val3 = val * val2; Quaternion val4 = Quaternion.RotateTowards(_physicsRig.rotation, val3, 85f * num); _physicsRig.MoveRotation(val4); Vector3 val5 = val4 * Vector3.forward; Vector3 val6 = _physicsRig.velocity; float num3 = Vector3.Dot(val6, val5); float num4 = _throttle * 50f; float num5 = ((num4 >= num3) ? (15f * num) : (20f * num)); float num6 = Mathf.MoveTowards(num3, num4, num5); Vector3 val7 = val6 - val5 * num3; val7 = Vector3.Lerp(val7, Vector3.zero, Mathf.Clamp01(3.5f * num)); val6 = val5 * num6 + val7; float num7 = 0f; bool key = Input.GetKey((KeyCode)32); bool flag = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305); if (key && !flag) { num7 = 12f; } else if (flag && !key) { num7 = -9f; } if (key || flag) { float num8 = 28f * num; val6.y = Mathf.MoveTowards(val6.y, num7, num8); } else if (_throttle < 0.02f) { val6.y = Mathf.MoveTowards(val6.y, 0f, 18f * num); } if (((Vector3)(ref val6)).magnitude > 55f) { val6 = ((Vector3)(ref val6)).normalized * 55f; } _physicsRig.velocity = val6; _physicsRig.angularVelocity = Vector3.zero; } private Vector3 GetMouseAimDirection() { //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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0104: 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_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: 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_0120: 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_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) Vector3 val = _mouseAimDirection; if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = (((Object)(object)_physicsRig != (Object)null) ? ((Component)_physicsRig).transform.forward : Vector3.forward); } if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); float num = Mathf.Sin((float)Math.PI * 13f / 36f); float num2 = Mathf.Clamp(val.y, 0f - num, num); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(val.x, 0f, val.z); if (((Vector3)(ref val2)).sqrMagnitude < 0.001f) { val2 = (Vector3)(((Object)(object)_physicsRig != (Object)null) ? new Vector3(((Component)_physicsRig).transform.forward.x, 0f, ((Component)_physicsRig).transform.forward.z) : Vector3.forward); } ((Vector3)(ref val2)).Normalize(); float num3 = Mathf.Sqrt(Mathf.Max(0f, 1f - num2 * num2)); Vector3 val3 = val2 * num3 + Vector3.up * num2; return ((Vector3)(ref val3)).normalized; } private void BeginThirdPersonCamera() { //IL_003b: 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) HookCameraPreCull(); if (!TryAcquireFlightCamera()) { PluginLog.Warning("Flight enabled, but no active game camera was available for third-person view."); InitializeAimFromDirection(((Component)_physicsRig).transform.forward); } else { InitializeAimFromDirection(((Component)_physicsRig).transform.forward); UpdateThirdPersonCamera(); PluginLog.Msg("Fixed third-person chase camera enabled."); } } private void EndThirdPersonCamera() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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_003e: Unknown result type (might be due to invalid IL or missing references) UnhookCameraPreCull(); if ((Object)(object)_flightCamera != (Object)null && _cameraStateSaved) { try { ((Component)_flightCamera).transform.position = _savedCameraPosition; ((Component)_flightCamera).transform.rotation = _savedCameraRotation; } catch { } } _flightCamera = null; _cameraStateSaved = false; _mouseAimDirection = Vector3.zero; } private void InitializeAimFromDirection(Vector3 direction) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (((Vector3)(ref direction)).sqrMagnitude < 0.001f) { direction = Vector3.forward; } ((Vector3)(ref direction)).Normalize(); _aimYaw = Mathf.Atan2(direction.x, direction.z) * 57.29578f; _aimPitch = (0f - Mathf.Asin(Mathf.Clamp(direction.y, -1f, 1f))) * 57.29578f; _aimPitch = Mathf.Clamp(_aimPitch, -65f, 65f); RefreshAimDirection(); } private void UpdateMouseAim() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_001c: Unknown result type (might be due to invalid IL or missing references) Vector2 val = Vector2.zero; try { if (Mouse.current != null) { val = ((InputControl)(object)((Pointer)Mouse.current).delta).ReadValue(); } } catch { } _aimYaw += val.x * 0.11f; _aimPitch -= val.y * 0.11f; _aimPitch = Mathf.Clamp(_aimPitch, -65f, 65f); _aimYaw = Mathf.Repeat(_aimYaw + 180f, 360f) - 180f; RefreshAimDirection(); } private void RefreshAimDirection() { //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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) _mouseAimDirection = Quaternion.Euler(_aimPitch, _aimYaw, 0f) * Vector3.forward; if (((Vector3)(ref _mouseAimDirection)).sqrMagnitude < 0.001f) { _mouseAimDirection = Vector3.forward; } ((Vector3)(ref _mouseAimDirection)).Normalize(); } private void UpdateThirdPersonCamera() { if ((!((Object)(object)_flightCamera == (Object)null) || TryAcquireFlightCamera()) && !((Object)(object)_physicsRig == (Object)null)) { ForceThirdPersonCameraTransform(); } } private void ForceThirdPersonCameraTransform() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: 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_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_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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: 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_0111: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_flightCamera == (Object)null) && !((Object)(object)_physicsRig == (Object)null)) { Vector3 val = _physicsRig.rotation * Vector3.forward; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { ((Vector3)(ref val))..ctor(_mouseAimDirection.x, 0f, _mouseAimDirection.z); } if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); Vector3 val2 = _physicsRig.worldCenterOfMass + Vector3.up * 1.5f; Vector3 position = val2 - val * 11f + Vector3.up * 4f; Quaternion rotation = Quaternion.LookRotation(val, Vector3.up) * Quaternion.Euler(8f, 0f, 0f); ((Component)_flightCamera).transform.position = position; ((Component)_flightCamera).transform.rotation = rotation; } } private void HookCameraPreCull() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown if (!_cameraPreCullHooked) { Camera.onPreCull = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPreCull, (Delegate?)new CameraCallback(OnFlightCameraPreCull)); _cameraPreCullHooked = true; } } private void UnhookCameraPreCull() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown if (_cameraPreCullHooked) { Camera.onPreCull = (CameraCallback)Delegate.Remove((Delegate?)(object)Camera.onPreCull, (Delegate?)new CameraCallback(OnFlightCameraPreCull)); _cameraPreCullHooked = false; } } private void OnFlightCameraPreCull(Camera renderingCamera) { if (_flightEnabled && !((Object)(object)_physicsRig == (Object)null) && !((Object)(object)renderingCamera == (Object)null) && (!((Object)(object)_flightCamera == (Object)null) || TryAcquireFlightCamera()) && (Object)(object)renderingCamera == (Object)(object)_flightCamera) { ForceThirdPersonCameraTransform(); } } private void TryStartCarpetBombBarrage() { if (_flightEnabled && !((Object)(object)_activeBoat == (Object)null) && !((Object)(object)_physicsRig == (Object)null) && !_bombBarrageActive && !(Time.unscaledTime < _nextBombBarrageAllowedTime) && IsLocalPlayerTheDriver(_activeBoat)) { if (!ResolveBombingSystems() || !ResolveDynamitePrefab()) { PluginLog.Warning("Carpet bombing is unavailable: the native Dynamite spawn systems were not found."); return; } _bombBarrageActive = true; _bombsRemaining = 10; _nextBombDropTime = Time.unscaledTime; _nextBombBarrageAllowedTime = Time.unscaledTime + 3f; _loggedForcedSpawnThisBarrage = false; PluginLog.Msg("CARPET BOMBING: force-spawning 10 dynamites 2.2 metres beneath the boat."); } } private void UpdateCarpetBombBarrage() { if (!_bombBarrageActive) { return; } if (!_flightEnabled || (Object)(object)_activeBoat == (Object)null || (Object)(object)_physicsRig == (Object)null || !IsLocalPlayerTheDriver(_activeBoat)) { _bombBarrageActive = false; _bombsRemaining = 0; } else if (!(Time.unscaledTime < _nextBombDropTime)) { DropOneCarpetBomb(); _bombsRemaining--; if (_bombsRemaining <= 0) { _bombBarrageActive = false; PluginLog.Msg("Carpet-bomb barrage released."); } else { _nextBombDropTime = Time.unscaledTime + 0.14f; } } } private void DropOneCarpetBomb() { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_011e: 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_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) Component val = FindLiveRuntimeComponent(_itemManagerType, "Server(Clone)"); if ((Object)(object)val == (Object)null || _spawnNewItemMethod == null || (Object)(object)_dynamitePrefab == (Object)null) { ResolveBombingSystems(); ResolveDynamitePrefab(); val = FindLiveRuntimeComponent(_itemManagerType, "Server(Clone)"); } if ((Object)(object)val == (Object)null || _spawnNewItemMethod == null || (Object)(object)_dynamitePrefab == (Object)null) { PluginLog.Warning("A carpet bomb could not spawn because ItemManager was unavailable."); _bombBarrageActive = false; return; } Vector3 forward = ((Component)_physicsRig).transform.forward; Vector3 right = ((Component)_physicsRig).transform.right; float num = Random.Range(-3.2f, 3.2f); Vector3 val2 = _physicsRig.worldCenterOfMass - Vector3.up * 2.2f + right * num - forward * 0.75f; Quaternion rotation = Random.rotation; object obj; try { obj = _spawnNewItemMethod.Invoke(val, new object[3] { _dynamitePrefab, val2, rotation }); } catch (Exception ex) { PluginLog.Warning("Native Dynamite spawn failed: " + ex.GetType().Name); return; } Component val3 = (Component)((obj is Component) ? obj : null); if ((Object)(object)val3 == (Object)null) { PluginLog.Warning("Native Dynamite spawn returned an object that was not an Item component."); return; } ForceReleaseBombToWorld(val3); val3.transform.position = val2; val3.transform.rotation = rotation; object obj2 = ReadMember(val3, "Rig"); Rigidbody val4 = (Rigidbody)((obj2 is Rigidbody) ? obj2 : null); if ((Object)(object)val4 == (Object)null) { val4 = val3.GetComponent(); } if ((Object)(object)val4 != (Object)null) { val4.velocity = _physicsRig.velocity + Vector3.down * 8f + right * Random.Range(-1.5f, 1.5f); val4.angularVelocity = Random.onUnitSphere * Random.Range(3f, 8f); } Component val5 = null; if (_explosiveType != null && _explosiveType.IsInstanceOfType(val3)) { val5 = val3; } if ((Object)(object)val5 == (Object)null) { object obj3 = ReadMember(val3, "Explosive"); val5 = (Component)((obj3 is Component) ? obj3 : null); } if ((Object)(object)val5 == (Object)null && _explosiveType != null) { val5 = val3.GetComponent(_explosiveType); } if ((Object)(object)val5 != (Object)null) { PendingBomb pendingBomb = new PendingBomb(); pendingBomb.Explosive = val5; pendingBomb.PlayerWhoDropped = ReadMember(_activeBoat, "LocalPlayerOnBoat"); pendingBomb.DetonateAt = Time.time + 3.5f; _pendingBombs.Add(pendingBomb); } } private void UpdatePendingBombs() { for (int num = _pendingBombs.Count - 1; num >= 0; num--) { PendingBomb pendingBomb = _pendingBombs[num]; if (pendingBomb == null || (Object)(object)pendingBomb.Explosive == (Object)null) { _pendingBombs.RemoveAt(num); } else if (!(Time.time < pendingBomb.DetonateAt)) { DetonateBomb(pendingBomb.Explosive, pendingBomb.PlayerWhoDropped); _pendingBombs.RemoveAt(num); } } } private void DetonateBomb(Component explosive, object playerWhoDropped) { if ((Object)(object)explosive == (Object)null) { return; } Component val = FindLiveRuntimeComponent(_serverType, "Server(Clone)"); if ((Object)(object)val == (Object)null || _activateExplosiveMethod == null) { ResolveBombingSystems(); val = FindLiveRuntimeComponent(_serverType, "Server(Clone)"); } if ((Object)(object)val == (Object)null || _activateExplosiveMethod == null) { return; } uint num = ReadNetworkTick(val); try { _activateExplosiveMethod.Invoke(val, new object[5] { explosive, num, true, true, playerWhoDropped }); } catch (Exception ex) { PluginLog.Warning("Dynamite detonation failed: " + ex.GetType().Name); } } private bool ResolveBombingSystems() { if (_itemManagerType == null) { _itemManagerType = FindRuntimeType("ItemManager"); } if (_explosiveType == null) { _explosiveType = FindRuntimeType("Explosive"); } if (_serverType == null) { _serverType = FindRuntimeType("Server"); } if (_itemManagerType != null && _spawnNewItemMethod == null) { _spawnNewItemMethod = FindMethodRecursive(_itemManagerType, "SpawnNewItem", 3); } if (_serverType != null && _activateExplosiveMethod == null) { _activateExplosiveMethod = FindMethodRecursive(_serverType, "ActivateExplosive", 5); } if (_serverType != null && _setItemHolderMethod == null) { _setItemHolderMethod = FindMethodRecursive(_serverType, "SetItemHolder", 3); } if (_serverType != null && _handOverItemSimulationMethod == null) { _handOverItemSimulationMethod = FindMethodRecursive(_serverType, "HandOverItemSimulation", 1); } if (_itemManagerType != null && _explosiveType != null && _serverType != null && _spawnNewItemMethod != null && _activateExplosiveMethod != null && _setItemHolderMethod != null) { return _handOverItemSimulationMethod != null; } return false; } private void LogBombingRouteStatus() { PluginLog.Msg("FORCED-SPAWN ROUTE: SpawnNewItem=" + FoundText(_spawnNewItemMethod) + " | SetItemHolder=" + FoundText(_setItemHolderMethod) + " | HandOverItemSimulation=" + FoundText(_handOverItemSimulationMethod) + " | ActivateExplosive=" + FoundText(_activateExplosiveMethod)); } private static string FoundText(MethodInfo method) { if (!(method == null)) { return "FOUND"; } return "NOT FOUND"; } private void ForceReleaseBombToWorld(Component spawnedItem) { if ((Object)(object)spawnedItem == (Object)null) { return; } Component val = FindLiveRuntimeComponent(_serverType, "Server(Clone)"); if ((Object)(object)val == (Object)null || _setItemHolderMethod == null || _handOverItemSimulationMethod == null) { ResolveBombingSystems(); val = FindLiveRuntimeComponent(_serverType, "Server(Clone)"); } if ((Object)(object)val == (Object)null || _setItemHolderMethod == null || _handOverItemSimulationMethod == null) { PluginLog.Warning("FORCED-SPAWN FAIL: the live Server release route was unavailable after SpawnNewItem."); return; } try { _setItemHolderMethod.Invoke(val, new object[3] { spawnedItem, null, null }); _handOverItemSimulationMethod.Invoke(val, new object[1] { spawnedItem }); if (!_loggedForcedSpawnThisBarrage) { _loggedForcedSpawnThisBarrage = true; PluginLog.Msg("FORCED-SPAWN PASS: Server.SetItemHolder(item, null, null) + Server.HandOverItemSimulation(item)."); } } catch (TargetInvocationException ex) { Exception ex2 = ex.InnerException ?? ex; PluginLog.Warning("FORCED-SPAWN FAIL: " + ex2.GetType().Name + ": " + ex2.Message); } catch (Exception ex3) { PluginLog.Warning("FORCED-SPAWN FAIL: " + ex3.GetType().Name + ": " + ex3.Message); } } private static Component FindLiveRuntimeComponent(Type type, string preferredPathText) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) if (type == null) { return null; } object obj = ReadStaticMember(type, "Instance"); Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val != (Object)null) { return val; } Object[] array; try { array = Resources.FindObjectsOfTypeAll(type); } catch { return null; } Component val2 = null; int num = int.MinValue; foreach (Object obj3 in array) { Component val3 = (Component)(object)((obj3 is Component) ? obj3 : null); if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3.gameObject == (Object)null)) { int num2 = 0; if (val3.gameObject.activeInHierarchy) { num2 += 100; } Scene scene = val3.gameObject.scene; if (!string.IsNullOrEmpty(((Scene)(ref scene)).name)) { num2 += 50; } string hierarchyPath = GetHierarchyPath(val3.transform); if (!string.IsNullOrEmpty(preferredPathText) && hierarchyPath.IndexOf(preferredPathText, StringComparison.OrdinalIgnoreCase) >= 0) { num2 += 500; } if (hierarchyPath.IndexOf("Backup", StringComparison.OrdinalIgnoreCase) >= 0) { num2 -= 500; } if ((Object)(object)val2 == (Object)null || num2 > num) { val2 = val3; num = num2; } } } return val2; } private static string GetHierarchyPath(Transform transform) { if ((Object)(object)transform == (Object)null) { return string.Empty; } string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } private bool ResolveDynamitePrefab() { //IL_0074: 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) if ((Object)(object)_dynamitePrefab != (Object)null) { return true; } if (_explosiveType == null && !ResolveBombingSystems()) { return false; } Object[] array; try { array = Resources.FindObjectsOfTypeAll(_explosiveType); } catch { return false; } Component dynamitePrefab = null; foreach (Object obj2 in array) { Component val = (Component)(object)((obj2 is Component) ? obj2 : null); if (!((Object)(object)val == (Object)null) && string.Equals(((Object)val.gameObject).name, "Dynamite", StringComparison.OrdinalIgnoreCase)) { dynamitePrefab = val; Scene scene = val.gameObject.scene; if (string.IsNullOrEmpty(((Scene)(ref scene)).name)) { _dynamitePrefab = val; return true; } } } _dynamitePrefab = dynamitePrefab; return (Object)(object)_dynamitePrefab != (Object)null; } private static uint ReadNetworkTick(Component server) { try { object target = ReadMember(server, "TimeManager"); object obj = ReadMember(target, "Tick"); if (obj != null) { return Convert.ToUInt32(obj); } } catch { } return 0u; } private bool IsActiveBoatStillValid() { if ((Object)(object)_activeBoat == (Object)null || (Object)(object)_physicsRig == (Object)null) { return false; } try { return _activeBoat.gameObject.activeInHierarchy && IsLocalPlayerTheDriver(_activeBoat); } catch { return false; } } private Component FindLocallyDrivenBoat() { //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) if (_boatType == null) { return null; } Object[] array; try { array = Resources.FindObjectsOfTypeAll(_boatType); } catch { return null; } foreach (Object obj2 in array) { Component val = (Component)(object)((obj2 is Component) ? obj2 : null); if ((Object)(object)val == (Object)null) { continue; } try { if (val.gameObject.activeInHierarchy) { Scene scene = val.gameObject.scene; if (!string.IsNullOrEmpty(((Scene)(ref scene)).name) && IsLocalPlayerTheDriver(val)) { return val; } } } catch { } } return null; } private bool IsLocalPlayerTheDriver(Component boat) { if ((Object)(object)boat == (Object)null || _boatType == null) { return false; } if (!TryReadStaticBoolean(_boatType, "IsDrivingLocally", out var value) || !value) { return false; } object obj = ReadMember(boat, "LocalPlayerOnBoat"); if (obj == null) { obj = ReadMember(boat, "k__BackingField"); } object obj2 = ReadMember(boat, "Driver"); if (obj == null || obj2 == null) { return false; } Object val = (Object)((obj is Object) ? obj : null); Object val2 = (Object)((obj2 is Object) ? obj2 : null); if (val != (Object)null && val2 != (Object)null) { return val == val2; } return object.ReferenceEquals(obj, obj2); } private bool TryAcquireFlightCamera() { //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_0042: 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) Camera val = FindActiveGameCamera(); if ((Object)(object)val == (Object)null) { return false; } _flightCamera = val; if (!_cameraStateSaved) { _savedCameraPosition = ((Component)_flightCamera).transform.position; _savedCameraRotation = ((Component)_flightCamera).transform.rotation; _cameraStateSaved = true; } return true; } private static Camera FindActiveGameCamera() { Camera main = Camera.main; if (IsUsableCamera(main)) { return main; } Object[] array; try { array = Resources.FindObjectsOfTypeAll(typeof(Camera)); } catch { return null; } Camera val = null; int num = int.MinValue; foreach (Object obj2 in array) { Camera val2 = (Camera)(object)((obj2 is Camera) ? obj2 : null); if (IsUsableCamera(val2)) { int num2 = ScoreGameCamera(val2); if ((Object)(object)val == (Object)null || num2 > num) { val = val2; num = num2; } } } return val; } private static bool IsUsableCamera(Camera camera) { //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) if ((Object)(object)camera == (Object)null) { return false; } try { int result; if (((Behaviour)camera).enabled && ((Component)camera).gameObject.activeInHierarchy) { Scene scene = ((Component)camera).gameObject.scene; if (!string.IsNullOrEmpty(((Scene)(ref scene)).name)) { result = (((Object)(object)camera.targetTexture == (Object)null) ? 1 : 0); goto IL_0049; } } result = 0; goto IL_0049; IL_0049: return (byte)result != 0; } catch { return false; } } private static int ScoreGameCamera(Camera camera) { int num = 0; if (!camera.orthographic) { num += 50; } if ((Object)(object)((Component)camera).GetComponent() != (Object)null) { num += 100; } string text = ((Object)((Component)camera).gameObject).name.ToLowerInvariant(); if (text.IndexOf("player") >= 0) { num += 40; } if (text.IndexOf("main") >= 0) { num += 30; } if (text.IndexOf("game") >= 0) { num += 15; } if (text.IndexOf("ui") >= 0) { num -= 100; } if (text.IndexOf("menu") >= 0) { num -= 100; } if (text.IndexOf("endgame") >= 0) { num -= 100; } return num; } private static bool IsClientOnly(Component boat) { bool value; bool flag = TryReadBoolean(boat, "IsServerStarted", out value); bool value2; bool flag2 = TryReadBoolean(boat, "IsClientStarted", out value2); if (flag && flag2 && value2) { return !value; } return false; } private static Type FindRuntimeType(string fullName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { try { Type type = assemblies[i].GetType(fullName, throwOnError: false); if (type != null) { return type; } } catch { } } return null; } private static MethodInfo FindMethodRecursive(Type type, string name, int parameterCount) { Type type2 = type; while (type2 != null) { try { MethodInfo[] methods = type2.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == name && methodInfo.GetParameters().Length == parameterCount) { return methodInfo; } } } catch { } type2 = type2.BaseType; } return null; } private void ResolveBoatType() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = null; try { type = assemblies[i].GetType("Boat", throwOnError: false); } catch { } if (type != null && typeof(Component).IsAssignableFrom(type)) { _boatType = type; PluginLog.Msg("Resolved the runtime Boat controller from " + assemblies[i].GetName().Name + "."); break; } } } private static object ReadMember(object target, string name) { if (target == null) { return null; } Type type = target.GetType(); PropertyInfo propertyInfo = FindPropertyRecursive(type, name); if (propertyInfo != null && propertyInfo.GetIndexParameters().Length == 0) { try { return propertyInfo.GetValue(target, null); } catch { } } FieldInfo fieldInfo = FindFieldRecursive(type, name); if (fieldInfo != null) { try { return fieldInfo.GetValue(target); } catch { } } return null; } private static object ReadStaticMember(Type type, string name) { if (type == null) { return null; } PropertyInfo propertyInfo = FindPropertyRecursive(type, name); if (propertyInfo != null && propertyInfo.GetIndexParameters().Length == 0) { try { return propertyInfo.GetValue(null, null); } catch { } } FieldInfo fieldInfo = FindFieldRecursive(type, name); if (fieldInfo != null && fieldInfo.IsStatic) { try { return fieldInfo.GetValue(null); } catch { } } return null; } private static bool TryReadBoolean(object target, string name, out bool value) { value = false; object obj = ReadMember(target, name); if (obj is bool) { value = (bool)obj; return true; } return false; } private static bool TryReadStaticBoolean(Type type, string name, out bool value) { value = false; PropertyInfo propertyInfo = FindPropertyRecursive(type, name); if (propertyInfo != null) { try { object value2 = propertyInfo.GetValue(null, null); if (value2 is bool) { value = (bool)value2; return true; } } catch { } } FieldInfo fieldInfo = FindFieldRecursive(type, name); if (fieldInfo != null && fieldInfo.IsStatic) { try { object value3 = fieldInfo.GetValue(null); if (value3 is bool) { value = (bool)value3; return true; } } catch { } } return false; } private static FieldInfo FindFieldRecursive(Type type, string name) { Type type2 = type; while (type2 != null) { try { FieldInfo field = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field; } } catch { } type2 = type2.BaseType; } return null; } private static PropertyInfo FindPropertyRecursive(Type type, string name) { Type type2 = type; while (type2 != null) { try { PropertyInfo property = type2.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property; } } catch { } type2 = type2.BaseType; } return null; } } internal static class PluginLog { private static ManualLogSource _source; internal static void Bind(ManualLogSource source) { _source = source; } internal static void Msg(object value) { if (_source != null) { _source.LogInfo(value); } } internal static void Warning(object value) { if (_source != null) { _source.LogWarning(value); } } internal static void Error(object value) { if (_source != null) { _source.LogError(value); } } }