using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using EntityStates;
using EntityStates.Merc;
using On.EntityStates.Merc;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.Skills;
using UnityEngine;
using UnityEngine.AddressableAssets;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ExamplePlugin")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ExamplePlugin")]
[assembly: AssemblyTitle("ExamplePlugin")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace ExamplePlugin;
[BepInPlugin("com.fafnir62.mercenary_extra_effects", "Mercenary Extra Effects", "1.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class MercExtraEffectsPlugin : BaseUnityPlugin
{
private class MercTracker : MonoBehaviour
{
public bool uppercutActive;
public bool whirlwindActive;
public bool evisActive;
}
public const string PluginGUID = "com.fafnir62.mercenary_extra_effects";
public const string PluginName = "Mercenary Extra Effects";
public const string PluginVersion = "1.1.0";
private static BodyIndex mercBodyIndex = (BodyIndex)(-1);
private static ConfigEntry<float> cfgSecondaryLifestealPercent;
private static ConfigEntry<float> cfgEvisBarrierPercent;
private static ConfigEntry<float> cfgFocusedAssaultDamagePercent;
private static ConfigEntry<float> cfgFocusedAssaultCooldownSeconds;
private static ConfigEntry<float> cfgEvisDamagePercent;
private static ConfigEntry<float> cfgEvisCooldownSeconds;
private const float UtilityCooldownSeconds = 6f;
private const string FocusedAssaultSkillDefPath = "RoR2/Base/Merc/MercBodyFocusedAssault.asset";
private static readonly string[] EvisSkillDefPaths = new string[3] { "RoR2/Base/Merc/MercBodyEviscerate.asset", "RoR2/Base/Merc/MercBodyEvis.asset", "RoR2/Base/Merc/MercBodySpecialEviscerate.asset" };
private bool _inRiskOfOptionsSetup;
private bool _suppressSettingChanged;
private void Awake()
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"[MercExtraEffects] Awake");
InitConfig();
RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(OnGameLoaded));
}
private void OnGameLoaded()
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Expected O, but got Unknown
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: Expected O, but got Unknown
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Expected O, but got Unknown
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Expected O, but got Unknown
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: Expected O, but got Unknown
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Expected O, but got Unknown
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Expected O, but got Unknown
mercBodyIndex = BodyCatalog.FindBodyIndex("MercBody");
((BaseUnityPlugin)this).Logger.LogInfo((object)("[MercExtraEffects] MercBodyIndex=" + ((object)(BodyIndex)(ref mercBodyIndex)).ToString()));
CharacterBody.onBodyStartGlobal += OnBodyStart;
Uppercut.OnEnter += new hook_OnEnter(Uppercut_OnEnter);
Uppercut.OnExit += new hook_OnExit(Uppercut_OnExit);
WhirlwindBase.OnEnter += new hook_OnEnter(WhirlwindBase_OnEnter);
WhirlwindBase.OnExit += new hook_OnExit(WhirlwindBase_OnExit);
Evis.OnEnter += new hook_OnEnter(Evis_OnEnter);
Evis.OnExit += new hook_OnExit(Evis_OnExit);
FocusedAssaultDash.AuthorityModifyOverlapAttack += new hook_AuthorityModifyOverlapAttack(FocusedAssaultDash_AuthorityModifyOverlapAttack);
GlobalEventManager.onServerDamageDealt += OnServerDamageDealt;
TrySetMercUtilityCooldownFromBodyPrefab(6f);
ApplySkillTuning();
}
private void InitConfig()
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected O, but got Unknown
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Expected O, but got Unknown
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Expected O, but got Unknown
//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
//IL_00f0: Expected O, but got Unknown
//IL_0123: Unknown result type (might be due to invalid IL or missing references)
//IL_012d: Expected O, but got Unknown
//IL_0160: Unknown result type (might be due to invalid IL or missing references)
//IL_016a: Expected O, but got Unknown
cfgSecondaryLifestealPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Merc", "Secondary Lifesteal (%)", 20f, new ConfigDescription("How much of damage dealt is healed while Merc M2 (Uppercut/Whirlwind) is active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), Array.Empty<object>()));
cfgEvisBarrierPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Merc", "Evis Barrier Per Hit (%)", 3f, new ConfigDescription("Barrier gained per hit while Eviscerate is active, as % of max combined health.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
cfgFocusedAssaultDamagePercent = ((BaseUnityPlugin)this).Config.Bind<float>("Merc", "Focused Assault Damage (%)", 700f, new ConfigDescription("Focused Assault damage per dash hit (vanilla 700%). Example: 700 = 700%.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5000f), Array.Empty<object>()));
cfgFocusedAssaultCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Merc", "Focused Assault Cooldown (s)", 8f, new ConfigDescription("Focused Assault base cooldown in seconds (vanilla 8).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>()));
cfgEvisDamagePercent = ((BaseUnityPlugin)this).Config.Bind<float>("Merc", "Eviscerate Damage (%)", 110f, new ConfigDescription("Eviscerate damage per hit (vanilla 110%). Example: 110 = 110%.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5000f), Array.Empty<object>()));
cfgEvisCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Merc", "Eviscerate Cooldown (s)", 6f, new ConfigDescription("Eviscerate base cooldown in seconds (vanilla 6). Requires game restart after editing config file.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>()));
((BaseUnityPlugin)this).Config.SettingChanged += OnAnyConfigSettingChanged;
try
{
_inRiskOfOptionsSetup = true;
ModSettingsManager.SetModDescription("Merc skill effects.\n\nThis mod is configured via the BepInEx config file only.\nValues shown here are read-only to avoid confusing clients.");
AddReadOnlySlider(cfgSecondaryLifestealPercent, 0f, 100f, 1f);
AddReadOnlySlider(cfgEvisBarrierPercent, 0f, 100f, 0.5f);
AddReadOnlySlider(cfgFocusedAssaultDamagePercent, 0f, 5000f, 50f);
AddReadOnlySlider(cfgFocusedAssaultCooldownSeconds, 0f, 30f, 0.5f);
AddReadOnlySlider(cfgEvisDamagePercent, 0f, 5000f, 10f);
AddReadOnlySlider(cfgEvisCooldownSeconds, 0f, 30f, 0.5f);
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("[MercExtraEffects] RiskOfOptions UI not available: " + ex.Message));
}
finally
{
_inRiskOfOptionsSetup = false;
}
}
private void AddReadOnlySlider(ConfigEntry<float> entry, float min, float max, float increment)
{
//IL_0002: 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_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Expected O, but got Unknown
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Expected O, but got Unknown
ModSettingsManager.AddOption((BaseOption)new StepSliderOption(entry, new StepSliderConfig
{
min = min,
max = max,
increment = increment
}));
}
private void OnAnyConfigSettingChanged(object sender, SettingChangedEventArgs e)
{
if (_suppressSettingChanged || e == null || e.ChangedSetting == null)
{
return;
}
try
{
if ((e.ChangedSetting == cfgSecondaryLifestealPercent || e.ChangedSetting == cfgEvisBarrierPercent || e.ChangedSetting == cfgFocusedAssaultDamagePercent || e.ChangedSetting == cfgFocusedAssaultCooldownSeconds || e.ChangedSetting == cfgEvisDamagePercent || e.ChangedSetting == cfgEvisCooldownSeconds) && !_inRiskOfOptionsSetup)
{
_suppressSettingChanged = true;
((BaseUnityPlugin)this).Config.Reload();
((BaseUnityPlugin)this).Logger.LogInfo((object)"[MercExtraEffects] In-game config change detected and reverted. Edit the config file + restart to apply.");
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("[MercExtraEffects] Failed to revert in-game config change: " + ex));
}
finally
{
_suppressSettingChanged = false;
}
}
private static bool IsMerc(CharacterBody body)
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
return (Object)(object)body != (Object)null && body.bodyIndex == mercBodyIndex;
}
private void OnBodyStart(CharacterBody body)
{
if (!((Object)(object)body == (Object)null) && (Object)(object)((Component)body).GetComponent<MercTracker>() == (Object)null)
{
((Component)body).gameObject.AddComponent<MercTracker>();
}
}
private static MercTracker GetTracker(CharacterBody body)
{
return ((Object)(object)body != (Object)null) ? ((Component)body).GetComponent<MercTracker>() : null;
}
private void Uppercut_OnEnter(orig_OnEnter orig, Uppercut self)
{
orig.Invoke(self);
CharacterBody characterBody = ((EntityState)self).characterBody;
if (IsMerc(characterBody))
{
MercTracker tracker = GetTracker(characterBody);
if ((Object)(object)tracker != (Object)null)
{
tracker.uppercutActive = true;
}
}
}
private void Uppercut_OnExit(orig_OnExit orig, Uppercut self)
{
CharacterBody characterBody = ((EntityState)self).characterBody;
if (IsMerc(characterBody))
{
MercTracker tracker = GetTracker(characterBody);
if ((Object)(object)tracker != (Object)null)
{
tracker.uppercutActive = false;
}
}
orig.Invoke(self);
}
private void WhirlwindBase_OnEnter(orig_OnEnter orig, WhirlwindBase self)
{
orig.Invoke(self);
CharacterBody characterBody = ((EntityState)self).characterBody;
if (IsMerc(characterBody))
{
MercTracker tracker = GetTracker(characterBody);
if ((Object)(object)tracker != (Object)null)
{
tracker.whirlwindActive = true;
}
}
}
private void WhirlwindBase_OnExit(orig_OnExit orig, WhirlwindBase self)
{
CharacterBody characterBody = ((EntityState)self).characterBody;
if (IsMerc(characterBody))
{
MercTracker tracker = GetTracker(characterBody);
if ((Object)(object)tracker != (Object)null)
{
tracker.whirlwindActive = false;
}
}
orig.Invoke(self);
}
private void Evis_OnEnter(orig_OnEnter orig, Evis self)
{
orig.Invoke(self);
CharacterBody characterBody = ((EntityState)self).characterBody;
if (IsMerc(characterBody))
{
MercTracker tracker = GetTracker(characterBody);
if ((Object)(object)tracker != (Object)null)
{
tracker.evisActive = true;
}
}
}
private void Evis_OnExit(orig_OnExit orig, Evis self)
{
CharacterBody characterBody = ((EntityState)self).characterBody;
if (IsMerc(characterBody))
{
MercTracker tracker = GetTracker(characterBody);
if ((Object)(object)tracker != (Object)null)
{
tracker.evisActive = false;
}
}
orig.Invoke(self);
}
private void FocusedAssaultDash_AuthorityModifyOverlapAttack(orig_AuthorityModifyOverlapAttack orig, FocusedAssaultDash self, OverlapAttack overlapAttack)
{
orig.Invoke(self, overlapAttack);
try
{
if (self != null && overlapAttack != null)
{
float num = Mathf.Clamp(cfgFocusedAssaultDamagePercent.Value, 0f, 5000f) / 100f;
overlapAttack.damage = ((BaseState)self).damageStat * num;
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("[MercExtraEffects] FocusedAssaultDash_AuthorityModifyOverlapAttack error: " + ex));
}
}
private void OnServerDamageDealt(DamageReport report)
{
//IL_00d0: 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)
try
{
if (report == null)
{
return;
}
CharacterBody attackerBody = report.attackerBody;
if (!IsMerc(attackerBody))
{
return;
}
MercTracker tracker = GetTracker(attackerBody);
if ((Object)(object)tracker == (Object)null)
{
return;
}
HealthComponent healthComponent = attackerBody.healthComponent;
if (!Object.op_Implicit((Object)(object)healthComponent))
{
return;
}
float damageDealt = report.damageDealt;
if (damageDealt <= 0f)
{
return;
}
if (tracker.uppercutActive || tracker.whirlwindActive)
{
float num = Mathf.Clamp(cfgSecondaryLifestealPercent.Value, 0f, 200f) / 100f;
float num2 = damageDealt * num;
if (num2 > 0f)
{
healthComponent.Heal(num2, default(ProcChainMask), true);
}
}
if (tracker.evisActive)
{
float num3 = Mathf.Clamp(cfgEvisBarrierPercent.Value, 0f, 100f) / 100f;
float num4 = healthComponent.fullCombinedHealth * num3;
if (num4 > 0f)
{
healthComponent.AddBarrier(num4);
}
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("[MercExtraEffects] OnServerDamageDealt error: " + ex));
}
}
private void TrySetMercUtilityCooldownFromBodyPrefab(float cooldownSeconds)
{
try
{
GameObject val = BodyCatalog.FindBodyPrefab("MercBody");
if ((Object)(object)val == (Object)null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"[MercExtraEffects] Could not find MercBody prefab to set utility cooldown.");
return;
}
SkillLocator component = val.GetComponent<SkillLocator>();
if ((Object)(object)component == (Object)null || (Object)(object)component.utility == (Object)null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"[MercExtraEffects] MercBody SkillLocator or utility slot not found.");
return;
}
SkillDef skillDef = component.utility.skillDef;
if ((Object)(object)skillDef == (Object)null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"[MercExtraEffects] Merc utility SkillDef not found.");
return;
}
float baseRechargeInterval = skillDef.baseRechargeInterval;
skillDef.baseRechargeInterval = cooldownSeconds;
((BaseUnityPlugin)this).Logger.LogInfo((object)("[MercExtraEffects] Utility cooldown changed: " + baseRechargeInterval + " -> " + skillDef.baseRechargeInterval));
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("[MercExtraEffects] Failed to set utility cooldown: " + ex));
}
}
private void ApplySkillTuning()
{
ApplyFocusedAssaultCooldown();
ApplyEvisDamageCoefficient();
ApplyEvisCooldown();
}
private void ApplyFocusedAssaultCooldown()
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
try
{
float baseRechargeInterval = Mathf.Clamp(cfgFocusedAssaultCooldownSeconds.Value, 0f, 60f);
SkillDef val = Addressables.LoadAssetAsync<SkillDef>((object)"RoR2/Base/Merc/MercBodyFocusedAssault.asset").WaitForCompletion();
if ((Object)(object)val == (Object)null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"[MercExtraEffects] Focused Assault SkillDef not found at: RoR2/Base/Merc/MercBodyFocusedAssault.asset");
return;
}
float baseRechargeInterval2 = val.baseRechargeInterval;
val.baseRechargeInterval = baseRechargeInterval;
((BaseUnityPlugin)this).Logger.LogInfo((object)("[MercExtraEffects] Focused Assault cooldown changed: " + baseRechargeInterval2 + " -> " + val.baseRechargeInterval));
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("[MercExtraEffects] Failed to set Focused Assault cooldown: " + ex));
}
}
private void ApplyEvisDamageCoefficient()
{
try
{
float value = Mathf.Clamp(cfgEvisDamagePercent.Value, 0f, 5000f) / 100f;
if (!TrySetStaticFloat(typeof(Evis), "damageCoefficient", value))
{
TrySetStaticFloat(typeof(Evis), "baseDamageCoefficient", value);
TrySetStaticFloat(typeof(Evis), "damageCoefficientPerHit", value);
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("[MercExtraEffects] Failed to set Evis damage coefficient: " + ex));
}
}
private void ApplyEvisCooldown()
{
//IL_0034: 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)
try
{
float baseRechargeInterval = Mathf.Clamp(cfgEvisCooldownSeconds.Value, 0f, 60f);
SkillDef val = null;
string[] evisSkillDefPaths = EvisSkillDefPaths;
foreach (string text in evisSkillDefPaths)
{
try
{
val = Addressables.LoadAssetAsync<SkillDef>((object)text).WaitForCompletion();
if ((Object)(object)val != (Object)null)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("[MercExtraEffects] Found Evis SkillDef at: " + text));
break;
}
}
catch
{
}
}
if ((Object)(object)val == (Object)null)
{
val = FindMercSpecialSkillDefByState(typeof(EvisDash)) ?? FindMercSpecialSkillDefByState(typeof(Evis));
}
if ((Object)(object)val == (Object)null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"[MercExtraEffects] Could not find Eviscerate SkillDef to set cooldown. (Tried addressables + MercBody scan)");
return;
}
float baseRechargeInterval2 = val.baseRechargeInterval;
val.baseRechargeInterval = baseRechargeInterval;
((BaseUnityPlugin)this).Logger.LogInfo((object)("[MercExtraEffects] Eviscerate cooldown changed: " + baseRechargeInterval2 + " -> " + val.baseRechargeInterval));
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("[MercExtraEffects] Failed to set Eviscerate cooldown: " + ex));
}
}
private SkillDef FindMercSpecialSkillDefByState(Type targetStateType)
{
try
{
GameObject val = BodyCatalog.FindBodyPrefab("MercBody");
if ((Object)(object)val == (Object)null)
{
return null;
}
SkillLocator component = val.GetComponent<SkillLocator>();
if ((Object)(object)component == (Object)null || (Object)(object)component.special == (Object)null)
{
return null;
}
SkillDef skillDef = component.special.skillDef;
if ((Object)(object)skillDef == (Object)null)
{
return null;
}
if (((SerializableEntityStateType)(ref skillDef.activationState)).stateType == targetStateType)
{
return skillDef;
}
return null;
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("[MercExtraEffects] FindMercSpecialSkillDefByState error: " + ex.Message));
return null;
}
}
private bool TrySetStaticFloat(Type type, string fieldName, float value)
{
try
{
FieldInfo field = type.GetField(fieldName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (field == null)
{
return false;
}
if (field.FieldType != typeof(float))
{
return false;
}
float num = (float)field.GetValue(null);
field.SetValue(null, value);
((BaseUnityPlugin)this).Logger.LogInfo((object)$"[MercExtraEffects] {type.Name}.{fieldName} changed: {num} -> {value}");
return true;
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("[MercExtraEffects] Failed setting " + type.Name + "." + fieldName + ": " + ex.Message));
return false;
}
}
}
internal static class Log
{
private static ManualLogSource _logSource;
internal static void Init(ManualLogSource logSource)
{
_logSource = logSource;
}
internal static void Debug(object data)
{
_logSource.LogDebug(data);
}
internal static void Error(object data)
{
_logSource.LogError(data);
}
internal static void Fatal(object data)
{
_logSource.LogFatal(data);
}
internal static void Info(object data)
{
_logSource.LogInfo(data);
}
internal static void Message(object data)
{
_logSource.LogMessage(data);
}
internal static void Warning(object data)
{
_logSource.LogWarning(data);
}
}