protected override void Apply(AttributeLoader loader, object wrapperbj)
        {
            if (!(wrapperbj is StatusEffectAttributesWrapper w))
            {
                throw new System.InvalidCastException();
            }

            loader.ApplyPPatch(Duration, () => w.Duration, Fixed64.UnsafeFromDouble);
            loader.ApplyPMultPatch(DurationMult, () => w.Duration);

            loader.ApplyPPatch(MaxStacks, () => w.MaxStacks);
            loader.ApplyPMultPatch(MaxStacksMult, () => w.MaxStacks);

            loader.ApplyPPatch(Lifetime, () => w.Lifetime);
            loader.ApplyPPatch(WeaponFireTriggerEndEvent, () => w.WeaponFireTriggerEndEvent);
            loader.ApplyPPatch(StackingBehaviour, () => w.StackingBehaviour);

            if (BuffsToApplyToTarget != null)
            {
                var l = w.BuffsToApplyToTarget?.Select(x => new UnitTypeBuffWrapper(x)).ToList() ?? new List <UnitTypeBuffWrapper>();
                loader.ApplyLPatch(BuffsToApplyToTarget, l, () => new UnitTypeBuffWrapper(), "BuffsToApplyToTarget");
                w.BuffsToApplyToTarget = l.ToArray();
            }

            if (UnitTypeBuffsToApply != null)
            {
                var l = w.UnitTypeBuffsToApply?.Select(x => new UnitTypeBuffWrapper(x)).ToList() ?? new List <UnitTypeBuffWrapper>();
                loader.ApplyLPatch(UnitTypeBuffsToApply, l, () => new UnitTypeBuffWrapper(), "UnitTypeBuffsToApply");
                w.UnitTypeBuffsToApply = l.ToArray();
            }

            if (Modifiers != null)
            {
                var wrapperModifiers = w.Modifiers?.ToList() ?? new List <ModifierAttributes>();

                var parsed = new Dictionary <int, ModifierAttributesPatch>();

                foreach (var kvp in Modifiers)
                {
                    if (!int.TryParse(kvp.Key, out var index))
                    {
                        loader.logger.Log($"ERROR: Non-integer key: {kvp.Key}");
                        break;
                    }

                    parsed[index] = kvp.Value;
                }

                var toRemove = new Stack <int>();

                foreach (var kvp in parsed.OrderBy(p => p.Key))
                {
                    var index        = kvp.Key;
                    var elementPatch = kvp.Value;

                    using (loader.logger.BeginScope($"Modifiers: {index}"))
                    {
                        var remove = elementPatch.Remove;

                        ModifierAttributes newValue;

                        if (index < wrapperModifiers.Count())
                        {
                            if (remove)
                            {
                                loader.logger.Log("(removed)");
                                toRemove.Push(index);
                                continue;
                            }

                            newValue = wrapperModifiers[index];
                        }
                        else if (index >= wrapperModifiers.Count())
                        {
                            if (remove)
                            {
                                loader.logger.Log("WARNING: Remove flag set for non-existent entry");
                                continue;
                            }

                            loader.logger.BeginScope("(created)").Dispose();

                            newValue = new ModifierAttributes();
                        }
                        else                         // if (index > wrapperModifiers.Count)
                        {
                            loader.logger.Log("ERROR: Non-consecutive index");
                            continue;
                        }

                        loader.ApplyPRefPatch(elementPatch.ModifierType, ref newValue.ModifierType, "ModifierType");
                        loader.ApplyPRefPatch(elementPatch.EnableWeapon_WeaponID, ref newValue.EnableWeaponAttributes.WeaponID, "EnableWeapon_WeaponID");
                        loader.ApplyPRefPatch(elementPatch.SwapFromWeaponID, ref newValue.SwapWeaponAttributes.SwapFromWeaponID, "SwapFromWeaponID");
                        loader.ApplyPRefPatch(elementPatch.SwapToWeaponID, ref newValue.SwapWeaponAttributes.SwapToWeaponID, "SwapToWeaponID");

                        if (elementPatch.HealthOverTimeAttributes != null)
                        {
                            using (loader.logger.BeginScope("HealthOverTimeAttributes:"))
                            {
                                loader.ApplyPRefPatch(elementPatch.HealthOverTimeAttributes.ID, ref newValue.HealthOverTimeAttributes.ID, "ID");
                                loader.ApplyPRefPatch(elementPatch.HealthOverTimeAttributes.Amount, ref newValue.HealthOverTimeAttributes.Amount, "Amount");
                                loader.ApplyPRefPatch(elementPatch.HealthOverTimeAttributes.MSTickDuration, ref newValue.HealthOverTimeAttributes.MSTickDuration, "MSTickDuration");
                                loader.ApplyPRefPatch(elementPatch.HealthOverTimeAttributes.DamageType, ref newValue.HealthOverTimeAttributes.DamageType, "DamageType");
                            }
                        }

                        if (index < wrapperModifiers.Count)
                        {
                            wrapperModifiers[index] = newValue;
                        }
                        else
                        {
                            wrapperModifiers.Add(newValue);
                        }
                    }
                }

                foreach (var v in toRemove)
                {
                    wrapperModifiers.RemoveAt(v);
                }

                w.Modifiers = wrapperModifiers.ToArray();
            }
        }
        protected override void Apply(AttributeLoader loader, object wrapperObj)
        {
            if (!(wrapperObj is AbilityViewAttributes w))
            {
                throw new System.InvalidCastException();
            }

            loader.ApplyPRefPatch(LocalizedTitleStringID, ref w.LocalizedTitleStringID, "LocalizedTitleStringID");
            loader.ApplyPRefPatch(LocalizedShortDescriptionStringID, ref w.LocalizedShortDescriptionStringID, "LocalizedShortDescriptionStringID");
            loader.ApplyPRefPatch(LocalizedLongDescriptionStringID, ref w.LocalizedLongDescriptionStringID, "LocalizedLongDescriptionStringID");
            loader.ApplyPRefPatch(LocalizedToggledTitleStringID, ref w.LocalizedToggledTitleStringID, "LocalizedToggledTitleStringID");
            loader.ApplyPRefPatch(LocalizedToggledShortDescriptionStringID, ref w.LocalizedToggledShortDescriptionStringID, "LocalizedToggledShortDescriptionStringID");
            loader.ApplyPRefPatch(LocalizedToggledLongDescriptionStringID, ref w.LocalizedToggledLongDescriptionStringID, "LocalizedToggledLongDescriptionStringID");

            loader.ApplyPRefPatch(IconName, ref w.IconName, "IconName");
            loader.ApplyPRefPatch(ToggledIconName, ref w.ToggledIconName, "ToggledIconName");

            loader.ApplyPRefPatch(Hotkey, ref w.Hotkey, "Hotkey");
            loader.ApplyPRefPatch(ToggledHotkey, ref w.ToggledHotkey, "ToggledHotkey");

            loader.ApplyPRefPatch(BindingIsExclusive, ref w.BindingIsExclusive, "BindingIsExclusive");
            loader.ApplyPRefPatch(AutocastHotkeyModifier, ref w.AutocastHotkeyModifier, "AutocastHotkeyModifier");
            loader.ApplyPRefPatch(DrawPreviewRouteLine, ref w.DrawPreviewRouteLine, "DrawPreviewRouteLine");
            loader.ApplyPRefPatch(ForceHideAutocastSprite, ref w.ForceHideAutocastSprite, "ForceHideAutocastSprite");
            loader.ApplyPRefPatch(ForcePassiveVisuals, ref w.ForcePassiveVisuals, "ForcePassiveVisuals");
            loader.ApplyPRefPatch(WillToggleDialIndicator, ref w.WillToggleDialIndicator, "WillToggleDialIndicator");
            loader.ApplyPRefPatch(InventoryID, ref w.InventoryID, "InventoryID");
            loader.ApplyPRefPatch(InvertFillCooldown, ref w.InvertFillCooldown, "InvertFillCooldown");
        }