Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CrewRow v0.1.5
CrewRow.dll
Decompiled a month agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using CrewRow.Config; using CrewRow.Core; using CrewRow.Features; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: AssemblyCompany("valheimcrew")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Los tripulantes de un barco reman junto al timonel para ganar velocidad con viento en contra.")] [assembly: AssemblyFileVersion("0.1.5.0")] [assembly: AssemblyInformationalVersion("0.1.5+32c3d8c668d6b2ff2442afbf04b3f12bfceded99")] [assembly: AssemblyProduct("CrewRow")] [assembly: AssemblyTitle("CrewRow")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.5.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CrewRow { [BepInPlugin("com.valheimcrew.crewrow", "CrewRow", "0.1.5")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.valheimcrew.crewrow"; public const string PluginName = "CrewRow"; public const string PluginVersion = "0.1.5"; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; PluginConfig.Bind(((BaseUnityPlugin)this).Config); _harmony = new Harmony("com.valheimcrew.crewrow"); ApplyPatches(); WarnAboutConflicts(); Log.LogInfo((object)"CrewRow 0.1.5 cargado."); } private void ApplyPatches() { PatchGroup("aviso de remada", typeof(Ship_Start_Patch)); PatchGroup("empuje de la tripulacion", typeof(Ship_CustomFixedUpdate_Patch)); PatchGroup("ciclo de vida", typeof(Game_Logout_Patch)); } private void PatchGroup(string feature, params Type[] patchTypes) { foreach (Type type in patchTypes) { try { _harmony.PatchAll(type); } catch (Exception ex) { Log.LogError((object)("No se pudo enganchar '" + feature + "' (" + type.Name + "). Esa parte del mod queda desactivada; probablemente el juego cambio en un parche. Detalle: " + ex.Message)); } } } private void Update() { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { SafeTick(RowerClient.Tick, "el aviso de remada"); SafeTick(SpeedReadout.Tick, "el indicador de velocidad"); } } private static void SafeTick(Action tick, string what) { try { tick(); } catch (Exception arg) { Log.LogError((object)$"Fallo en {what}: {arg}"); } } internal static bool IsTyping() { if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return true; } if (!Console.IsVisible() && !TextInput.IsVisible()) { return StoreGui.IsVisible(); } return true; } private void OnDestroy() { RowBoost.Restore(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private static void WarnAboutConflicts() { string[] marcas = new string[7] { "advancedboating", "rowing", "fasterboats", "hexrowfaster", "boatspeed", "sailing", "shipspeed" }; string[] array = Chainloader.PluginInfos.Keys.Where((string guid) => guid != "com.valheimcrew.crewrow" && marcas.Any((string m) => guid.IndexOf(m, StringComparison.OrdinalIgnoreCase) >= 0)).ToArray(); if (array.Length != 0) { Log.LogWarning((object)("Hay otros mods cargados que tambien tocan la velocidad de los barcos: " + string.Join(", ", array) + ". Conviene dejar solo uno para que las fuerzas no se multipliquen entre si.")); } } } } namespace CrewRow.Features { internal static class RowBoost { private static Ship _boosted; private static float _original; private static float _applied; private static Ship _tickShip; private static int _rowersThisTick; private static bool _disabled; private static bool _disabledLogged; public static void Restore() { if ((Object)(object)_boosted == (Object)null) { _boosted = null; return; } if (_boosted.m_backwardForce == _applied) { _boosted.m_backwardForce = _original; } _boosted = null; } private static void Abort() { Restore(); _tickShip = null; _rowersThisTick = 0; } public static void Begin(Ship ship) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Invalid comparison between Unknown and I4 //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Invalid comparison between Unknown and I4 Restore(); _tickShip = null; _rowersThisTick = 0; if (_disabled || !PluginConfig.Enabled.Value || (Object)(object)ship == (Object)null) { return; } ZNetView nview = ship.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return; } _tickShip = ship; _rowersThisTick = RowerRegistry.CountActive(ship); Notify(ship, _rowersThisTick); if (_rowersThisTick <= 0) { return; } Speed speed = ship.m_speed; if ((int)speed == 2 || ((int)speed == 1 && PluginConfig.ApplyInReverse.Value)) { float num = Bonus(_rowersThisTick); if (!(num <= 0f)) { _original = ship.m_backwardForce; _applied = _original * (1f + num); ship.m_backwardForce = _applied; _boosted = ship; } } } public static void End(Ship ship, float fixedDeltaTime) { Restore(); bool flag = _tickShip != null && _tickShip == ship; int rowersThisTick = _rowersThisTick; _tickShip = null; _rowersThisTick = 0; if (!_disabled && flag && rowersThisTick > 0 && !((Object)(object)ship == (Object)null) && PluginConfig.Scope.Value != AlcanceDelEmpuje.SoloRemo) { ApplySailAssist(ship, fixedDeltaTime, rowersThisTick); } } private static void ApplySailAssist(Ship ship, float fixedDeltaTime, int rowers) { //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) Speed speed = ship.m_speed; if (((int)speed != 3 && (int)speed != 4) || ship.m_lastUpdateWaterForceTime != Time.time) { return; } Rigidbody body = ship.m_body; if (!((Object)(object)body == (Object)null)) { float num = Bonus(rowers); if (PluginConfig.Scope.Value == AlcanceDelEmpuje.RemoYVelaEnContra) { num *= 1f - ship.GetWindAngleFactor(); } if (!(num <= 0.0001f)) { Vector3 forward = ((Component)ship).transform.forward; Vector3 val = forward * (ship.m_backwardForce * num * (1f - Mathf.Abs(ship.m_rudderValue))); Vector3 val2 = ((Component)ship).transform.position + forward * ship.m_stearForceOffset; body.AddForceAtPosition(val * (body.mass * fixedDeltaTime), val2, (ForceMode)1); } } } private static float Bonus(int rowers) { float num = (float)rowers * PluginConfig.BonusPerRower.Value; float value = PluginConfig.MaxBonus.Value; if (!(num < value)) { return value; } return num; } private static void Notify(Ship ship, int rowers) { if (!RowerRegistry.CountChanged(ship, rowers)) { return; } RowLog.Count(ship, rowers, 1f + Bonus(rowers)); if (!PluginConfig.ShowNotices.Value || (Object)(object)ship.m_shipControlls == (Object)null) { return; } long user = ship.m_shipControlls.GetUser(); if (user == 0L) { return; } Player player = Player.GetPlayer(user); if (!((Object)(object)player == (Object)null)) { ZNetView nview = ((Character)player).m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.HasOwner()) { ((Character)player).Message((MessageType)1, (rowers > 0) ? ("Remando: " + rowers) : "Nadie esta remando", 0, (Sprite)null, false); } } } internal static void DisableAfterFailure(string what, Exception e) { Abort(); _disabled = true; if (!_disabledLogged) { _disabledLogged = true; Plugin.Log.LogError((object)(what + ". El empuje de la tripulacion queda desactivado hasta reiniciar el juego; los barcos siguen navegando normal. Detalle: " + e)); } } } [HarmonyPatch(typeof(Ship), "CustomFixedUpdate")] internal static class Ship_CustomFixedUpdate_Patch { private static void Prefix(Ship __instance) { try { RowBoost.Begin(__instance); } catch (Exception e) { RowBoost.DisableAfterFailure("Fallo al preparar el empuje de la tripulacion", e); } } private static void Postfix(Ship __instance, float fixedDeltaTime) { try { RowBoost.End(__instance, fixedDeltaTime); } catch (Exception e) { RowBoost.DisableAfterFailure("Fallo al aplicar el empuje de la tripulacion", e); } } private static Exception Finalizer(Exception __exception) { RowBoost.Restore(); return __exception; } } internal static class RowerClient { private const float StaminaChargeInterval = 0.5f; private const float ResumeStaminaFraction = 0.25f; private const float MinSendInterval = 0.1f; private const float MoveThreshold = 0.1f; private static bool _rowing; private static bool _exhausted; private static bool _lastSent; private static float _nextSend; private static float _lastSend; private static long _lastOwner; private static Ship _lastShip; private static float _staminaTimer; public static void Tick() { Player localPlayer = Player.m_localPlayer; Ship val = (((Object)(object)localPlayer == (Object)null || !PluginConfig.Enabled.Value) ? null : Ship.GetLocalShip()); if (val != _lastShip) { SendStopTo(_lastShip, localPlayer); _lastShip = val; _lastOwner = 0L; _rowing = false; _exhausted = false; _lastSent = false; _staminaTimer = 0f; } if ((Object)(object)localPlayer == (Object)null || (Object)(object)val == (Object)null) { SetRowing(val, rowing: false, "el barco ya no esta"); return; } long playerID = localPlayer.GetPlayerID(); if (CanRow(val, localPlayer, playerID, out var reason)) { ReadHotkey(val); } else if (_rowing) { SetRowing(val, rowing: false, reason); } else { ExplainIfTried(localPlayer, reason); } if (_rowing) { UpdateStamina(localPlayer); } Publish(val, playerID); } private static bool IsRowingNow() { if (_rowing) { return !_exhausted; } return false; } public static void Reset() { _rowing = false; _exhausted = false; _lastSent = false; _nextSend = 0f; _lastSend = 0f; _lastOwner = 0L; _lastShip = null; _staminaTimer = 0f; } private static bool CanRow(Ship ship, Player me, long myID, out string reason) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) reason = null; ZNetView nview = ship.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { reason = "el barco no responde"; return false; } if ((Object)(object)ship.m_shipControlls != (Object)null && ship.m_shipControlls.GetUser() == myID) { reason = "estas en el timon"; return false; } if (!SpeedCounts(ship.GetSpeedSetting())) { reason = "el barco no esta remando"; return false; } if (PluginConfig.CancelOnMove.Value && ((Vector3)(ref ((Character)me).m_moveDir)).magnitude > 0.1f) { reason = "estas en movimiento"; return false; } return true; } private static void ExplainIfTried(Player me, string reason) { //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) if (reason != null && PluginConfig.ShowNotices.Value && !((Object)(object)me == (Object)null) && !Plugin.IsTyping()) { KeyboardShortcut value = PluginConfig.Hotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown() && !((Object)(object)me.GetControlledShip() != (Object)null)) { ((Character)me).Message((MessageType)1, "No podes remar: " + reason, 0, (Sprite)null, false); } } } private static bool SpeedCounts(Speed speed) { //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_0007: Invalid comparison between Unknown and I4 //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 if ((int)speed == 0) { return false; } if ((int)speed == 1) { return PluginConfig.ApplyInReverse.Value; } if ((int)speed == 2) { return true; } return PluginConfig.Scope.Value != AlcanceDelEmpuje.SoloRemo; } private static void ReadHotkey(Ship ship) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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) bool flag = Plugin.IsTyping(); KeyboardShortcut value; if (PluginConfig.Mode.Value == ModoDeRemada.Mantener) { int num; if (!flag) { value = PluginConfig.Hotkey.Value; num = (((KeyboardShortcut)(ref value)).IsPressed() ? 1 : 0); } else { num = 0; } bool flag2 = (byte)num != 0; if (flag2 != _rowing) { SetRowing(ship, flag2, null); } } else if (!flag) { value = PluginConfig.Hotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { SetRowing(ship, !_rowing, null); } } } private static void SetRowing(Ship ship, bool rowing, string reason) { if (_rowing == rowing) { return; } _rowing = rowing; _exhausted = false; _nextSend = 0f; RowLog.Local(ship, rowing); if (!PluginConfig.ShowNotices.Value) { return; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { if (rowing) { ((Character)localPlayer).Message((MessageType)1, "Estas remando", 0, (Sprite)null, false); } else { ((Character)localPlayer).Message((MessageType)1, (reason == null) ? "Dejaste de remar" : ("Dejaste de remar: " + reason), 0, (Sprite)null, false); } } } private static void UpdateStamina(Player me) { float value = PluginConfig.StaminaPerSecond.Value; if (value <= 0f) { _exhausted = false; _staminaTimer = 0f; return; } if (_exhausted) { if (((Character)me).HaveStamina(((Character)me).GetMaxStamina() * 0.25f)) { _exhausted = false; Say(me, "Volves a remar"); } return; } _staminaTimer += Time.deltaTime; if (!(_staminaTimer < 0.5f)) { float num = value * _staminaTimer; _staminaTimer = 0f; if (!((Character)me).HaveStamina(num)) { _exhausted = true; Say(me, "Sin stamina: paras de remar hasta recuperar"); } else { ((Character)me).UseStamina(num); } } } private static void Say(Player me, string text) { if (PluginConfig.ShowNotices.Value && (Object)(object)me != (Object)null) { ((Character)me).Message((MessageType)1, text, 0, (Sprite)null, false); } } private static void Publish(Ship ship, long myID) { ZNetView nview = ship.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.HasOwner()) { return; } long owner = nview.GetZDO().GetOwner(); bool flag = IsRowingNow(); if (!flag && !_lastSent) { _lastOwner = owner; return; } float time = Time.time; if ((owner != _lastOwner || flag != _lastSent || !(time < _nextSend)) && !(time - _lastSend < 0.1f)) { nview.InvokeRPC("CrewRow_Rowing", new object[2] { myID, flag }); _lastSent = flag; _lastOwner = owner; _lastSend = time; _nextSend = time + PluginConfig.HeartbeatSeconds.Value; } } private static void SendStopTo(Ship ship, Player me) { if (_lastSent && !((Object)(object)ship == (Object)null) && !((Object)(object)me == (Object)null)) { ZNetView nview = ship.m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.HasOwner()) { nview.InvokeRPC("CrewRow_Rowing", new object[2] { me.GetPlayerID(), false }); } } } } internal static class RowerRegistry { private sealed class Budget { public float WindowStart; public int Used; public int Dropped; } private sealed class ShipState { public readonly Dictionary<long, float> Rowers = new Dictionary<long, float>(); public readonly Dictionary<long, Budget> Budgets = new Dictionary<long, Budget>(); public int LastNotified; } internal const string RpcName = "CrewRow_Rowing"; private const int MaxSenders = 64; private static readonly Dictionary<Ship, ShipState> States = new Dictionary<Ship, ShipState>(); private static readonly List<Ship> DeadShips = new List<Ship>(8); private static readonly List<long> ExpiredRowers = new List<long>(16); private static readonly List<long> ExpiredSenders = new List<long>(16); private static float _nextRejectLog; public static void Register(Ship ship) { if (!((Object)(object)ship == (Object)null) && !((Object)(object)ship.m_nview == (Object)null) && ship.m_nview.IsValid()) { ship.m_nview.Register<long, bool>("CrewRow_Rowing", (Action<long, long, bool>)delegate(long sender, long playerID, bool rowing) { Handle(ship, sender, playerID, rowing); }); } } private static void Handle(Ship ship, long sender, long playerID, bool rowing) { try { if ((Object)(object)ship == (Object)null) { return; } ZNetView nview = ship.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner() || !PluginConfig.Enabled.Value || playerID == 0L || sender == 0L) { return; } if (!ship.IsPlayerInBoat(playerID)) { LogRejected("no esta a bordo", sender, playerID); return; } Player player = Player.GetPlayer(playerID); if ((Object)(object)player == (Object)null || ((Character)player).GetOwner() != sender) { LogRejected("la identidad no coincide con el remitente", sender, playerID); return; } ShipState state = GetState(ship); if (TakeBudget(state, sender)) { if ((Object)(object)ship.m_shipControlls != (Object)null && ship.m_shipControlls.GetUser() == playerID) { state.Rowers.Remove(playerID); } else if (rowing) { state.Rowers[playerID] = Time.time; } else { state.Rowers.Remove(playerID); } } } catch (Exception ex) { Plugin.Log.LogError((object)("Fallo al procesar un aviso de remada: " + ex)); } } public static int CountActive(Ship ship) { if ((Object)(object)ship == (Object)null) { return 0; } if (!States.TryGetValue(ship, out var value) || value.Rowers.Count == 0) { return 0; } float time = Time.time; float num = PluginConfig.StaleSeconds.Value; float num2 = PluginConfig.HeartbeatSeconds.Value * 2f; if (num < num2) { num = num2; } ExpiredRowers.Clear(); foreach (KeyValuePair<long, float> rower in value.Rowers) { if (time - rower.Value > num || !ship.IsPlayerInBoat(rower.Key)) { ExpiredRowers.Add(rower.Key); } } for (int i = 0; i < ExpiredRowers.Count; i++) { value.Rowers.Remove(ExpiredRowers[i]); } int value2 = PluginConfig.MaxRowers.Value; if (value.Rowers.Count >= value2) { return value2; } return value.Rowers.Count; } public static bool CountChanged(Ship ship, int rowers) { if ((Object)(object)ship == (Object)null || !States.TryGetValue(ship, out var value)) { return false; } if (value.LastNotified == rowers) { return false; } value.LastNotified = rowers; return true; } public static void Reset() { States.Clear(); } private static ShipState GetState(Ship ship) { if (States.TryGetValue(ship, out var value)) { return value; } PruneDeadShips(); value = new ShipState(); States[ship] = value; return value; } private static void PruneDeadShips() { if (States.Count < 16) { return; } DeadShips.Clear(); foreach (KeyValuePair<Ship, ShipState> state in States) { if ((Object)(object)state.Key == (Object)null) { DeadShips.Add(state.Key); } } for (int i = 0; i < DeadShips.Count; i++) { States.Remove(DeadShips[i]); } } private static bool TakeBudget(ShipState state, long sender) { if (!state.Budgets.TryGetValue(sender, out var value)) { PruneBudgets(state); if (state.Budgets.Count >= 64) { return false; } value = new Budget(); state.Budgets[sender] = value; } float time = Time.time; if (time - value.WindowStart >= 1f) { if (value.Dropped > 0) { RowLog.Rejected("inundacion, " + value.Dropped + " avisos descartados", sender, 0L); } value.WindowStart = time; value.Used = 0; value.Dropped = 0; } if (value.Used >= PluginConfig.MaxMessagesPerSecond.Value) { value.Dropped++; return false; } value.Used++; return true; } private static void PruneBudgets(ShipState state) { if (state.Budgets.Count < 32) { return; } float time = Time.time; ExpiredSenders.Clear(); foreach (KeyValuePair<long, Budget> budget in state.Budgets) { if (time - budget.Value.WindowStart > 60f) { ExpiredSenders.Add(budget.Key); } } for (int i = 0; i < ExpiredSenders.Count; i++) { state.Budgets.Remove(ExpiredSenders[i]); } } private static void LogRejected(string reason, long sender, long playerID) { float time = Time.time; if (!(time < _nextRejectLog)) { _nextRejectLog = time + 1f; RowLog.Rejected(reason, sender, playerID); } } } internal static class SpeedReadout { private const float SmoothingSeconds = 0.3f; private const float TeleportDistance = 50f; private const float MetersPerSecondToKnots = 1.943844f; private static Ship _tracked; private static Vector3 _lastPosition; private static float _speed; public static void Tick() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Reset(); return; } Ship val = localPlayer.GetControlledShip(); if ((Object)(object)val == (Object)null) { val = Ship.GetLocalShip(); } Sample(val); if (PluginConfig.ShowSpeed.Value && !((Object)(object)localPlayer.GetControlledShip() == (Object)null) && !Plugin.IsTyping()) { KeyboardShortcut value = PluginConfig.Hotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { ((Character)localPlayer).Message((MessageType)1, Describe(), 0, (Sprite)null, false); } } } public static void Reset() { _tracked = null; _speed = 0f; } private static void Sample(Ship ship) { //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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ship == (Object)null) { Reset(); return; } Vector3 position = ((Component)ship).transform.position; if (ship != _tracked) { _tracked = ship; _lastPosition = position; _speed = 0f; return; } float deltaTime = Time.deltaTime; if (!(deltaTime <= 0f)) { Vector3 val = position - _lastPosition; _lastPosition = position; if (((Vector3)(ref val)).magnitude > 50f) { _speed = 0f; return; } val.y = 0f; Vector3 forward = ((Component)ship).transform.forward; forward.y = 0f; ((Vector3)(ref forward)).Normalize(); float num = Vector3.Dot(val / deltaTime, forward); _speed += (num - _speed) * (1f - Mathf.Exp((0f - deltaTime) / 0.3f)); } } private static string Describe() { float num = _speed * 1.943844f; return "Velocidad: " + _speed.ToString("F1") + " m/s (" + num.ToString("F1") + " nudos)"; } } } namespace CrewRow.Core { [HarmonyPatch(typeof(Ship), "Start")] internal static class Ship_Start_Patch { private static void Postfix(Ship __instance) { try { RowerRegistry.Register(__instance); } catch (Exception ex) { Plugin.Log.LogError((object)("No se pudo registrar el aviso de remada en un barco. Ese barco no va a recibir remeros; el resto sigue funcionando. Detalle: " + ex.Message)); } } } [HarmonyPatch(typeof(Game), "Logout")] internal static class Game_Logout_Patch { private static void Prefix() { RowBoost.Restore(); RowerRegistry.Reset(); RowerClient.Reset(); SpeedReadout.Reset(); } } internal static class RowLog { public static void Local(Ship ship, bool rowing) { if (!PluginConfig.RowLog.Value) { return; } try { Plugin.Log.LogInfo((object)((rowing ? "REMANDO -> " : "PARO <- ") + Describe(ship))); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Fallo al anotar la remada local: " + ex.Message)); } } public static void Count(Ship ship, int rowers, float factor) { if (!PluginConfig.RowLog.Value) { return; } try { Plugin.Log.LogInfo((object)("TRIPULACION " + Describe(ship) + " | remeros: " + rowers + " | empuje x" + factor.ToString("F2"))); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Fallo al anotar la cuenta de remeros: " + ex.Message)); } } public static void Rejected(string reason, long sender, long playerID) { if (!PluginConfig.RowLog.Value) { return; } try { Plugin.Log.LogInfo((object)("DESCARTADO (" + reason + ") | de " + sender + " | jugador " + playerID)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Fallo al anotar un descarte: " + ex.Message)); } } private static string Describe(Ship ship) { //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) if ((Object)(object)ship == (Object)null) { return "barco desconocido"; } Vector3 position = ((Component)ship).transform.position; return ((Object)ship).name.Replace("(Clone)", "") + " (" + position.x.ToString("F0") + ", " + position.z.ToString("F0") + ")"; } } } namespace CrewRow.Config { public enum ModoDeRemada { Alternar, Mantener } public enum AlcanceDelEmpuje { SoloRemo, RemoYVelaEnContra, Siempre } internal static class PluginConfig { private const string General = "1 - General"; private const string Remar = "2 - Remar"; private const string Fuerza = "3 - Fuerza"; private const string Red = "4 - Red"; private const string Interfaz = "5 - Interfaz"; public static ConfigEntry<bool> Enabled; public static ConfigEntry<bool> RowLog; public static ConfigEntry<KeyboardShortcut> Hotkey; public static ConfigEntry<ModoDeRemada> Mode; public static ConfigEntry<AlcanceDelEmpuje> Scope; public static ConfigEntry<bool> ApplyInReverse; public static ConfigEntry<bool> CancelOnMove; public static ConfigEntry<float> StaminaPerSecond; public static ConfigEntry<float> BonusPerRower; public static ConfigEntry<float> MaxBonus; public static ConfigEntry<int> MaxRowers; public static ConfigEntry<float> HeartbeatSeconds; public static ConfigEntry<float> StaleSeconds; public static ConfigEntry<int> MaxMessagesPerSecond; public static ConfigEntry<bool> ShowNotices; public static ConfigEntry<bool> ShowSpeed; public static void Bind(ConfigFile cfg) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Expected O, but got Unknown //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Expected O, but got Unknown //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Expected O, but got Unknown //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Expected O, but got Unknown //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Expected O, but got Unknown Enabled = cfg.Bind<bool>("1 - General", "Activado", true, "Apaga el mod entero sin desinstalarlo."); RowLog = cfg.Bind<bool>("1 - General", "LogDeRemada", false, "Anota en BepInEx/LogOutput.log cada vez que alguien empieza o deja de remar, y cuantos remeros cuenta el barco. Sirve para entender por que no suma velocidad; dejalo apagado en el dia a dia porque es ruidoso."); Hotkey = cfg.Bind<KeyboardShortcut>("2 - Remar", "Atajo", new KeyboardShortcut((KeyCode)98, Array.Empty<KeyCode>()), "Tecla para ponerse a remar. No se dispara mientras escribis en el chat, la consola o un cartel. Cambiala si choca con otro mod."); Mode = cfg.Bind<ModoDeRemada>("2 - Remar", "Modo", ModoDeRemada.Alternar, "Alternar: una pulsacion prende y otra apaga. Mantener: se rema solo mientras tenes la tecla apretada."); Scope = cfg.Bind<AlcanceDelEmpuje>("2 - Remar", "AplicarEn", AlcanceDelEmpuje.SoloRemo, "SoloRemo: la tripulacion suma unicamente en las velocidades de remo, que es lo unico que sirve con viento en contra, y la vela queda igual que en el juego base. RemoYVelaEnContra: ademas empuja con la vela izada, en proporcion a lo malo que sea el angulo del viento, asi que con viento a favor no cambia nada. Siempre: suma en todos los modos."); ApplyInReverse = cfg.Bind<bool>("2 - Remar", "AplicarEnReversa", true, "Si la tripulacion tambien ayuda cuando el barco va marcha atras."); CancelOnMove = cfg.Bind<bool>("2 - Remar", "CancelarAlMoverse", true, "Dejas de remar apenas caminas por la cubierta, y no podes empezar a remar en movimiento. Remar ocupa las dos manos: no se puede ir caminando al mismo tiempo. Apagalo si preferis seguir remando mientras te acomodas."); StaminaPerSecond = cfg.Bind<float>("2 - Remar", "CostoStaminaPorSegundo", 1f, new ConfigDescription("Stamina por segundo que gasta remar. Mientras remas no regeneras, asi que el gasto neto es justo este numero: con 1 y 75 de stamina se rema poco mas de un minuto seguido, bastante mas con comida. Al quedarte sin, la remada se PAUSA y vuelve sola al recuperar un cuarto de la barra, sin tener que apretar la tecla de nuevo. En 0 remar es gratis y la stamina ni se toca.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); BonusPerRower = cfg.Bind<float>("3 - Fuerza", "BonoPorRemero", 0.5f, new ConfigDescription("Cuanto suma cada remero, como fraccion del empuje de remo del barco. 0.5 = cada uno agrega medio remero extra.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>())); MaxBonus = cfg.Bind<float>("3 - Fuerza", "BonoMaximo", 3f, new ConfigDescription("Tope del bono sumado de toda la tripulacion. 3 = como mucho el empuje llega a 4 veces el normal. Ojo que el agua frena de forma cuadratica: 4 veces la fuerza es cerca del DOBLE de velocidad, no el cuadruple.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>())); MaxRowers = cfg.Bind<int>("3 - Fuerza", "MaxRemeros", 6, new ConfigDescription("Cuantos remeros se cuentan como maximo, por mas que haya mas gente arriba.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 13), Array.Empty<object>())); HeartbeatSeconds = cfg.Bind<float>("4 - Red", "LatidoSegundos", 0.5f, new ConfigDescription("Cada cuanto el remero le recuerda al dueno del barco que sigue remando. Es el mismo ritmo que usa el juego para mandar el timon. Subirlo baja el trafico y agranda el bache cuando cambia el dueno del barco.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 5f), Array.Empty<object>())); StaleSeconds = cfg.Bind<float>("4 - Red", "CaducidadSegundos", 2f, new ConfigDescription("Cuanto se le cree a un remero sin volver a escucharlo. Tiene que ser holgadamente mayor que LatidoSegundos, o los remeros van a titilar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>())); MaxMessagesPerSecond = cfg.Bind<int>("4 - Red", "MaxMensajesPorSegundo", 6, new ConfigDescription("Tope de avisos por segundo que se le aceptan a cada jugador. El motor del juego no limita los RPC, asi que el freno lo ponemos aca: lo que pase de este tope se descarta sin mirarlo.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>())); ShowNotices = cfg.Bind<bool>("5 - Interfaz", "MostrarAvisos", true, "Avisa arriba a la izquierda cuando empezas o dejas de remar, y le avisa al timonel cuantos estan remando cada vez que ese numero cambia."); ShowSpeed = cfg.Bind<bool>("5 - Interfaz", "MostrarVelocidad", true, "Llevando el timon, la misma tecla de remar muestra a que velocidad va el barco. Para el timonel esa tecla no tiene otro uso, porque no puede remar."); WarnAboutMouseHotkey(); Hotkey.SettingChanged += delegate { WarnAboutMouseHotkey(); }; } private unsafe static void WarnAboutMouseHotkey() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 KeyboardShortcut value = Hotkey.Value; KeyCode mainKey = ((KeyboardShortcut)(ref value)).MainKey; if ((int)mainKey >= 323 && (int)mainKey <= 329) { Plugin.Log.LogWarning((object)("El atajo para remar esta puesto en " + ((object)(*(KeyCode*)(&mainKey))/*cast due to .constrained prefix*/).ToString() + ", que es un boton del mouse y se pisa con atacar o usar. Cambialo en la opcion Atajo si te molesta.")); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } }