using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using Configgy;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Ultrakill Dual Wield Skill Mod")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Ultrakill Dual Wield Skill Mod")]
[assembly: AssemblyCopyright("Copyright © 2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("118d6cb8-6e15-4c1e-8f93-e0833b89ea40")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace DualWieldSkill;
[BepInPlugin("com.ReFraX.ultrakill.dualwieldskill", "Dual Wield Skill", "2.1.0")]
public class DualWieldMod : BaseUnityPlugin
{
public static DualWieldMod Instance;
private ConfigEntry<float> cooldownConfig;
private ConfigEntry<float> powerUpDurationConfig;
[Configgable("UI", "Horizontal Position", 0, null)]
private static FloatSlider uiXPositionConfig = new FloatSlider(-750f, -1000f, 1000f);
[Configgable("UI", "Vertical Position", 0, null)]
private static FloatSlider uiYPositionConfig = new FloatSlider(400f, -600f, 600f);
[Configgable("UI", "Icon Size", 0, null)]
private static FloatSlider uiScaleConfig = new FloatSlider(1f, 0.5f, 2f);
[Configgable("", "Activate Dual-Wield", 0, null)]
public static ConfigKeybind inputKeyConfig = new ConfigKeybind((KeyCode)98);
private float nextUseTime;
private float sceneLoadTime;
private bool uiFadeInStarted;
private Image activeIcon;
private Image inactiveIcon;
private Text timerText;
private Font customFont;
private Canvas uiCanvas;
private CanvasGroup canvasGroup;
private bool inputEnabled = true;
private bool wasDead;
private GameObject uiContainer;
private float lastXValue;
private float lastYValue;
private float lastScaleValue = 1f;
private float originalCooldownValue;
private float cooldownStartTime;
private bool cooldownActive;
private void Awake()
{
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
Instance = this;
cooldownConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Gameplay", "Cooldown Time", 120f, "Cooldown time in seconds for the dual-wield power-up.");
powerUpDurationConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Gameplay", "Power-Up Duration", 30f, "Duration in seconds for the dual-wield power-up.");
originalCooldownValue = cooldownConfig.Value;
cooldownConfig.SettingChanged += OnCooldownSettingChanged;
new ConfigBuilder("Dual Wield Skill", (string)null).BuildAll();
customFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
new Harmony("com.ReFraX.ultrakill.dualwieldskill").PatchAll();
CheckBepInExConfig();
}
private void OnCooldownSettingChanged(object sender, EventArgs e)
{
if (cooldownActive)
{
float num = Time.time - cooldownStartTime;
float num2 = (originalCooldownValue - num) / originalCooldownValue * cooldownConfig.Value;
nextUseTime = Time.time + num2;
originalCooldownValue = cooldownConfig.Value;
cooldownStartTime = Time.time - (cooldownConfig.Value - num2);
}
}
private void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void CheckBepInExConfig()
{
string path = Path.Combine(Paths.ConfigPath, "BepInEx.cfg");
if (File.Exists(path) && File.ReadAllText(path).Contains("HideManagerGameObject = false"))
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"For the Dual Wield Skill mod to function correctly with newer ULTRAKILL versions, please set 'HideManagerGameObject = true' in BepInEx.cfg.");
}
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
ResetCooldown();
sceneLoadTime = Time.time;
uiFadeInStarted = false;
if ((Object)(object)canvasGroup != (Object)null)
{
canvasGroup.alpha = 0f;
}
UpdateUIPosition();
((MonoBehaviour)this).Invoke("EnsureUIVisible", 3f);
}
private void EnsureUIVisible()
{
if (!uiFadeInStarted && !IsRealMenu() && !IsPauseMenu())
{
uiFadeInStarted = true;
((Behaviour)uiCanvas).enabled = true;
canvasGroup.alpha = 1f;
}
}
private void CreateUICanvas()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Expected O, but got Unknown
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
GameObject val = GameObject.Find("DualWieldCanvas");
if ((Object)(object)val != (Object)null)
{
Object.Destroy((Object)(object)val);
}
GameObject val2 = new GameObject("DualWieldCanvas");
uiCanvas = val2.AddComponent<Canvas>();
uiCanvas.renderMode = (RenderMode)0;
canvasGroup = val2.AddComponent<CanvasGroup>();
canvasGroup.alpha = 0f;
CanvasScaler obj = val2.AddComponent<CanvasScaler>();
obj.uiScaleMode = (ScaleMode)1;
obj.referenceResolution = new Vector2(1920f, 1080f);
obj.screenMatchMode = (ScreenMatchMode)0;
obj.matchWidthOrHeight = 0.5f;
val2.AddComponent<GraphicRaycaster>();
Object.DontDestroyOnLoad((Object)(object)val2);
}
private void Start()
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Expected O, but got Unknown
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Expected O, but got Unknown
//IL_007d: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: 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_00bb: 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_00d0: Expected O, but got Unknown
//IL_011c: Unknown result type (might be due to invalid IL or missing references)
//IL_0123: Expected O, but got Unknown
//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
//IL_01de: Unknown result type (might be due to invalid IL or missing references)
//IL_01f3: 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_0212: Unknown result type (might be due to invalid IL or missing references)
//IL_022f: Unknown result type (might be due to invalid IL or missing references)
//IL_0236: Expected O, but got Unknown
//IL_027f: Unknown result type (might be due to invalid IL or missing references)
//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
CreateUICanvas();
GameObject val = new GameObject("CooldownUI");
val.transform.SetParent(((Component)uiCanvas).transform, false);
try
{
Sprite sprite = LoadEmbeddedResource("Dual_Wield_Skill.Assets.Dual Wield Skill Icon.png");
Sprite sprite2 = LoadEmbeddedResource("Dual_Wield_Skill.Assets.Dual Wield Skill Icon Inactive.png");
uiContainer = new GameObject("UIContainer");
uiContainer.transform.SetParent(val.transform, false);
RectTransform obj = uiContainer.AddComponent<RectTransform>();
obj.anchorMin = new Vector2(0.5f, 0.5f);
obj.anchorMax = new Vector2(0.5f, 0.5f);
obj.pivot = new Vector2(0.5f, 0.5f);
obj.sizeDelta = new Vector2(150f, 150f);
GameObject val2 = new GameObject("InactiveIcon");
val2.transform.SetParent(uiContainer.transform, false);
inactiveIcon = val2.AddComponent<Image>();
inactiveIcon.sprite = sprite2;
inactiveIcon.type = (Type)0;
inactiveIcon.preserveAspect = true;
GameObject val3 = new GameObject("ActiveIcon");
val3.transform.SetParent(uiContainer.transform, false);
activeIcon = val3.AddComponent<Image>();
activeIcon.sprite = sprite;
activeIcon.type = (Type)3;
activeIcon.fillMethod = (FillMethod)4;
activeIcon.fillOrigin = 2;
activeIcon.fillClockwise = true;
activeIcon.preserveAspect = true;
RectTransform[] array = (RectTransform[])(object)new RectTransform[2]
{
((Component)inactiveIcon).GetComponent<RectTransform>(),
((Component)activeIcon).GetComponent<RectTransform>()
};
foreach (RectTransform obj2 in array)
{
obj2.anchorMin = new Vector2(0.5f, 0.5f);
obj2.anchorMax = new Vector2(0.5f, 0.5f);
obj2.pivot = new Vector2(0.5f, 0.5f);
obj2.anchoredPosition = Vector2.zero;
obj2.sizeDelta = new Vector2(100f, 100f);
}
GameObject val4 = new GameObject("TimerText");
val4.transform.SetParent(uiContainer.transform, false);
timerText = val4.AddComponent<Text>();
timerText.font = customFont;
timerText.fontSize = 24;
((Graphic)timerText).color = Color.yellow;
timerText.alignment = (TextAnchor)4;
RectTransform component = ((Component)timerText).GetComponent<RectTransform>();
component.anchorMin = new Vector2(0.5f, 0.5f);
component.anchorMax = new Vector2(0.5f, 0.5f);
component.pivot = new Vector2(0.5f, 0.5f);
component.anchoredPosition = Vector2.zero;
component.sizeDelta = new Vector2(150f, 50f);
((Transform)component).SetAsLastSibling();
UpdateUIPosition();
}
catch (Exception ex)
{
Debug.LogError((object)("Error setting up UI: " + ex));
}
}
private void UpdateUIPosition()
{
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)uiContainer == (Object)null) && !((Object)(object)uiCanvas == (Object)null))
{
RectTransform component = uiContainer.GetComponent<RectTransform>();
if (!((Object)(object)component == (Object)null))
{
float value = ((ConfigValueElement<float>)(object)uiXPositionConfig).Value;
float value2 = ((ConfigValueElement<float>)(object)uiYPositionConfig).Value;
float value3 = ((ConfigValueElement<float>)(object)uiScaleConfig).Value;
component.anchoredPosition = new Vector2(value, value2);
((Transform)component).localScale = new Vector3(value3, value3, 1f);
lastXValue = value;
lastYValue = value2;
lastScaleValue = value3;
}
}
}
private Sprite LoadEmbeddedResource(string resourceName)
{
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Expected O, but got Unknown
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
if (stream == null)
{
Debug.LogError((object)("Resource not found: " + resourceName));
return null;
}
byte[] array = new byte[stream.Length];
stream.Read(array, 0, array.Length);
Texture2D val = new Texture2D(2, 2);
ImageConversion.LoadImage(val, array);
return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
}
private void Update()
{
//IL_0214: Unknown result type (might be due to invalid IL or missing references)
if (!Object.op_Implicit((Object)(object)activeIcon) || !Object.op_Implicit((Object)(object)inactiveIcon) || !Object.op_Implicit((Object)(object)timerText) || !Object.op_Implicit((Object)(object)canvasGroup))
{
return;
}
if (Time.time - sceneLoadTime < 2.5f)
{
((Behaviour)uiCanvas).enabled = false;
return;
}
if (!uiFadeInStarted)
{
uiFadeInStarted = true;
canvasGroup.alpha = 0f;
((Behaviour)uiCanvas).enabled = true;
}
if (canvasGroup.alpha < 1f)
{
canvasGroup.alpha = Mathf.Min(1f, canvasGroup.alpha + Time.deltaTime * 2f);
}
if (((ConfigValueElement<float>)(object)uiXPositionConfig).Value != lastXValue || ((ConfigValueElement<float>)(object)uiYPositionConfig).Value != lastYValue || ((ConfigValueElement<float>)(object)uiScaleConfig).Value != lastScaleValue)
{
UpdateUIPosition();
}
if ((Object)(object)MonoSingleton<NewMovement>.Instance != (Object)null && MonoSingleton<NewMovement>.Instance.dead)
{
SetUIVisibility(visible: false);
wasDead = true;
return;
}
if (wasDead)
{
wasDead = false;
((Behaviour)uiCanvas).enabled = true;
canvasGroup.alpha = 1f;
UpdateUIPosition();
}
if (IsRealMenu())
{
SetUIVisibility(visible: false);
inputEnabled = false;
return;
}
inputEnabled = !IsPauseMenu();
if (!uiFadeInStarted && Time.time - sceneLoadTime >= 2.5f)
{
uiFadeInStarted = true;
((Behaviour)uiCanvas).enabled = true;
}
if (uiFadeInStarted && canvasGroup.alpha < 1f)
{
canvasGroup.alpha = Mathf.Min(1f, canvasGroup.alpha + Time.deltaTime * 2f);
}
if (!(Time.time - sceneLoadTime < 2.5f))
{
if (inputEnabled && Input.GetKeyDown(((ConfigValueElement<KeyCode>)(object)inputKeyConfig).Value) && Time.time >= nextUseTime)
{
ActivateDualWield();
nextUseTime = Time.time + cooldownConfig.Value;
cooldownActive = true;
cooldownStartTime = Time.time;
originalCooldownValue = cooldownConfig.Value;
}
float num = Mathf.Max(0f, nextUseTime - Time.time);
if (num > 0f)
{
float num2 = (cooldownActive ? originalCooldownValue : cooldownConfig.Value);
float num3 = num2 - num;
activeIcon.fillAmount = num3 / num2;
((Component)inactiveIcon).gameObject.SetActive(true);
timerText.text = $"{num:F1}s";
}
else
{
activeIcon.fillAmount = 1f;
((Component)inactiveIcon).gameObject.SetActive(false);
timerText.text = "Ready!";
cooldownActive = false;
}
if (!cooldownActive)
{
activeIcon.fillAmount = 1f;
((Component)inactiveIcon).gameObject.SetActive(false);
timerText.text = "Ready!";
}
}
}
private void SetUIVisibility(bool visible)
{
if ((Object)(object)uiCanvas != (Object)null)
{
((Behaviour)uiCanvas).enabled = visible;
}
}
public static bool IsRealMenu()
{
if (SceneHelper.CurrentScene == null)
{
return true;
}
return new string[6] { "Intro", "Bootstrap", "Main Menu", "Level 2-S", "Intermission1", "Intermission2" }.Contains(SceneHelper.CurrentScene);
}
public static bool IsPauseMenu()
{
if ((Object)(object)MonoSingleton<OptionsManager>.Instance != (Object)null && MonoSingleton<OptionsManager>.Instance.paused)
{
return !IsRealMenu();
}
return false;
}
public void ResetCooldown()
{
nextUseTime = 0f;
cooldownActive = false;
if ((Object)(object)activeIcon != (Object)null)
{
activeIcon.fillAmount = 1f;
}
if ((Object)(object)inactiveIcon != (Object)null)
{
((Component)inactiveIcon).gameObject.SetActive(false);
}
if ((Object)(object)timerText != (Object)null)
{
timerText.text = "Ready!";
}
}
private void ActivateDualWield()
{
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Expected O, but got Unknown
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: 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_00e5: 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)
if (Object.op_Implicit((Object)(object)MonoSingleton<GunControl>.Instance))
{
GameObject val = new GameObject();
val.transform.SetParent(((Component)MonoSingleton<GunControl>.Instance).transform, true);
val.transform.localRotation = Quaternion.identity;
DualWield[] componentsInChildren = ((Component)MonoSingleton<GunControl>.Instance).GetComponentsInChildren<DualWield>();
if (componentsInChildren != null && componentsInChildren.Length % 2 == 0)
{
val.transform.localScale = new Vector3(-1f, 1f, 1f);
}
else
{
val.transform.localScale = Vector3.one;
}
if (componentsInChildren == null || componentsInChildren.Length == 0)
{
val.transform.localPosition = Vector3.zero;
}
else if (componentsInChildren.Length % 2 == 0)
{
val.transform.localPosition = new Vector3((float)(componentsInChildren.Length / 2) * -1.5f, 0f, 0f);
}
else
{
val.transform.localPosition = new Vector3((float)((componentsInChildren.Length + 1) / 2) * 1.5f, 0f, 0f);
}
DualWield val2 = val.AddComponent<DualWield>();
val2.delay = 0.05f;
val2.juiceAmount = powerUpDurationConfig.Value;
if (componentsInChildren != null && componentsInChildren.Length != 0)
{
val2.delay += (float)componentsInChildren.Length / 20f;
}
}
}
}
[HarmonyPatch(typeof(OptionsMenuToManager), "RestartCheckpoint")]
public static class CheckpointRestartPatch
{
[HarmonyPrefix]
public static void OnCheckpointRestart()
{
if ((Object)(object)DualWieldMod.Instance != (Object)null)
{
DualWieldMod.Instance.ResetCooldown();
}
}
}
[HarmonyPatch(typeof(NewMovement), "GetHurt")]
public static class PlayerDeathPatch
{
[HarmonyPostfix]
public static void OnPlayerDeath()
{
if ((Object)(object)MonoSingleton<NewMovement>.Instance != (Object)null && MonoSingleton<NewMovement>.Instance.dead && (Object)(object)DualWieldMod.Instance != (Object)null)
{
DualWieldMod.Instance.ResetCooldown();
}
}
}