using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GlobalEnums; using HarmonyLib; using TeamCherry.SharedUtils; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("silgsong_checkpoint-mod")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+76311ddc742297d098e756f857f84c74f8d7a050")] [assembly: AssemblyProduct("silgsong_checkpoint-mod")] [assembly: AssemblyTitle("silgsong_checkpoint-mod")] [assembly: AssemblyVersion("1.0.0.0")] namespace silgsong_checkpoint_mod; internal sealed class CheckpointHotkeys : MonoBehaviour { private const float MessageSeconds = 2.5f; private const float FadeSeconds = 0.5f; private static ConfigEntry hotKey; private string message; private float messageUntil; private GUIStyle messageStyle; private Texture2D background; public static void Bind(ConfigFile config) { hotKey = config.Bind("Controls", "HotKey", (KeyCode)112, "Клавиша: нажать — поставить точку возрождения здесь, с Shift — удалить её."); } private void Update() { //IL_0000: 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) if (WasKeyPressed(EasyModeManager.HotKey)) { bool flag = EasyModeManager.Toggle(HeroController.instance); ShowMessage(flag ? "Легкий режим: ВКЛ" : "Легкий режим: ВЫКЛ"); } if (WasKeyPressed(hotKey.Value)) { if (IsShiftHeld()) { ClearCheckpoint(); } else { SetCheckpointHere(); } } } private void OnDestroy() { if ((Object)(object)background != (Object)null) { Object.Destroy((Object)(object)background); } } private void SetCheckpointHere() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) GameManager instance = GameManager.instance; HeroController instance2 = HeroController.instance; if (!((Object)(object)instance != (Object)null) || !((Object)(object)instance2 != (Object)null) || (int)instance.GameState != 4 || instance2.cState.dead || string.IsNullOrEmpty(instance.GetSceneNameString())) { ShowMessage("Точку можно поставить только в обычной игре"); return; } CustomCheckpoint.Set(instance.GetSceneNameString(), Vector2.op_Implicit(((Component)instance2).transform.position), instance2.cState.facingRight); ShowMessage("Точка возрождения поставлена здесь"); } private void ClearCheckpoint() { if (!CustomCheckpoint.IsSet) { ShowMessage("Точка возрождения не задана"); return; } CustomCheckpoint.Clear(); ShowMessage("Точка возрождения удалена — возрождение как в ванилле"); } private void ShowMessage(string text) { message = text; messageUntil = Time.unscaledTime + 2.5f; } private void OnGUI() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) float num = messageUntil - Time.unscaledTime; if (!string.IsNullOrEmpty(message) && !(num <= 0f)) { EnsureStyles(); GUIContent val = new GUIContent(message); float num2 = Mathf.Min(messageStyle.CalcSize(val).x + 40f, (float)Screen.width - 40f); float num3 = messageStyle.CalcHeight(val, num2) + 20f; Rect val2 = new Rect(((float)Screen.width - num2) * 0.5f, (float)Screen.height * 0.1f, num2, num3); Color color = GUI.color; GUI.color = new Color(1f, 1f, 1f, Mathf.Clamp01(num / 0.5f)); GUI.Label(val2, val, messageStyle); GUI.color = color; } } private void EnsureStyles() { //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_001e: Expected O, but got Unknown //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_00a6: Expected O, but got Unknown //IL_00ab: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if (messageStyle == null) { background = new Texture2D(1, 1) { hideFlags = (HideFlags)61 }; background.SetPixel(0, 0, new Color(0f, 0f, 0f, 0.75f)); background.Apply(); messageStyle = new GUIStyle(GUI.skin.label) { fontSize = Mathf.Clamp(Mathf.RoundToInt(18f * (float)Screen.height / 1080f), 16, 32), alignment = (TextAnchor)4, wordWrap = true, padding = new RectOffset(20, 20, 10, 10) }; messageStyle.normal.background = background; messageStyle.normal.textColor = Color.white; } } private static bool IsShiftHeld() { try { Keyboard current = Keyboard.current; if (current != null) { return ((ButtonControl)current.leftShiftKey).isPressed || ((ButtonControl)current.rightShiftKey).isPressed; } } catch (Exception) { } try { return Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303); } catch (Exception) { return false; } } private unsafe static bool WasKeyPressed(KeyCode key) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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) try { Keyboard current = Keyboard.current; if (current != null && Enum.TryParse(((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString(), ignoreCase: true, out Key result) && (int)result != 0) { KeyControl val = current[result]; if (val != null) { return ((ButtonControl)val).wasPressedThisFrame; } } } catch (Exception) { } try { return Input.GetKeyDown(key); } catch (Exception) { return false; } } } internal static class CustomCheckpoint { public const string MarkerName = "S2E Custom Respawn Marker"; private static ConfigEntry scene; private static ConfigEntry posX; private static ConfigEntry posY; private static ConfigEntry facingRight; public static bool IsSet => !string.IsNullOrEmpty(scene.Value); public static string Scene => scene.Value; public static Vector2 Position => new Vector2(posX.Value, posY.Value); public static bool FacingRight => facingRight.Value; public static void Bind(ConfigFile config) { scene = config.Bind("Checkpoint", "Scene", "", "Сцена пользовательской точки возрождения. Пусто — точка не задана, возрождение полностью ванильное."); posX = config.Bind("Checkpoint", "X", 0f, "Координата X точки возрождения."); posY = config.Bind("Checkpoint", "Y", 0f, "Координата Y точки возрождения."); facingRight = config.Bind("Checkpoint", "FacingRight", true, "В какую сторону смотрит герой после возрождения."); } public static void Set(string sceneName, Vector2 position, bool isFacingRight) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) scene.Value = sceneName; posX.Value = position.x; posY.Value = position.y; facingRight.Value = isFacingRight; ModEntry.Log.LogInfo((object)$"Точка возрождения поставлена: {sceneName} ({position.x:0.##}, {position.y:0.##})."); } public static void Clear() { scene.Value = ""; posX.Value = 0f; posY.Value = 0f; DestroyMarker(); ModEntry.Log.LogInfo((object)"Точка возрождения удалена, возрождение снова ванильное."); } public static Transform GetOrCreateMarker(Vector2 position, bool isFacingRight) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_0032: Expected O, but got Unknown RespawnMarker val = FindMarker(); if ((Object)(object)val == (Object)null) { val = new GameObject("S2E Custom Respawn Marker").AddComponent(); } if (val.customFadeDuration == null) { val.customFadeDuration = new OverrideFloat(); } val.customWakeUp = false; val.respawnFacingRight = isFacingRight; ((Component)val).transform.position = new Vector3(position.x, position.y, 0f); return ((Component)val).transform; } private static RespawnMarker FindMarker() { List markers = RespawnMarker.Markers; if (markers == null) { return null; } foreach (RespawnMarker item in markers) { if ((Object)(object)item != (Object)null && ((Object)item).name == "S2E Custom Respawn Marker") { return item; } } return null; } private static void DestroyMarker() { RespawnMarker val = FindMarker(); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } } } internal static class EasyModeManager { private static ConfigEntry hotKey; private static ConfigEntry isEnabledConfig; private static readonly Dictionary fractionalDamageMap = new Dictionary(); private static bool isEnabled; public static KeyCode HotKey { get { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (hotKey == null) { return (KeyCode)108; } return hotKey.Value; } } public static void Bind(ConfigFile config) { hotKey = config.Bind("Controls", "EasyModeHotKey", (KeyCode)108, "Клавиша для переключения Легкого режима (Easy Mode)."); isEnabledConfig = config.Bind("EasyMode", "Enabled", false, "Включен ли легкий режим по умолчанию."); isEnabled = isEnabledConfig.Value; } public static bool IsEnabled(HeroController hero = null) { return isEnabled; } public static bool Toggle(HeroController hero = null) { isEnabled = !isEnabled; if (isEnabledConfig != null) { isEnabledConfig.Value = isEnabled; } ResetFractionalDamage(hero); ModEntry.Log.LogInfo((object)("Легкий режим (Easy Mode) " + (isEnabled ? "ВКЛЮЧЕН" : "ВЫКЛЮЧЕН") + ".")); return isEnabled; } public static int CalculateIncomingDamage(HeroController hero, int rawDamage) { if (rawDamage <= 0) { return 0; } float fractionalDamage = GetFractionalDamage(hero); float num = (float)rawDamage * 0.5f; float num2 = fractionalDamage + num; int num3 = Mathf.FloorToInt(num2); float num4 = num2 - (float)num3; SetFractionalDamage(hero, num4); ModEntry.Log.LogDebug((object)$"[EasyMode] Входящий урон: {rawDamage} -> Получено: {num3} (Дробный остаток: {num4:0.0})"); return num3; } public static void ResetFractionalDamage(HeroController hero = null) { if ((Object)(object)hero != (Object)null) { fractionalDamageMap.Remove(hero); } else { fractionalDamageMap.Clear(); } } public static bool IsHeroAttack(HitInstance hitInstance) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (!isEnabled) { return false; } if (hitInstance.IsHeroDamage) { return true; } if ((Object)(object)hitInstance.Source != (Object)null) { if (hitInstance.Source.CompareTag("Player") || ((Component)hitInstance.Source.transform.root).CompareTag("Player")) { return true; } if ((Object)(object)hitInstance.Source.GetComponentInParent() != (Object)null) { return true; } if ((Object)(object)HeroController.instance != (Object)null && ((Object)(object)hitInstance.Source == (Object)(object)((Component)HeroController.instance).gameObject || hitInstance.Source.transform.IsChildOf(((Component)HeroController.instance).transform))) { return true; } } return false; } private static float GetFractionalDamage(HeroController hero) { if ((Object)(object)hero != (Object)null && fractionalDamageMap.TryGetValue(hero, out var value)) { return value; } return 0f; } private static void SetFractionalDamage(HeroController hero, float val) { if ((Object)(object)hero != (Object)null) { if (val > 0.001f) { fractionalDamageMap[hero] = val; } else { fractionalDamageMap.Remove(hero); } } } } [HarmonyPatch] internal static class EasyModePatches { private static bool isHandlingTakeHealth; [HarmonyPatch(typeof(PlayerData), "TakeHealth")] [HarmonyPrefix] private static void PlayerDataTakeHealthPrefix(PlayerData __instance, ref int amount) { if (isHandlingTakeHealth || amount <= 0 || !EasyModeManager.IsEnabled()) { return; } try { isHandlingTakeHealth = true; amount = EasyModeManager.CalculateIncomingDamage(HeroController.instance, amount); } finally { isHandlingTakeHealth = false; } } [HarmonyPatch(typeof(HeroController), "StartRecoil")] [HarmonyPrefix] private static void HeroStartRecoilPrefix(HeroController __instance, ref CollisionSide impactSide) { if (EasyModeManager.IsEnabled(__instance)) { impactSide = (CollisionSide)4; } } [HarmonyPatch(typeof(HealthManager), "Hit")] [HarmonyPrefix] private static void HealthManagerHitPrefix(ref HitInstance hitInstance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (EasyModeManager.IsHeroAttack(hitInstance)) { hitInstance.DamageDealt *= 2; } } [HarmonyPatch(typeof(HealthManager), "ApplyExtraDamage", new Type[] { typeof(HitInstance) })] [HarmonyPrefix] private static void HealthManagerApplyExtraDamagePrefix(ref HitInstance hitInstance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (EasyModeManager.IsHeroAttack(hitInstance)) { hitInstance.DamageDealt *= 2; } } [HarmonyPatch(typeof(HeroController), "HeroRespawned")] [HarmonyPostfix] private static void HeroRespawnedPostfix(HeroController __instance) { EasyModeManager.ResetFractionalDamage(__instance); } [HarmonyPatch(typeof(HeroController), "Die")] [HarmonyPostfix] private static void HeroDiePostfix(HeroController __instance) { EasyModeManager.ResetFractionalDamage(__instance); } } [BepInPlugin("silksong.checkpoint.mod", "Silksong Checkpoint Mod", "1.0.0")] public sealed class ModEntry : BaseUnityPlugin { public const string Guid = "silksong.checkpoint.mod"; public const string Name = "Silksong Checkpoint Mod"; public const string Version = "1.0.0"; private Harmony harmony; internal static ManualLogSource Log { get; private set; } private void Awake() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; CustomCheckpoint.Bind(((BaseUnityPlugin)this).Config); CheckpointHotkeys.Bind(((BaseUnityPlugin)this).Config); EasyModeManager.Bind(((BaseUnityPlugin)this).Config); harmony = new Harmony("silksong.checkpoint.mod"); harmony.PatchAll(typeof(RespawnPatches)); harmony.PatchAll(typeof(EasyModePatches)); ((Component)this).gameObject.AddComponent(); Log.LogInfo((object)("Silksong Checkpoint Mod 1.0.0 загружен. Точка возрождения: " + (CustomCheckpoint.IsSet ? CustomCheckpoint.Scene : "не задана") + ". Легкий режим: " + (EasyModeManager.IsEnabled() ? "ВКЛ" : "ВЫКЛ") + ".")); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } } [HarmonyPatch] internal static class RespawnPatches { private static bool loadingSavedGame; private static bool overrideArmed; private static string armedScene; private static Vector2 armedPosition; private static bool armedFacingRight; [HarmonyPatch(typeof(GameManager), "ReadyForRespawn")] [HarmonyPrefix] private static void ReadyForRespawnPrefix(bool isFirstLevelForPlayer) { loadingSavedGame = isFirstLevelForPlayer; } [HarmonyPatch(typeof(GameManager), "ReadyForRespawn")] [HarmonyFinalizer] private static void ReadyForRespawnFinalizer() { loadingSavedGame = false; } [HarmonyPatch(typeof(GameManager), "GetRespawnInfo")] [HarmonyPostfix] private static void GetRespawnInfoPostfix(ref string scene, ref string marker) { //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) if (!loadingSavedGame && CustomCheckpoint.IsSet) { scene = CustomCheckpoint.Scene; marker = "S2E Custom Respawn Marker"; overrideArmed = true; armedScene = CustomCheckpoint.Scene; armedPosition = CustomCheckpoint.Position; armedFacingRight = CustomCheckpoint.FacingRight; } } [HarmonyPatch(typeof(HeroController), "LocateSpawnPoint")] [HarmonyPostfix] private static void LocateSpawnPointPostfix(ref Transform __result) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (overrideArmed && (IsArmedScene() || !((Object)(object)__result != (Object)null))) { Transform orCreateMarker = CustomCheckpoint.GetOrCreateMarker(armedPosition, armedFacingRight); if (!((Object)(object)orCreateMarker == (Object)null)) { __result = orCreateMarker; overrideArmed = false; } } } private static bool IsArmedScene() { //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) GameManager instance = GameManager.instance; if ((Object)(object)instance != (Object)null && instance.GetSceneNameString() == armedScene) { return true; } Scene activeScene = SceneManager.GetActiveScene(); return ((Scene)(ref activeScene)).name == armedScene; } }