/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ using System; using System.Linq; [Inspectable] public class StatModifierData : BehaviorData { [Inspectable, LegacyPayloadKey(1)] public StatReference Stat = new StatReference(); [Inspectable, LegacyPayloadKey(2)] public PerformanceStat Modifier = new PerformanceStat(); [Inspectable, LegacyPayloadKey(3)] public StatModifierType Type; [InspectableType(typeof(BehaviorData)), LegacyPayloadKey(4)] public string RequireBehavior; public override Behavior CreateInstance(EquippedItem item) { return new StatModifier(this, item); } public override Behavior CreateInstance(ConsumableItemEffect consumable) { return new StatModifier(this, consumable); } } [Order(-4)] public class StatModifier : Behavior, IInitializableBehavior, IDisposable, IAlwaysUpdatedBehavior { private StatModifierData _data; private PerformanceStat[] _stats; private bool _applied; private bool _executed; public StatModifier(StatModifierData data, EquippedItem item) : base(data, item) { _data = data; } public StatModifier(StatModifierData data, ConsumableItemEffect item) : base(data, item) { _data = data; } public void Initialize() { _stats = Entity.Equipment .Select(hp => hp.EquippableItem) .Where(HasRequiredBehavior) .SelectMany(gear => ItemManager.GetRuntimeBehaviorConfigs(gear)) .Where(behavior => BehaviorKindMatches(behavior.Kind, _data.Stat.Target)) .Select(FindTargetStat) .Where(stat => stat != null) .ToArray(); } private PerformanceStat FindTargetStat(BehaviorData behavior) { var statField = behavior .GetType() .GetFields() .Where(f => f.FieldType == typeof(PerformanceStat)) .FirstOrDefault(f => f.Name == _data.Stat.Stat); return statField?.GetValue(behavior) as PerformanceStat; } private static bool BehaviorKindMatches(string runtimeKind, string expectedKind) { if (string.IsNullOrWhiteSpace(expectedKind)) { return false; } if (string.Equals(runtimeKind, expectedKind, StringComparison.Ordinal)) { return true; } const string legacyDataSuffix = "Data"; return expectedKind.EndsWith(legacyDataSuffix, StringComparison.Ordinal) && string.Equals(runtimeKind, expectedKind.Substring(0, expectedKind.Length - legacyDataSuffix.Length), StringComparison.Ordinal); } private bool HasRequiredBehavior(EquippableItem gear) { return string.IsNullOrWhiteSpace(_data.RequireBehavior) || ItemManager.GetRuntimeBehaviorConfigs(gear) .Any(behavior => BehaviorKindMatches(behavior.Kind, _data.RequireBehavior)); } private void ApplyModifier() { _applied = true; foreach (var stat in _stats) (_data.Type == StatModifierType.Constant ? stat.GetConstantModifiers(Entity) : stat.GetScaleModifiers(Entity))[this] = Evaluate(_data.Modifier); } private void RemoveModifier() { _applied = false; foreach (var stat in _stats) (_data.Type == StatModifierType.Constant ? stat.GetConstantModifiers(Entity) : stat.GetScaleModifiers(Entity)).Remove(this); } public override bool Execute(float dt) { _executed = true; return true; } public void Dispose() { if(_applied) RemoveModifier(); } public void Update(float delta) { if(_executed && !_applied) ApplyModifier(); if(!_executed && _applied) RemoveModifier(); _executed = false; } } public enum StatModifierType { Constant, Multiplier } [Inspectable] public class StatReference { [InspectableType(typeof(BehaviorData)), LegacyPayloadKey(1)] public string Target; [Inspectable, LegacyPayloadKey(2)] public string Stat; }