/// <summary> /// Calculates the damage reduction modifier for bows and casters /// with 'Magic Absorbing' property /// </summary> public float AbsorbMagic(Creature target, WorldObject item) { // https://asheron.fandom.com/wiki/Category:Magic_Absorbing // Tomes and Bows // The formula to determine magic absorption for Tomes and the Fetish of the Dark Idols: // - For a 25% maximum item: (magic absorbing %) = 25 - (0.1 * (319 - base magic defense)) // - For a 10% maximum item: (magic absorbing %) = 10 - (0.04 * (319 - base magic defense)) // wiki currently has what is likely a typo for the 10% formula, // where it has a factor of 0.4 instead of 0.04 // with 0.4, the 10% items would not start to become effective until base magic defense 294 // with 0.04, both formulas start to become effective at base magic defense 69 // using an equivalent formula that produces the correct results for 10% and 25%, // and also produces the correct results for any % if (item.AbsorbMagicDamage == null) { return(1.0f); } var maxPercent = item.AbsorbMagicDamage.Value; var baseCap = 319; var magicDefBase = target.GetCreatureSkill(Skill.MagicDefense).Base; var diff = Math.Max(0, baseCap - magicDefBase); var percent = maxPercent - maxPercent * diff * 0.004f; return(Math.Min(1.0f, 1.0f - (float)percent)); }
/// <summary> /// Calculates the amount of damage a shield absorbs from magic projectile /// </summary> public float GetShieldMod(Creature target) { // ensure combat stance if (target.CombatMode == CombatMode.NonCombat) { return(1.0f); } // does the player have a shield equipped? var shield = target.GetEquippedShield(); if (shield == null || shield.GetProperty(PropertyFloat.AbsorbMagicDamage) == null) { return(1.0f); } // is spell projectile in front of player, // within shield effectiveness area? var effectiveAngle = 180.0f; var angle = target.GetAngle(this); if (Math.Abs(angle) > effectiveAngle / 2.0f) { return(1.0f); } // https://asheron.fandom.com/wiki/Shield // The formula to determine magic absorption for shields is: // Reduction Percent = (cap * specMod * baseSkill * 0.003f) - (cap * specMod * 0.3f) // Cap = Maximum reduction // SpecMod = 1.0 for spec, 0.8 for trained // BaseSkill = 100 to 433 (above 433 base shield you always achieve the maximum %) var shieldSkill = target.GetCreatureSkill(Skill.Shield); // ensure trained? if (shieldSkill.AdvancementClass < SkillAdvancementClass.Trained || shieldSkill.Base < 100) { return(1.0f); } var baseSkill = Math.Min(shieldSkill.Base, 433); var specMod = shieldSkill.AdvancementClass == SkillAdvancementClass.Specialized ? 1.0f : 0.8f; var cap = (float)(shield.GetProperty(PropertyFloat.AbsorbMagicDamage) ?? 0.0f); // speced, 100 skill = 0% // trained, 100 skill = 0% // speced, 200 skill = 30% // trained, 200 skill = 24% // speced, 300 skill = 60% // trained, 300 skill = 48% // speced, 433 skill = 100% // trained, 433 skill = 80% var reduction = (cap * specMod * baseSkill * 0.003f) - (cap * specMod * 0.3f); var shieldMod = 1.0f - reduction; return(shieldMod); }
public uint CalculateManaUsage(Creature caster, SpellBase spell, WorldObject target = null) { var items = new List <WorldObject>(); if ((target as Player) != null) { items = (target as Player).GetAllWieldedItems(); } CreatureSkill mc = caster.GetCreatureSkill(Skill.ManaConversion); double z = mc.Current; double baseManaPercent = 1; if (z > spell.Power) { baseManaPercent = spell.Power / z; } double preCost = 0; uint manaUsed = 0; if ((int)Math.Floor(baseManaPercent) == 1) { preCost = spell.BaseMana; manaUsed = (uint)preCost; } else { if ((spell.School == MagicSchool.ItemEnchantment) && (spell.MetaSpellType == SpellType.Enchantment)) { int count = 1; if ((target as Player) != null) { count = items.Count; } preCost = (spell.BaseMana + (spell.ManaMod * items.Count)) * baseManaPercent; } else { preCost = spell.BaseMana * baseManaPercent; } if (preCost < 1) { preCost = 1; } manaUsed = (uint)Physics.Common.Random.RollDice(1, (int)preCost); } return(manaUsed); }
public uint CalculateManaUsage(Creature caster, Spell spell, WorldObject target = null) { var baseCost = spell.BaseMana; // for casting spells built into a casting implement, use the ItemManaCost var castItem = caster.GetEquippedWand(); if (castItem != null && (castItem.SpellDID ?? 0) == spell.Id) { baseCost = (uint)(castItem.ItemManaCost ?? 0); } uint mana_conversion_skill = caster.GetCreatureSkill(Skill.ManaConversion).Current; uint difficulty = spell.PowerMod; // modified power difficulty double baseManaPercent = 1.0; if (mana_conversion_skill > difficulty) { baseManaPercent = (double)difficulty / mana_conversion_skill; } uint preCost = 0; if ((spell.School == MagicSchool.ItemEnchantment) && (spell.MetaSpellType == SpellType.Enchantment)) { var targetPlayer = target as Player; int numTargetItems = 1; if (targetPlayer != null) { numTargetItems = targetPlayer.EquippedObjects.Count; } preCost = (uint)Math.Round((baseCost + (spell.ManaMod * numTargetItems)) * baseManaPercent); } else { preCost = (uint)Math.Round(baseCost * baseManaPercent); } if (preCost < 1) { preCost = 1; } uint manaUsed = ThreadSafeRandom.Next(1, preCost); return(manaUsed); }
public uint CalculateManaUsage(Creature caster, Spell spell, WorldObject target = null) { var baseCost = spell.BaseMana; // for casting spells built into a casting implement, use the ItemManaCost var castItem = caster.GetEquippedWand(); if (castItem != null && (castItem.SpellDID ?? 0) == spell.Id) { baseCost = (uint)(castItem.ItemManaCost ?? 0); } if ((spell.School == MagicSchool.ItemEnchantment) && (spell.MetaSpellType == SpellType.Enchantment) && (spell.Category >= SpellCategory.ArmorValueRaising) && (spell.Category <= SpellCategory.AcidicResistanceLowering) && target is Player targetPlayer) { var numTargetItems = 1; if (targetPlayer != null) { numTargetItems = targetPlayer.EquippedObjects.Values.Count(i => (i is Clothing || i.IsShield) && i.IsEnchantable); } baseCost += spell.ManaMod * (uint)numTargetItems; } else if ((spell.Flags & SpellFlags.FellowshipSpell) != 0) { var numFellows = 1; if (this is Player player && player.Fellowship != null) { numFellows = player.Fellowship.FellowshipMembers.Count; } baseCost += spell.ManaMod * (uint)numFellows; } var manaConversion = caster.GetCreatureSkill(Skill.ManaConversion); if (manaConversion.AdvancementClass < SkillAdvancementClass.Trained || spell.Flags.HasFlag(SpellFlags.IgnoresManaConversion)) { return(baseCost); } var difficulty = spell.PowerMod; // modified power difficulty var mana_conversion_skill = (uint)Math.Round(manaConversion.Current * GetWeaponManaConversionModifier(caster)); var manaCost = GetManaCost(difficulty, baseCost, mana_conversion_skill); return(manaCost); }
public uint CalculateManaUsage(Creature caster, SpellBase spell, WorldObject target = null) { uint mana_conversion_skill = caster.GetCreatureSkill(Skill.ManaConversion).Current; uint difficulty = Math.Max(25, spell.Power); // for mana conversion only? double baseManaPercent = 1.0; if (mana_conversion_skill > difficulty) { baseManaPercent = (double)difficulty / mana_conversion_skill; } uint preCost = 0; if ((spell.School == MagicSchool.ItemEnchantment) && (spell.MetaSpellType == SpellType.Enchantment)) { var targetPlayer = target as Player; int numTargetItems = 1; if (targetPlayer != null) { var targetItems = targetPlayer.GetAllWieldedItems(); numTargetItems = targetItems.Count; } preCost = (uint)Math.Round((spell.BaseMana + (spell.ManaMod * numTargetItems)) * baseManaPercent); } else { preCost = (uint)Math.Round(spell.BaseMana * baseManaPercent); } if (preCost < 1) { preCost = 1; } uint manaUsed = Physics.Common.Random.RollDice(1, preCost); return(manaUsed); }
public uint CalculateManaUsage(Creature caster, Spell spell, WorldObject target = null) { var baseCost = spell.BaseMana; // for casting spells built into a casting implement, use the ItemManaCost var castItem = caster.GetEquippedWand(); if (castItem != null && (castItem.SpellDID ?? 0) == spell.Id) { baseCost = (uint)(castItem.ItemManaCost ?? 0); } uint mana_conversion_skill = (uint)Math.Round(caster.GetCreatureSkill(Skill.ManaConversion).Current *GetWeaponManaConversionModifier(caster)); uint difficulty = spell.PowerMod; // modified power difficulty double baseManaPercent = 1.0; if (mana_conversion_skill > difficulty) { baseManaPercent = (double)difficulty / mana_conversion_skill; } uint preCost = 0; if ((spell.School == MagicSchool.ItemEnchantment) && (spell.MetaSpellType == SpellType.Enchantment) && (spell.Category >= SpellCategory.ArmorValueRaising) && (spell.Category <= SpellCategory.AcidicResistanceLowering) && target is Player targetPlayer) { int numTargetItems = 1; if (targetPlayer != null) { numTargetItems = targetPlayer.EquippedObjects.Values.Count(i => (i is Clothing || i.IsShield) && i.IsEnchantable); } preCost = (uint)Math.Round((baseCost + (spell.ManaMod * numTargetItems)) * baseManaPercent); } else if ((spell.Flags & SpellFlags.FellowshipSpell) != 0) { int numFellows = 1; var player = this as Player; if (player != null && player.Fellowship != null) { numFellows = player.Fellowship.FellowshipMembers.Count; } preCost = (uint)Math.Round((baseCost + (spell.ManaMod * numFellows)) * baseManaPercent); } else { preCost = (uint)Math.Round(baseCost * baseManaPercent); } if (preCost < 1) { preCost = 1; } uint manaUsed = ThreadSafeRandom.Next(1, preCost); return(manaUsed); }
/// <summary> /// Method used for handling player targeted spell casts /// </summary> public void CreatePlayerSpell(ObjectGuid guidTarget, uint spellId) { Player player = CurrentLandblock.GetObject(Guid) as Player; WorldObject target = CurrentLandblock.GetObject(guidTarget); TargetCategory targetCategory = TargetCategory.WorldObject; if (target == null) { target = GetWieldedItem(guidTarget); targetCategory = TargetCategory.Wielded; } if (target == null) { target = GetInventoryItem(guidTarget); targetCategory = TargetCategory.Inventory; } if (target == null) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.TargetNotAcquired)); targetCategory = TargetCategory.UnDef; return; } SpellTable spellTable = DatManager.PortalDat.SpellTable; if (!spellTable.Spells.ContainsKey(spellId)) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.MagicInvalidSpellType)); return; } SpellBase spell = spellTable.Spells[spellId]; if (IsInvalidTarget(spell, target)) { player.Session.Network.EnqueueSend(new GameEventCommunicationTransientString(player.Session, $"{spell.Name} cannot be cast on {target.Name}.")); player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.None)); return; } Database.Models.World.Spell spellStatMod = DatabaseManager.World.GetCachedSpell(spellId); if (spellStatMod == null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{spell.Name} spell not implemented, yet!", ChatMessageType.System)); player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.MagicInvalidSpellType)); return; } if (player.IsBusy == true) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.YoureTooBusy)); return; } else { player.IsBusy = true; } uint targetEffect = spell.TargetEffect; // Grab player's skill level in the spell's Magic School var magicSkill = player.GetCreatureSkill(spell.School).Current; if (targetCategory == TargetCategory.WorldObject) { if (guidTarget != Guid) { float distanceTo = Location.Distance2D(target.Location); if (distanceTo > spell.BaseRangeConstant + magicSkill * spell.BaseRangeMod) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.MagicTargetOutOfRange), new GameMessageSystemChat($"{target.Name} is out of range!", ChatMessageType.Magic)); player.IsBusy = false; return; } } } // Ensure that a harmful spell isn't being cast on a player target that doesn't have the same PK status if (target.WeenieClassId == 1 && player.PlayerKillerStatus != ACE.Entity.Enum.PlayerKillerStatus.NPK) { bool isSpellHarmful = IsSpellHarmful(spell); if (player.PlayerKillerStatus != target.PlayerKillerStatus && isSpellHarmful) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.InvalidPkStatus)); player.IsBusy = false; return; } } float scale = SpellAttributes(player.Session.Account, spellId, out float castingDelay, out MotionCommand windUpMotion, out MotionCommand spellGesture); var formula = SpellTable.GetSpellFormula(spellTable, spellId, player.Session.Account); bool spellCastSuccess = false || ((Physics.Common.Random.RollDice(0.0f, 1.0f) > (1.0f - SkillCheck.GetMagicSkillChance((int)magicSkill, (int)spell.Power))) && (magicSkill >= (int)spell.Power - 50) && (magicSkill > 0)); // Calculating mana usage #region CreatureSkill mc = player.GetCreatureSkill(Skill.ManaConversion); double z = mc.Current; double baseManaPercent = 1; if (z > spell.Power) { baseManaPercent = spell.Power / z; } double preCost; uint manaUsed; if ((int)Math.Floor(baseManaPercent) == 1) { preCost = spell.BaseMana; manaUsed = (uint)preCost; } else { preCost = spell.BaseMana * baseManaPercent; if (preCost < 1) { preCost = 1; } manaUsed = (uint)Physics.Common.Random.RollDice(1, (int)preCost); } if (spell.MetaSpellType == SpellType.Transfer) { uint vitalChange, casterVitalChange; vitalChange = (uint)(player.GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source) * spellStatMod.Proportion); if (spellStatMod.TransferCap != 0) { if (vitalChange > spellStatMod.TransferCap) { vitalChange = (uint)spellStatMod.TransferCap; } } casterVitalChange = (uint)(vitalChange * (1.0f - spellStatMod.LossPercent)); vitalChange = (uint)(casterVitalChange / (1.0f - spellStatMod.LossPercent)); if (spellStatMod.Source == (int)PropertyAttribute2nd.Mana && (vitalChange + 10 + manaUsed) > player.Mana.Current) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.YouDontHaveEnoughManaToCast)); player.IsBusy = false; }); resourceCheckChain.EnqueueChain(); return; } else if ((vitalChange + 10) > player.GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source)) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => player.IsBusy = false); resourceCheckChain.EnqueueChain(); return; } } else if (manaUsed > player.Mana.Current) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.YouDontHaveEnoughManaToCast)); player.IsBusy = false; }); resourceCheckChain.EnqueueChain(); return; } else { player.UpdateVital(player.Mana, player.Mana.Current - manaUsed); } #endregion ActionChain spellChain = new ActionChain(); uint fastCast = (spell.Bitfield >> 14) & 0x1u; if (fastCast != 1) { spellChain.AddAction(this, () => { var motionWindUp = new UniversalMotion(MotionStance.Spellcasting); motionWindUp.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionWindUp.MovementData.ForwardCommand = (uint)windUpMotion; motionWindUp.MovementData.ForwardSpeed = 2; DoMotion(motionWindUp); }); } spellChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageCreatureMessage(SpellComponentsTable.GetSpellWords(DatManager.PortalDat.SpellComponentsTable, formula), Name, Guid.Full, ChatMessageType.Magic)); }); spellChain.AddAction(this, () => { var motionCastSpell = new UniversalMotion(MotionStance.Spellcasting); motionCastSpell.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionCastSpell.MovementData.ForwardCommand = (uint)spellGesture; motionCastSpell.MovementData.ForwardSpeed = 2; DoMotion(motionCastSpell); }); if (fastCast == 1) { spellChain.AddDelaySeconds(castingDelay * 0.26f); } else { spellChain.AddDelaySeconds(castingDelay); } if (spellCastSuccess == false) { spellChain.AddAction(this, () => CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f))); } else { spellChain.AddAction(this, () => { bool targetDeath; string message; switch (spell.School) { case MagicSchool.WarMagic: WarMagic(target, spell, spellStatMod); break; case MagicSchool.VoidMagic: VoidMagic(target, spell, spellStatMod); break; case MagicSchool.CreatureEnchantment: if (IsSpellHarmful(spell)) { // Retrieve player's skill level in the Magic School var playerMagicSkill = player.GetCreatureSkill(spell.School).Current; // Retrieve target's Magic Defense Skill Creature creature = (Creature)target; var targetMagicDefenseSkill = creature.GetCreatureSkill(Skill.MagicDefense).Current; if (MagicDefenseCheck(playerMagicSkill, targetMagicDefenseSkill)) { CurrentLandblock.EnqueueBroadcastSound(player, Sound.ResistSpell); player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{creature.Name} resists {spell.Name}", ChatMessageType.Magic)); break; } } CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(target.Guid, (PlayScript)spell.TargetEffect, scale)); message = CreatureMagic(target, spell, spellStatMod); if (message != "") { player.Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Magic)); } break; case MagicSchool.LifeMagic: if (spell.MetaSpellType != SpellType.LifeProjectile) { if (IsSpellHarmful(spell)) { // Retrieve player's skill level in the Magic School var playerMagicSkill = player.GetCreatureSkill(spell.School).Current; // Retrieve target's Magic Defense Skill Creature creature = (Creature)target; var targetMagicDefenseSkill = creature.GetCreatureSkill(Skill.MagicDefense).Current; if (MagicDefenseCheck(playerMagicSkill, targetMagicDefenseSkill)) { CurrentLandblock.EnqueueBroadcastSound(player, Sound.ResistSpell); player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{creature.Name} resists {spell.Name}", ChatMessageType.Magic)); break; } } } CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(target.Guid, (PlayScript)spell.TargetEffect, scale)); targetDeath = LifeMagic(target, spell, spellStatMod, out message); if (message != null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Magic)); } if (targetDeath == true) { Creature creatureTarget = (Creature)target; creatureTarget.Die(); Strings.DeathMessages.TryGetValue(DamageType.Base, out var messages); player.Session.Network.EnqueueSend(new GameMessageSystemChat(string.Format(messages[0], target.Name), ChatMessageType.Broadcast)); player.EarnXP((long)target.XpOverride); } break; case MagicSchool.ItemEnchantment: if (guidTarget == Guid) { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, (PlayScript)spell.CasterEffect, scale)); } else { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(target.Guid, (PlayScript)spell.TargetEffect, scale)); } message = ItemMagic(target, spell, spellStatMod); if (message != "") { player.Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Magic)); } break; default: break; } }); } spellChain.AddAction(this, () => { var motionReturnToCastStance = new UniversalMotion(MotionStance.Spellcasting); motionReturnToCastStance.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionReturnToCastStance.MovementData.ForwardCommand = (uint)MotionCommand.Invalid; DoMotion(motionReturnToCastStance); }); spellChain.AddDelaySeconds(1.0f); spellChain.AddAction(this, () => { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.None)); player.IsBusy = false; }); spellChain.EnqueueChain(); return; }