using System; using System.Collections.Generic; using ACE.Entity; using ACE.Entity.Enum; using ACE.Entity.Enum.Properties; using System.Numerics; namespace ACE.Server.Entity { partial class Spell { // ====================================== // SpellBase fields - from the client DAT // ====================================== /// /// The spell ID /// public uint Id { get => _spellBase.MetaSpellId; } /// /// The spell name /// public string Name { get => _spellBase.Name; } /// /// The spell description that appears in client /// public string Description { get => _spellBase.Desc; } /// /// The magic school this spell belongs to /// public MagicSchool School { get => _spellBase.School; } /// /// The spell icon ID for display in client /// public uint IconID { get => _spellBase.Icon; } /// /// Used for spell stacking, ie. Strength Self I and Strength Self VI will be the same category /// public SpellCategory Category { get => _spellBase.Category; } /// /// bit flags for the spell /// public SpellFlags Flags { get => (SpellFlags)_spellBase.Bitfield; } /// /// The base mana cost required for casting the spell /// public uint BaseMana { get => _spellBase.BaseMana; } /// /// The base maximum distance for casting the spell /// public float BaseRangeConstant { get => _spellBase.BaseRangeConstant; } /// /// An additive multiplier to BaseRangeConstant /// based on caster's skill level /// public float BaseRangeMod { get => _spellBase.BaseRangeMod; } /// /// The difficulty of casting the spell /// public uint Power { get => _spellBase.Power; } /// /// Returns the minimum spell power required for some calculations, /// such as mana conversion and proficiency points /// public uint PowerMod { get => Math.Max(Power, 25); } /// /// The modifier for the original spell economy /// A legacy of a bygone era /// public float SpellEconomyMod { get => _spellBase.SpellEconomyMod; } /// /// The version # of the spell formula /// public uint FormulaVersion { get => _spellBase.FormulaVersion; } /// /// The burn rate for casting the spell /// public float ComponentLoss { get => _spellBase.ComponentLoss; } /// /// A subtype for the spell /// public SpellType MetaSpellType { get => _spellBase.MetaSpellType; } /// /// The amount of time the spell lasts /// usually for EnchantmentSpell / FellowshipEnchantmentSpells /// /// If spell has DotDuration, this will return DotDuration + 5 /// This is to handle the odd way retail did DoT ticks /// In retail, if a DoT had a duration of 15 seconds, /// it would actually perform 4 ticks, instead of 3 /// Presumably DoT ticks happened on heartbeats, which are every 5 seconds by default /// So there could be additional complication here if the target's Heartbeat is something other than 5 /// This also affects the value that is sent over the wire, which could be further investigated /// public double Duration { get { if (_spell?.DotDuration != null) { if (HasExtraTick) return _spell.DotDuration.Value + 5.0f; else return _spell.DotDuration.Value; } else return _spellBase.Duration; } } /// /// The DoT (damage over time) duration for the spell /// public double DotDuration { get => _spell.DotDuration ?? 0; } /// /// Returns the number of ticks for a DoT spell /// /// The PropertyFloat.HeartbeatInterval of the Creature with the enchantment public int GetNumTicks(float heartbeat = 5.0f) { if (DotDuration == 0) return 0; // see explanation of Duration, and why it sets Duration to DotDuration + 5 // For spell like Surge of Regeneration, which is listed as 19 seconds, // this would be the equivalent of 20 seconds, ie. Ceil((19 + 5) / 5) = 5 ticks return (int)Math.Ceiling(Duration / heartbeat); } /// /// Returns the damage per tick for a DoT spell /// /// The PropertyFloat.HeartbeatInterval of the Creature with the enchantment public float GetDamagePerTick(float heartbeat = 5.0f) { if (DotDuration == 0) return 0; // normally we can just use the value in StatModVal for this, // however it assumes that heartbeat is 5 if (heartbeat == 5.0f) return StatModVal; // calculate the total damage w/ default 5 heartbeats // BaseIntensity could also maybe be used for this var totalDamage = StatModVal * GetNumTicks(); // divide totalDamage by the actual # of heartbeats var numTicks = GetNumTicks(heartbeat); return totalDamage / numTicks; } /// /// Unknown what this does? /// client visual FX possibly? /// public float DegradeModifier { get => _spellBase.DegradeModifier; } /// /// Unknown what this does? /// client visual FX possibly? /// public float DegradeLimit { get => _spellBase.DegradeLimit; } /// /// The duration for PortalSummon_SpellType /// public double PortalLifetime { get => _spellBase.PortalLifetime; } /// /// uint values correspond to the SpellComponentsTable /// private List _formula { get => _spellBase.Formula; } /// /// Effect that plays on the caster for this spell (ie. for buffs, protects, etc.) /// public PlayScript CasterEffect { get => (PlayScript)_spellBase.CasterEffect; } /// /// Effect that plays on the target for this spell (ie. for debuffs, vulns, etc.) /// public PlayScript TargetEffect { get => (PlayScript)_spellBase.TargetEffect; } /// /// is always zero - all spells have the same fizzle effect /// public PlayScript FizzleEffect { get => (PlayScript)_spellBase.FizzleEffect; } /// /// is always zero /// public double RecoveryInterval { get => _spellBase.RecoveryInterval; } /// /// is always zero /// public float RecoveryAmount { get => _spellBase.RecoveryAmount; } /// /// For sorting in the spell list in the client UI /// public uint DisplayOrder { get => _spellBase.DisplayOrder; } /// /// The allowed target types for this spell /// public ItemType NonComponentTargetType { get => (ItemType)_spellBase.NonComponentTargetType; } /// /// Additional mana cost per target (e.g. "Incantation of Acid Bane" Mana Cost = 80 + 14 per target) /// public uint ManaMod { get => _spellBase.ManaMod; } //================================== // Spell fields - from the server DB //================================== /// /// The stat modifier type, usually EnchantmentTypeFlags /// public EnchantmentTypeFlags StatModType { get => (EnchantmentTypeFlags)(_spell.StatModType ?? 0); } /// /// The stat modifier key, used for lookup in the enchantment registry /// public uint StatModKey { get => _spell.StatModKey ?? 0; } /// /// The amount to modify a stat /// public float StatModVal { get => _spell.StatModVal ?? 0.0f; } /// /// The damage type for this spell /// public DamageType DamageType { get => (DamageType)(_spell.EType ?? 0); } /// /// The base amount of damage for this spell /// public int BaseIntensity { get => _spell.BaseIntensity ?? 0; } public int MinDamage { get => BaseIntensity; } /// /// The maximum additional daamage for this spell /// public int Variance { get => _spell.Variance ?? 0; } public int MaxDamage { get => BaseIntensity + Variance; } /// /// The weenie class ID associated for this spell, ie. the projectile weenie class id /// public uint WeenieClassId { get => _spell.Wcid ?? 0; } public uint Wcid { get => _spell.Wcid ?? 0; } /// /// The total # of projectiles launched for this spell /// public int NumProjectiles { get => _spell.NumProjectiles ?? 0; } /// /// The maximum # of additional projectiles possibly launched /// public int NumProjectilesVariance { get => _spell.NumProjectilesVariance ?? 0; } /// /// The total angle for multi-projectile spells, /// ie. 90 degrees for 3-5 projectiles, or 360 degrees for ring spells /// public float SpreadAngle { get => _spell.SpreadAngle ?? 0; } /// /// The vertical angle to launch this spell /// public float VerticalAngle { get => _spell.VerticalAngle ?? 0; } /// /// The default angle to launch this spell /// (relative to player, or global?) /// public float DefaultLaunchAngle { get => _spell.DefaultLaunchAngle ?? 0; } /// /// If this is on then projectile spells won't lead a target. /// Arc spells have this set to true. /// public bool NonTracking { get => _spell.NonTracking ?? false; } /// /// The offset to apply to the spawn position /// public Vector3 CreateOffset { get => new Vector3(_spell.CreateOffsetOriginX ?? 0.0f, _spell.CreateOffsetOriginY ?? 0.0f, _spell.CreateOffsetOriginZ ?? 0.0f); } /// /// The minimum amount of padding to ensure for the spell to spawn /// public Vector3 Padding { get => new Vector3(_spell.PaddingOriginX ?? 0.0f, _spell.PaddingOriginY ?? 0.0f, _spell.PaddingOriginZ ?? 0.0f); } /// /// The dimensions of the origin, used for Volley spells? /// public Vector3 Dims { get => new Vector3(_spell.DimsOriginX ?? 0.0f, _spell.DimsOriginY ?? 0.0f, _spell.DimsOriginZ ?? 0.0f); } /// /// The maximum variation for spawn position /// public Vector3 Peturbation { get => new Vector3(_spell.PeturbationOriginX ?? 0.0f, _spell.PeturbationOriginY ?? 0.0f, _spell.PeturbationOriginZ ?? 0.0f); } /// /// The imbued effect for this spell /// public uint ImbuedEffect { get => _spell.ImbuedEffect ?? 0; } /// /// The creature class for a slayer spell /// public CreatureType SlayerCreatureType { get => (CreatureType)(_spell.SlayerCreatureType ?? 0); } /// /// The amount of additional damage for a slayer spell /// public float SlayerDamageBonus { get => _spell.SlayerDamageBonus ?? 0; } // 0 = additive, or 1 = multiplier? /// /// The critical chance frequency for this spell /// public double CritFrequency { get => _spell.CritFreq ?? 0.0; } // default: 0, 1, or 0.03? /// /// The critical damage multiplier for this spell /// public double CritMultiplier { get => _spell.CritMultiplier ?? 1.0; } // verify default multiplier? /// /// If TRUE, ignores magic resistance /// public bool IgnoreMagicResist { get => Convert.ToBoolean(_spell.IgnoreMagicResist ?? 0); } // verify bool? /// /// The elemental damage multiplier for this spell /// public double ElementalModifier { get => _spell.ElementalModifier ?? 1.0; } // verify default multiplier? /// /// The amount of source vital to drain for a life spell /// public float DrainPercentage { get => _spell.DrainPercentage ?? 0.0f; } /// /// The percentage of DrainPercentage to damage a target for life projectiles /// public float DamageRatio { get => _spell.DamageRatio ?? 1.0f; } /// /// DamageType used by LifeMagic spells that specifies Health, Mana, or Stamina for the Boost type spells /// public DamageType VitalDamageType { get => (DamageType)(_spell.DamageType ?? 0); } /// /// The minimum amount of vital boost from a life spell /// public int Boost { get => _spell.Boost ?? 0; } /// /// Boost + BoostVariance = the maximum amount of vital boost from a life spell /// public int BoostVariance { get => _spell.BoostVariance ?? 0; } public int MaxBoost { get => Boost + BoostVariance; } /// /// The source vital for a life spell /// public PropertyAttribute2nd Source { get => (PropertyAttribute2nd)(_spell.Source ?? 0); } /// /// The destination vital for a life spell /// public PropertyAttribute2nd Destination { get => (PropertyAttribute2nd)(_spell.Destination ?? 0); } /// /// The propotion of source vital to transfer to destination vital /// public float Proportion { get => _spell.Proportion ?? 1.0f; } /// /// The percent of source vital loss for a life magic transfer spell /// public float LossPercent { get => _spell.LossPercent ?? 0.0f; } /// /// A static amount of source vital loss for a life magic transfer spell? /// Unused / unknown? /// public int SourceLoss { get => _spell.SourceLoss ?? 0; } /// /// The maximum amount of vital transferred by a life magic spell /// public int TransferCap { get => _spell.TransferCap ?? 0; } /// /// The maximum destination vital boost for a life magic transfer spell? /// Unused / unknown? /// public int MaxBoostAllowed { get => _spell.MaxBoostAllowed ?? 0; } /// /// Indicates the source and destination for life magic transfer spells /// public TransferFlags TransferFlags { get => (TransferFlags)(_spell.TransferBitfield ?? 0); } /// /// Unknown index? /// public int Index { get => _spell.Index ?? 0; } /// /// For SpellType.PortalSummon spells, Link is set to either 1 for LinkedPortalOneDID or 2 for LinkedPortalTwoDID /// public int Link { get => _spell.Link ?? 0; } /// /// A destination location for a spell /// public Position Position { get => new Position(_spell.PositionObjCellId ?? 0, new Vector3(_spell.PositionOriginX ?? 0.0f, _spell.PositionOriginY ?? 0.0f, _spell.PositionOriginZ ?? 0.0f), new Quaternion(_spell.PositionAnglesX ?? 0.0f, _spell.PositionAnglesY ?? 0.0f, _spell.PositionAnglesZ ?? 0.0f, _spell.PositionAnglesW ?? 0.0f)); } /// /// The minimum spell power to dispel (unused?) /// public int MinPower { get => _spell.MinPower ?? 0; } /// /// The maximum spell power to dispel /// public int MaxPower { get => _spell.MaxPower ?? 0; } /// /// Possible RNG for spell power to dispel (unused?) /// public float PowerVariance { get => _spell.PowerVariance ?? 0.0f; } /// /// The magic school to dispel, or undefined for all schools /// public MagicSchool DispelSchool { get => (MagicSchool)(_spell.DispelSchool ?? 0); } /// /// The type of spells to dispel /// 0 = all spells /// 1 = positive /// 2 = negative /// public DispelType Align { get => (DispelType)(_spell.Align ?? 0); } /// /// The maximum # of spells to dispel /// public int Number { get =>_spell.Number ?? 0; } /// /// Number * NumberVariance = the minimum # of spells to dispel /// public float NumberVariance { get => _spell.NumberVariance ?? 0; } } }