public override void WriteNewHooks()
            {
                bool runStockScaling = FireboltAttackSpeedStockScaling.Value &&
                                       FireboltAttackSpeedStockScalingCoefficient.IsNotDefault();

                bool runCooldownScaling = FireboltAttackSpeedCooldownScaling.Value &&
                                          FireboltAttackSpeedCooldownScalingCoefficient.IsNotDefault();

                float cooldownCoeff = FireboltAttackSpeedCooldownScalingCoefficient.FloatValue;
                float stockCoeff    = FireboltAttackSpeedStockScalingCoefficient.FloatValue;

                if (runStockScaling || runCooldownScaling)
                {
                    IL.RoR2.CharacterBody.RecalculateStats += il =>
                    {
                        ILCursor c = new ILCursor(il);
                        c.GotoNext(x =>
                                   x.MatchRet()
                                   );

                        c.Emit(OpCodes.Ldarg_0);
                        c.Emit <CharacterBody>(OpCodes.Ldfld, "skillLocator");
                        c.Emit(OpCodes.Ldloc_S, (byte)45);
                        c.Emit(OpCodes.Ldloc_S, (byte)42);
                        c.EmitDelegate <Action <SkillLocator, float, float> >(
                            (skillLocator, cooldownScale, attackSpeed) =>
                        {
                            GenericSkill primary = skillLocator != null ? skillLocator.primary : null;
                            if (primary != null && primary.skillName == "FireFirebolt")
                            {
                                if (runCooldownScaling)
                                {
                                    float val = cooldownScale *
                                                (1 / (1 + cooldownCoeff * (attackSpeed - 1)));
                                    primary.cooldownScale = val;
                                }

                                if (runStockScaling)
                                {
                                    primary.SetBonusStockFromBody(
                                        (int)((attackSpeed - 1) * stockCoeff * primary.baseMaxStock));
                                }
                            }
                        });
                    };
                }


                On.EntityStates.Mage.Weapon.Flamethrower.OnEnter += (orig, self) =>
                {
                    Type flamethrowerType = self.GetType();

                    GameObject go = typeof(EntityState)
                                    .GetProperty("gameObject", BindingFlags.NonPublic | BindingFlags.Instance)
                                    ?.GetValue(self) as GameObject;

                    CharacterBody body = go.GetComponent <CharacterBody>();

                    if (FlamethrowerTickFrequencyScaleWithAttackSpeed.Value &&
                        FlamethrowerTickFrequencyScaleCoefficient.IsNotDefault())
                    {
                        float baseVal = FlamethrowerTickFrequency.ValueConfigWrapper.IsNotDefault()
                            ? FlamethrowerTickFrequency.ValueConfigWrapper.FloatValue
                            : VanillaFlamethrowerTickFrequency;

                        float val = baseVal + (body.attackSpeed - 1) *
                                    FlamethrowerTickFrequencyScaleCoefficient.FloatValue * baseVal
                        ;

                        flamethrowerType.SetFieldValue("tickFrequency",
                                                       val
                                                       );
                    }

                    if (FlamethrowerDurationScaleDownWithAttackSpeed.Value &&
                        FlamethrowerDurationScaleCoefficient.IsNotDefault())
                    {
                        float baseVal = FlamethrowerDuration.ValueConfigWrapper.IsNotDefault()
                            ? FlamethrowerDuration.ValueConfigWrapper.FloatValue
                            : VanillaFlamethrowerDuration;

                        float val = baseVal - (body.attackSpeed - 1) *
                                    FlamethrowerDurationScaleCoefficient.FloatValue *
                                    baseVal;

                        if (val < FlamethrowerMinimalDuration.FloatValue)
                        {
                            val = FlamethrowerMinimalDuration.FloatValue;
                        }

                        flamethrowerType.SetFieldValue("baseFlamethrowerDuration",
                                                       val
                                                       );
                    }


                    orig(self);
                };
            }
            public override void OverrideGameValues()
            {
                MineMaxDeployCount.SetDefaultValue(10);
                TurretMaxDeployCount.SetDefaultValue(2);
                ShieldMaxDeployCount.SetDefaultValue(1);

                if (MineMaxDeployCount.IsNotDefault() || TurretMaxDeployCount.IsNotDefault() ||
                    ShieldMaxDeployCount.IsNotDefault())
                {
                    IL.RoR2.CharacterMaster.AddDeployable += il =>
                    {
                        var c = new ILCursor(il).Goto(0);

                        c.GotoNext(
                            x => x.MatchStloc(1),
                            x => x.MatchLdarg(0)
                            );
                        c.Index      += 1;
                        c.Next.OpCode = OpCodes.Nop;
                        c.Index      += 1;
                        c.Emit(OpCodes.Ldloc_1);
                        c.Emit(OpCodes.Ldarg_0);
                        c.Emit(OpCodes.Ldarg_2);


                        c.EmitDelegate <Func <int, CharacterMaster, DeployableSlot, int> >((maxDeploy, self, slot) =>
                        {
                            switch (slot)
                            {
                            case DeployableSlot.EngiMine:
                                if (MineMaxDeployCount.IsNotDefault())
                                {
                                    maxDeploy = MineMaxDeployCount.Value;
                                }

                                break;

                            case DeployableSlot.EngiTurret:
                                if (TurretMaxDeployCount.IsNotDefault())
                                {
                                    maxDeploy = TurretMaxDeployCount.Value;
                                }

                                break;

                            case DeployableSlot.EngiBubbleShield:
                                if (ShieldMaxDeployCount.IsNotDefault())
                                {
                                    maxDeploy = ShieldMaxDeployCount.Value;
                                }

                                break;
                            }

                            return(maxDeploy);
                        });
                        c.Emit(OpCodes.Stloc_1);
                        c.Emit(OpCodes.Ldarg_0);
                    };
                }


                // Workaround for more than 8 max grenades
                if (GrenadeMinFireAmount.ValueConfigWrapper.Value >= 8 ||
                    GrenadeMaxFireAmount.ValueConfigWrapper.Value >= 8)
                {
                    On.EntityStates.Engi.EngiWeapon.FireGrenades.OnEnter += (orig, self) =>
                    {
                        Assembly assembly     = self.GetType().Assembly;
                        Type     fireGrenades = assembly.GetClass("EntityStates.Engi.EngiWeapon", "FireGrenades");

                        orig(self);
                        self.SetFieldValue("duration",
                                           fireGrenades.GetFieldValue <float>("baseDuration")
                                           * self.GetFieldValue <int>("grenadeCountMax") / 8f
                                           / self.GetFieldValue <float>("attackSpeedStat")
                                           );
                    };
                }

                // typeof(RoR2Application).Assembly doesn't seem to work
                On.RoR2.RoR2Application.Start += (orig, self) =>
                {
                    orig(self);
                    Assembly assembly = self.GetType().Assembly;

                    Type shieldDeployed = typeof(EntityStates.Engi.EngiBubbleShield.Deployed);

                    ShieldDeployedFields.ForEach(changer => changer.Apply(shieldDeployed));
                    if (ShieldEndlessDuration.Value)
                    {
                        EntityStates.Engi.EngiBubbleShield.Deployed.lifetime = float.PositiveInfinity;
                    }

                    Type chargeGrenades = assembly.GetClass("EntityStates.Engi.EngiWeapon", "ChargeGrenades");

                    ChargeGrenadesFields.ForEach(changer => changer.Apply(chargeGrenades));

                    if (GrenadeSetChargeCountToFireAmount.Value &&
                        GrenadeMaxFireAmount.ValueConfigWrapper.IsNotDefault())
                    {
                        chargeGrenades.SetFieldValue("maxCharges", GrenadeMaxFireAmount.ValueConfigWrapper.Value);
                    }
                };
            }
            public override void WriteNewHooks()
            {
                if (BarrageScalesWithAttackSpeed.Value && BarrageScaleModifier.IsNotDefault())
                {
                    On.EntityStates.Commando.CommandoWeapon.FireBarrage.OnEnter += (orig, self) =>
                    {
                        orig(self);

                        Assembly assembly = self.GetType().Assembly;

                        Type      fireBarr    = assembly.GetClass("EntityStates.Commando.CommandoWeapon", "FireBarrage");
                        FieldInfo attackSpeed = typeof(BaseState).GetField("attackSpeedStat",
                                                                           BindingFlags.NonPublic | BindingFlags.Instance);

                        FieldInfo durationBetweenShots = fireBarr.GetField("durationBetweenShots",
                                                                           BindingFlags.NonPublic | BindingFlags.Instance);

                        float attackSpeedF = (float)attackSpeed?.GetValue(self);

                        int baseShot = BarrageBaseShotAmount.ValueConfigWrapper.IsDefault()
                            ? VanillaBarrageBaseShotAmount
                            : BarrageBaseShotAmount.ValueConfigWrapper.Value;

                        durationBetweenShots?.SetValue(self,
                                                       (BarrageBaseDurationBetweenShots.ValueConfigWrapper.IsDefault()
                                ? VanillaBarrageBaseDurationBetweenShots
                                : BarrageBaseDurationBetweenShots.ValueConfigWrapper.FloatValue) / attackSpeedF /
                                                       BarrageScaleModifier.FloatValue);

                        fireBarr.SetFieldValue("bulletCount",
                                               baseShot + (int)((attackSpeedF - 1) * BarrageScaleModifier.FloatValue * baseShot));
                    };
                }


                On.EntityStates.Commando.DodgeState.OnEnter += (orig, self) =>
                {
                    orig(self);

                    if (DashResetsSecondCooldown.Value)
                    {
                        Assembly assembly = self.GetType().Assembly;

                        Type entityState = assembly.GetClass("EntityStates", "EntityState");

                        SkillLocator locator = (SkillLocator)entityState
                                               .GetProperty("skillLocator", BindingFlags.NonPublic | BindingFlags.Instance)
                                               ?.GetValue(self, null);

                        GenericSkill skill2 = locator.GetSkill(SkillSlot.Secondary);
                        skill2.Reset();
                    }

                    if (DashInvulnerability.Value)
                    {
                        if (DashInvulnerabilityTimer.IsDefault())
                        {
                            Transform transform = self.InvokeMethod <Transform>("GetModelTransform");

                            HurtBoxGroup hurtBoxGroup = transform.GetComponent <HurtBoxGroup>();
                            ++hurtBoxGroup.hurtBoxesDeactivatorCounter;
                        }
                        else
                        {
                            self.outer.commonComponents.characterBody.AddTimedBuff(BuffIndex.HiddenInvincibility,
                                                                                   DashInvulnerabilityTimer.FloatValue);
                        }
                    }
                };

                On.EntityStates.Commando.DodgeState.OnExit += (orig, self) =>
                {
                    if (DashInvulnerability.Value && DashInvulnerabilityTimer.IsDefault())
                    {
                        Transform transform = self.InvokeMethod <Transform>("GetModelTransform");

                        HurtBoxGroup hurtBoxGroup = transform.GetComponent <HurtBoxGroup>();
                        --hurtBoxGroup.hurtBoxesDeactivatorCounter;
                    }

                    orig(self);
                };


                if (PistolHitLowerBarrageCooldown.Value && PistolHitLowerBarrageCooldownPercent.IsNotDefault())
                {
                    Type      gsType            = typeof(GenericSkill);
                    FieldInfo rechargeStopwatch = gsType.GetField("rechargeStopwatch",
                                                                  BindingFlags.NonPublic | BindingFlags.Instance);
                    FieldInfo finalRechargeInterval = gsType.GetField("finalRechargeInterval",
                                                                      BindingFlags.NonPublic | BindingFlags.Instance);

                    IL.EntityStates.Commando.CommandoWeapon.FirePistol2.FireBullet += il =>
                    {
                        ILCursor c = new ILCursor(il);

                        c.GotoNext(x => x.MatchCallvirt(typeof(RoR2.BulletAttack).FullName, "Fire"));
                        c.EmitDelegate <Func <BulletAttack, BulletAttack> >((BulletAttack ba) =>
                        {
                            ba.hitCallback = (ref BulletAttack.BulletHit info) =>
                            {
                                bool result = ba.DefaultHitCallback(ref info);
                                if (info.entityObject?.GetComponent <HealthComponent>())
                                {
                                    SkillLocator skillLocator = ba.owner.GetComponent <SkillLocator>();
                                    GenericSkill special      = skillLocator.special;
                                    rechargeStopwatch.SetValue(special,
                                                               (float)rechargeStopwatch.GetValue(special) +
                                                               (float)finalRechargeInterval.GetValue(special) *
                                                               PistolHitLowerBarrageCooldownPercent.FloatValue);
                                }

                                return(result);
                            };
                            return(ba);
                        });
                    };
                }
            }