public static void ScaleDamageRanged(ACE.Server.WorldObjects.Creature c, ACE.Server.Entity.BaseDamageMod d) { if (!c.IsMonster) { return; } d.BaseDamage.MaxDamage = (int)((float)d.BaseDamage.MaxDamage * S_MONSTERMISSILEDMGINCREASE); }
public static void ScaleDamageMelee(ACE.Server.WorldObjects.Creature c, ACE.Server.Entity.DamageEvent d) { if (!c.IsMonster) { return; } if (d.HasDamage) { d.Damage *= S_MONSTERMELEEDMGINCREASE; } }
public static void Scale(ACE.Server.WorldObjects.Creature c) { if (!c.IsMonster) { return; } c.Health.Ranks += (uint)((float)c.Health.MaxValue * S_MONSTERHPINCREASE); c.Health.Current = c.Health.MaxValue; //c.Stamina.MaxValue = (int)((float)c.Stamina.MaxValue * 1.2f); //c.Stamina.Current = c.Stamina.MaxValue; //c.Mana.MaxValue = (int)((float)c.Mana.MaxValue * 1.2f); //c.Mana.Current = c.Mana.MaxValue; }
/// <summary> /// Called for a spell projectile to damage its target /// </summary> public void DamageTarget(WorldObject _target, double?damage, bool critical, bool critDefended, bool overpower) { var player = ProjectileSource as Player; var target = _target as Creature; var targetPlayer = _target as Player; if (targetPlayer != null && (targetPlayer.Invincible || targetPlayer.IsDead)) { return; } uint amount; var percent = 0.0f; var heritageMod = 1.0f; var sneakAttackMod = 1.0f; // handle life projectiles for stamina / mana if (Spell.Category == SpellCategory.StaminaLowering) { percent = (float)damage / target.Stamina.MaxValue; amount = (uint)-target.UpdateVitalDelta(target.Stamina, (int)-Math.Round(damage.Value)); } else if (Spell.Category == SpellCategory.ManaLowering) { percent = (float)damage / target.Mana.MaxValue; amount = (uint)-target.UpdateVitalDelta(target.Mana, (int)-Math.Round(damage.Value)); } else { // for possibly applying sneak attack to magic projectiles, // only do this for health-damaging projectiles? if (player != null) { // TODO: use target direction vs. projectile position, instead of player position // could sneak attack be applied to void DoTs? sneakAttackMod = player.GetSneakAttackMod(target); //Console.WriteLine("Magic sneak attack: + sneakAttackMod); heritageMod = player.GetHeritageBonus(player.GetEquippedWand()) ? 1.05f : 1.0f; } // DR / DRR applies for magic too? var creatureSource = ProjectileSource as Creature; var damageRating = creatureSource != null?creatureSource.GetDamageRating() : 0; var damageRatingMod = Creature.AdditiveCombine(Creature.GetPositiveRatingMod(damageRating), heritageMod, sneakAttackMod); var damageResistRatingMod = Creature.GetNegativeRatingMod(target.GetDamageResistRating(CombatType.Magic)); damage *= damageRatingMod * damageResistRatingMod; //Console.WriteLine($"Damage rating: " + Creature.ModToRating(damageRatingMod)); percent = (float)damage / target.Health.MaxValue; amount = (uint)-target.UpdateVitalDelta(target.Health, (int)-Math.Round(damage.Value)); target.DamageHistory.Add(ProjectileSource, Spell.DamageType, amount); //if (targetPlayer != null && targetPlayer.Fellowship != null) //targetPlayer.Fellowship.OnVitalUpdate(targetPlayer); } amount = (uint)Math.Round(damage.Value); // full amount for debugging if (target.IsAlive) { string verb = null, plural = null; Strings.GetAttackVerb(Spell.DamageType, percent, ref verb, ref plural); var type = Spell.DamageType.GetName().ToLower(); var critMsg = critical ? "Critical hit! " : ""; var sneakMsg = sneakAttackMod > 1.0f ? "Sneak Attack! " : ""; var overpowerMsg = overpower ? "Overpower! " : ""; var nonHealth = Spell.Category == SpellCategory.StaminaLowering || Spell.Category == SpellCategory.ManaLowering; if (player != null) { var critProt = critDefended ? " Your target's Critical Protection augmentation allows them to avoid your critical hit!" : ""; var attackerMsg = $"{critMsg}{overpowerMsg}{sneakMsg}You {verb} {target.Name} for {amount} points with {Spell.Name}.{critProt}"; // could these crit / sneak attack? if (nonHealth) { var vital = Spell.Category == SpellCategory.StaminaLowering ? "stamina" : "mana"; attackerMsg = $"With {Spell.Name} you drain {amount} points of {vital} from {target.Name}."; } if (!player.SquelchManager.Squelches.Contains(target, ChatMessageType.Magic)) { player.Session.Network.EnqueueSend(new GameMessageSystemChat(attackerMsg, ChatMessageType.Magic)); } player.Session.Network.EnqueueSend(new GameEventUpdateHealth(player.Session, target.Guid.Full, (float)target.Health.Current / target.Health.MaxValue)); } if (targetPlayer != null) { var critProt = critDefended ? " Your Critical Protection augmentation allows you to avoid a critical hit!" : ""; var defenderMsg = $"{critMsg}{overpowerMsg}{sneakMsg}{ProjectileSource.Name} {plural} you for {amount} points with {Spell.Name}.{critProt}"; if (nonHealth) { var vital = Spell.Category == SpellCategory.StaminaLowering ? "stamina" : "mana"; defenderMsg = $"{ProjectileSource.Name} casts {Spell.Name} and drains {amount} points of your {vital}."; } if (!targetPlayer.SquelchManager.Squelches.Contains(ProjectileSource, ChatMessageType.Magic)) { targetPlayer.Session.Network.EnqueueSend(new GameMessageSystemChat(defenderMsg, ChatMessageType.Magic)); } } if (!nonHealth) { if (target.HasCloakEquipped) { Cloak.TryProcSpell(target, ProjectileSource, percent); } target.EmoteManager.OnDamage(player); if (critical) { target.EmoteManager.OnReceiveCritical(player); } } } else { var lastDamager = ProjectileSource != null ? new DamageHistoryInfo(ProjectileSource) : null; target.OnDeath(lastDamager, Spell.DamageType, critical); target.Die(); } }
/// <summary> /// Calculates the damage for a spell projectile /// Used by war magic, void magic, and life magic projectiles /// </summary> public double?CalculateDamage(WorldObject source, WorldObject caster, Creature target, ref bool criticalHit, ref bool critDefended, ref bool overpower) { var sourcePlayer = source as Player; var targetPlayer = target as Player; if (source == null || targetPlayer != null && targetPlayer.Invincible == true) { return(null); } // target already dead? if (target.Health.Current <= 0) { return(-1); } // check lifestone protection if (targetPlayer != null && targetPlayer.UnderLifestoneProtection) { if (sourcePlayer != null) { sourcePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat($"The Lifestone's magic protects {targetPlayer.Name} from the attack!", ChatMessageType.Magic)); } targetPlayer.HandleLifestoneProtection(); return(null); } double damageBonus = 0.0f, warSkillBonus = 0.0f, finalDamage = 0.0f; var resistanceType = Creature.GetResistanceType(Spell.DamageType); var sourceCreature = source as Creature; if (sourceCreature?.Overpower != null) { overpower = Creature.GetOverpower(sourceCreature, target); } var resisted = source.TryResistSpell(target, Spell, caster, true); if (resisted && !overpower) { return(null); } CreatureSkill attackSkill = null; if (sourceCreature != null) { attackSkill = sourceCreature.GetCreatureSkill(Spell.School); } // critical hit var critical = GetWeaponMagicCritFrequency(sourceCreature, attackSkill, target); if (ThreadSafeRandom.Next(0.0f, 1.0f) < critical) { if (targetPlayer != null && targetPlayer.AugmentationCriticalDefense > 0) { var criticalDefenseMod = sourcePlayer != null ? 0.05f : 0.25f; var criticalDefenseChance = targetPlayer.AugmentationCriticalDefense * criticalDefenseMod; if (criticalDefenseChance > ThreadSafeRandom.Next(0.0f, 1.0f)) { critDefended = true; } } if (!critDefended) { criticalHit = true; } } var absorbMod = GetAbsorbMod(target); bool isPVP = sourcePlayer != null && targetPlayer != null; if (isPVP && Spell.IsHarmful) { Player.UpdatePKTimers(sourcePlayer, targetPlayer); } var elementalDmgBonus = GetCasterElementalDamageModifier(sourceCreature, target, Spell.DamageType); // Possible 2x + damage bonus for the slayer property var slayerBonus = GetWeaponCreatureSlayerModifier(sourceCreature, target); // life magic projectiles: ie., martyr's hecatomb if (Spell.School == MagicSchool.LifeMagic) { var lifeMagicDamage = LifeProjectileDamage * Spell.DamageRatio; // could life magic projectiles crit? // if so, did they use the same 1.5x formula as war magic, instead of 2.0x? if (criticalHit) { damageBonus = lifeMagicDamage * 0.5f * GetWeaponCritDamageMod(sourceCreature, attackSkill, target); } finalDamage = (lifeMagicDamage + damageBonus) * elementalDmgBonus * slayerBonus * absorbMod; return(finalDamage); } // war/void magic projectiles else { if (criticalHit) { // Original: // http://acpedia.org/wiki/Announcements_-_2002/08_-_Atonement#Letter_to_the_Players // Critical Strikes: In addition to the skill-based damage bonus, each projectile spell has a 2% chance of causing a critical hit on the target and doing increased damage. // A magical critical hit is similar in some respects to melee critical hits (although the damage calculation is handled differently). // While a melee critical hit automatically does twice the maximum damage of the weapon, a magical critical hit will do an additional half the minimum damage of the spell. // For instance, a magical critical hit from a level 7 spell, which does 110-180 points of damage, would add an additional 55 points of damage to the spell. // Later updated for PvE only: // http://acpedia.org/wiki/Announcements_-_2004/07_-_Treaties_in_Stone#Letter_to_the_Players // Currently when a War Magic spell scores a critical hit, it adds a multiple of the base damage of the spell to a normal damage roll. // Starting in July, War Magic critical hits will instead add a multiple of the maximum damage of the spell. // No more crits that do less damage than non-crits! if (isPVP) // PvP: 50% of the MIN damage added to normal damage roll { damageBonus = Spell.MinDamage * 0.5f; } else // PvE: 50% of the MAX damage added to normal damage roll { damageBonus = Spell.MaxDamage * 0.5f; } var critDamageMod = GetWeaponCritDamageMod(sourceCreature, attackSkill, target); damageBonus *= critDamageMod; } /* War Magic skill-based damage bonus * http://acpedia.org/wiki/Announcements_-_2002/08_-_Atonement#Letter_to_the_Players */ if (sourcePlayer != null) { // per retail stats, level 8 difficulty is capped to 350 instead of 400 // without this, level 7s have the potential to deal more damage than level 8s var difficulty = Math.Min(Spell.Power, 350); var magicSkill = sourcePlayer.GetCreatureSkill(Spell.School).Current; if (magicSkill > difficulty) { // Bonus clamped to a maximum of 50% //var percentageBonus = Math.Clamp((magicSkill - Spell.Power) / 100.0f, 0.0f, 0.5f); var percentageBonus = (magicSkill - difficulty) / 1000.0f; warSkillBonus = Spell.MinDamage * percentageBonus; } } var baseDamage = ThreadSafeRandom.Next(Spell.MinDamage, Spell.MaxDamage); var weaponResistanceMod = GetWeaponResistanceModifier(sourceCreature, attackSkill, Spell.DamageType); // if attacker/weapon has IgnoreMagicResist directly, do not transfer to spell projectile // only pass if SpellProjectile has it directly, such as 2637 - Invoking Aun Tanua var resistanceMod = Math.Max(0.0f, target.GetResistanceMod(resistanceType, this, null, weaponResistanceMod)); finalDamage = baseDamage + damageBonus + warSkillBonus; finalDamage *= resistanceMod * elementalDmgBonus * slayerBonus * absorbMod; return(finalDamage); } }
/// <summary> /// Main entry point for getting the armor mod /// </summary> public float GetArmorMod(DamageType damageType, List <WorldObject> armorLayers, Creature attacker, WorldObject weapon, float armorRendingMod = 1.0f) { var effectiveArmorVsType = GetEffectiveArmorVsType(damageType, armorLayers, attacker, weapon, armorRendingMod); return(SkillFormula.CalcArmorMod(effectiveArmorVsType)); }
public float GetEffectiveArmorVsType(DamageType damageType, List <WorldObject> armorLayers, Creature attacker, WorldObject weapon, float armorRendingMod = 1.0f) { var ignoreMagicArmor = (weapon?.IgnoreMagicArmor ?? false) || (attacker?.IgnoreMagicArmor ?? false); var ignoreMagicResist = (weapon?.IgnoreMagicResist ?? false) || (attacker?.IgnoreMagicResist ?? false); // get base AL / RL var armorVsType = Biota.Value.BaseArmor * (float)Creature.GetArmorVsType(damageType); // additive enchantments: // imperil / armor var enchantmentMod = ignoreMagicResist ? 0 : EnchantmentManager.GetBodyArmorMod(); var effectiveAL = armorVsType + enchantmentMod; // handle monsters w/ multiple layers of armor foreach (var armorLayer in armorLayers) { effectiveAL += GetArmorMod(armorLayer, damageType, ignoreMagicArmor); } // armor rending reduces base armor + all physical armor too? if (effectiveAL > 0) { effectiveAL *= armorRendingMod; } return(effectiveAL); }
/// <summary> /// Called when a player or creature starts an attack /// </summary> public void OnAttack(Creature target) { // self-procs happen on attack, regardless if the attack is successfully landed TryProcEquippedItems(this, true); }
public Creature_BodyPart(Creature creature, KeyValuePair <CombatBodyPart, PropertiesBodyPart> biota) { Creature = creature; Biota = biota; }
public DamageEvent DamageTarget(Creature target, WorldObject damageSource) { if (target.Health.Current <= 0) { return(null); } // check PK status var targetPlayer = target as Player; if (targetPlayer != null) { var pkError = CheckPKStatusVsTarget(this, targetPlayer, null); if (pkError != null) { Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(Session, pkError[0], target.Name)); targetPlayer.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(targetPlayer.Session, pkError[1], Name)); return(null); } } /*float? damage = null; * if (targetPlayer != null) * { * damage = CalculateDamagePVP(target, damageSource, damageType, ref critical, ref sneakAttack, ref bodyPart); * * // TODO: level up shield mod? * if (targetPlayer.Invincible ?? false) * damage = 0.0f; * } * else * damage = CalculateDamage(target, damageSource, ref critical, ref sneakAttack);*/ var damageEvent = DamageEvent.CalculateDamage(this, target, damageSource); if (damageEvent.HasDamage) { OnDamageTarget(target, damageEvent.CombatType, damageEvent.IsCritical); if (targetPlayer != null) { targetPlayer.TakeDamage(this, damageEvent.DamageType, damageEvent.Damage, damageEvent.BodyPart, damageEvent.IsCritical); } else { target.TakeDamage(this, damageEvent.DamageType, damageEvent.Damage, damageEvent.IsCritical); } } else { if (targetPlayer != null && targetPlayer.UnderLifestoneProtection) { Session.Network.EnqueueSend(new GameMessageSystemChat($"The Lifestone's magic protects {target.Name} from the attack!", ChatMessageType.Magic)); } else { Session.Network.EnqueueSend(new GameMessageSystemChat($"{target.Name} evaded your attack.", ChatMessageType.CombatSelf)); } } if (damageEvent.HasDamage && target.IsAlive) { var attackConditions = new AttackConditions(); if (damageEvent.RecklessnessMod > 1.0f) { attackConditions |= AttackConditions.Recklessness; } if (damageEvent.SneakAttackMod > 1.0f) { attackConditions |= AttackConditions.SneakAttack; } // notify attacker var intDamage = (uint)Math.Round(damageEvent.Damage); Session.Network.EnqueueSend(new GameEventAttackerNotification(Session, target.Name, damageEvent.DamageType, (float)intDamage / target.Health.MaxValue, intDamage, damageEvent.IsCritical, attackConditions)); // splatter effects if (targetPlayer == null) { Session.Network.EnqueueSend(new GameMessageSound(target.Guid, Sound.HitFlesh1, 0.5f)); if (damageEvent.Damage >= target.Health.MaxValue * 0.25f) { var painSound = (Sound)Enum.Parse(typeof(Sound), "Wound" + ThreadSafeRandom.Next(1, 3), true); Session.Network.EnqueueSend(new GameMessageSound(target.Guid, painSound, 1.0f)); } var splatter = (PlayScript)Enum.Parse(typeof(PlayScript), "Splatter" + GetSplatterHeight() + GetSplatterDir(target)); Session.Network.EnqueueSend(new GameMessageScript(target.Guid, splatter)); } // handle Dirty Fighting if (GetCreatureSkill(Skill.DirtyFighting).AdvancementClass >= SkillAdvancementClass.Trained) { FightDirty(target); } } if (damageEvent.Damage > 0.0f) { Session.Network.EnqueueSend(new GameEventUpdateHealth(Session, target.Guid.Full, (float)target.Health.Current / target.Health.MaxValue)); } if (targetPlayer == null) { OnAttackMonster(target); } return(damageEvent); }
protected static void WriteIdentifyObjectCreatureProfile(BinaryWriter writer, Creature creature, bool success) { var creatureProfile = new CreatureProfile(creature, success); writer.Write(creatureProfile); }
/// <summary> /// Calculates the damage for a spell projectile /// Used by war magic, void magic, and life magic projectiles /// </summary> public double?CalculateDamage(WorldObject source, Creature target, ref bool criticalHit, ref bool critDefended) { var sourcePlayer = source as Player; var targetPlayer = target as Player; if (source == null || targetPlayer != null && targetPlayer.Invincible == true) { return(null); } // target already dead? if (target.Health.Current <= 0) { return(-1); } // check lifestone protection if (targetPlayer != null && targetPlayer.UnderLifestoneProtection) { if (sourcePlayer != null) { sourcePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat($"The Lifestone's magic protects {targetPlayer.Name} from the attack!", ChatMessageType.Magic)); } targetPlayer.HandleLifestoneProtection(); return(null); } double damageBonus = 0.0f, warSkillBonus = 0.0f, finalDamage = 0.0f; var resistanceType = Creature.GetResistanceType(Spell.DamageType); var resisted = source.ResistSpell(target, Spell); if (resisted != null && resisted == true) { return(null); } CreatureSkill attackSkill = null; var sourceCreature = source as Creature; if (sourceCreature != null) { attackSkill = sourceCreature.GetCreatureSkill(Spell.School); } // critical hit var critical = GetWeaponMagicCritFrequencyModifier(sourceCreature, attackSkill, target); if (ThreadSafeRandom.Next(0.0f, 1.0f) < critical) { if (targetPlayer != null && targetPlayer.AugmentationCriticalDefense > 0) { var criticalDefenseMod = sourcePlayer != null ? 0.05f : 0.25f; var criticalDefenseChance = targetPlayer.AugmentationCriticalDefense * criticalDefenseMod; if (criticalDefenseChance > ThreadSafeRandom.Next(0.0f, 1.0f)) { critDefended = true; } } if (!critDefended) { criticalHit = true; } } var shieldMod = GetShieldMod(target); bool isPVP = sourcePlayer != null && targetPlayer != null; if (isPVP && Spell.IsHarmful) { Player.UpdatePKTimers(sourcePlayer, targetPlayer); } var elementalDmgBonus = GetCasterElementalDamageModifier(sourceCreature, target, Spell.DamageType); // Possible 2x + damage bonus for the slayer property var slayerBonus = GetWeaponCreatureSlayerModifier(sourceCreature, target); // life magic projectiles: ie., martyr's hecatomb if (Spell.School == MagicSchool.LifeMagic) { var lifeMagicDamage = LifeProjectileDamage * Spell.DamageRatio; // could life magic projectiles crit? // if so, did they use the same 1.5x formula as war magic, instead of 2.0x? if (criticalHit) { damageBonus = lifeMagicDamage * 0.5f * GetWeaponCritDamageMod(sourceCreature, attackSkill, target); } finalDamage = (lifeMagicDamage + damageBonus) * elementalDmgBonus * slayerBonus * shieldMod; return(finalDamage); } // war/void magic projectiles else { if (criticalHit) { if (isPVP) // PvP: 50% of the MIN damage added to normal damage roll { damageBonus = Spell.MinDamage * 0.5f; } else // PvE: 50% of the MAX damage added to normal damage roll { damageBonus = Spell.MaxDamage * 0.5f; } var critDamageMod = GetWeaponCritDamageMod(sourceCreature, attackSkill, target); damageBonus *= critDamageMod; } /* War Magic skill-based damage bonus * http://acpedia.org/wiki/Announcements_-_2002/08_-_Atonement#Letter_to_the_Players */ if (sourcePlayer != null) { // per retail stats, level 8 difficulty is capped to 350 instead of 400 // without this, level 7s have the potential to deal more damage than level 8s var difficulty = Math.Min(Spell.Power, 350); var magicSkill = sourcePlayer.GetCreatureSkill(Spell.School).Current; if (magicSkill > difficulty) { // Bonus clamped to a maximum of 50% //var percentageBonus = Math.Clamp((magicSkill - Spell.Power) / 100.0f, 0.0f, 0.5f); var percentageBonus = (magicSkill - difficulty) / 1000.0f; warSkillBonus = Spell.MinDamage * percentageBonus; } } var baseDamage = ThreadSafeRandom.Next(Spell.MinDamage, Spell.MaxDamage); var weaponResistanceMod = GetWeaponResistanceModifier(sourceCreature, attackSkill, Spell.DamageType); finalDamage = baseDamage + damageBonus + warSkillBonus; finalDamage *= target.GetResistanceMod(resistanceType, null, weaponResistanceMod) * elementalDmgBonus * slayerBonus * shieldMod; return(finalDamage); } }
/// <summary> /// Calculates the damage for a spell projectile /// Used by war magic, void magic, and life magic projectiles /// </summary> public float?CalculateDamage(WorldObject source, WorldObject caster, Creature target, ref bool criticalHit, ref bool critDefended, ref bool overpower) { var sourcePlayer = source as Player; var targetPlayer = target as Player; if (source == null || !target.IsAlive || targetPlayer != null && targetPlayer.Invincible) { return(null); } // check lifestone protection if (targetPlayer != null && targetPlayer.UnderLifestoneProtection) { if (sourcePlayer != null) { sourcePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat($"The Lifestone's magic protects {targetPlayer.Name} from the attack!", ChatMessageType.Magic)); } targetPlayer.HandleLifestoneProtection(); return(null); } var critDamageBonus = 0.0f; var weaponCritDamageMod = 1.0f; var weaponResistanceMod = 1.0f; var resistanceMod = 1.0f; // life magic var lifeMagicDamage = 0.0f; // war/void magic var baseDamage = 0; var skillBonus = 0.0f; var finalDamage = 0.0f; var resistanceType = Creature.GetResistanceType(Spell.DamageType); var sourceCreature = source as Creature; if (sourceCreature?.Overpower != null) { overpower = Creature.GetOverpower(sourceCreature, target); } var resisted = source.TryResistSpell(target, Spell, caster, true); if (resisted && !overpower) { return(null); } CreatureSkill attackSkill = null; if (sourceCreature != null) { attackSkill = sourceCreature.GetCreatureSkill(Spell.School); } // critical hit var criticalChance = GetWeaponMagicCritFrequency(sourceCreature, attackSkill, target); if (ThreadSafeRandom.Next(0.0f, 1.0f) < criticalChance) { if (targetPlayer != null && targetPlayer.AugmentationCriticalDefense > 0) { var criticalDefenseMod = sourcePlayer != null ? 0.05f : 0.25f; var criticalDefenseChance = targetPlayer.AugmentationCriticalDefense * criticalDefenseMod; if (criticalDefenseChance > ThreadSafeRandom.Next(0.0f, 1.0f)) { critDefended = true; } } if (!critDefended) { criticalHit = true; } } var absorbMod = GetAbsorbMod(target); bool isPVP = sourcePlayer != null && targetPlayer != null; //http://acpedia.org/wiki/Announcements_-_2014/01_-_Forces_of_Nature - Aegis is 72% effective in PvP if (isPVP && (target.CombatMode == CombatMode.Melee || target.CombatMode == CombatMode.Missile)) { absorbMod = 1 - absorbMod; absorbMod *= 0.72f; absorbMod = 1 - absorbMod; } if (isPVP && Spell.IsHarmful) { Player.UpdatePKTimers(sourcePlayer, targetPlayer); } var elementalDamageMod = GetCasterElementalDamageModifier(sourceCreature, target, Spell.DamageType); // Possible 2x + damage bonus for the slayer property var slayerMod = GetWeaponCreatureSlayerModifier(sourceCreature, target); // life magic projectiles: ie., martyr's hecatomb if (Spell.MetaSpellType == ACE.Entity.Enum.SpellType.LifeProjectile) { lifeMagicDamage = LifeProjectileDamage * Spell.DamageRatio; // could life magic projectiles crit? // if so, did they use the same 1.5x formula as war magic, instead of 2.0x? if (criticalHit) { weaponCritDamageMod = GetWeaponCritDamageMod(sourceCreature, attackSkill, target); critDamageBonus = lifeMagicDamage * 0.5f * weaponCritDamageMod; } weaponResistanceMod = GetWeaponResistanceModifier(sourceCreature, attackSkill, Spell.DamageType); // if attacker/weapon has IgnoreMagicResist directly, do not transfer to spell projectile // only pass if SpellProjectile has it directly, such as 2637 - Invoking Aun Tanua resistanceMod = (float)Math.Max(0.0f, target.GetResistanceMod(resistanceType, this, null, weaponResistanceMod)); finalDamage = (lifeMagicDamage + critDamageBonus) * elementalDamageMod * slayerMod * resistanceMod * absorbMod; } // war/void magic projectiles else { if (criticalHit) { // Original: // http://acpedia.org/wiki/Announcements_-_2002/08_-_Atonement#Letter_to_the_Players // Critical Strikes: In addition to the skill-based damage bonus, each projectile spell has a 2% chance of causing a critical hit on the target and doing increased damage. // A magical critical hit is similar in some respects to melee critical hits (although the damage calculation is handled differently). // While a melee critical hit automatically does twice the maximum damage of the weapon, a magical critical hit will do an additional half the minimum damage of the spell. // For instance, a magical critical hit from a level 7 spell, which does 110-180 points of damage, would add an additional 55 points of damage to the spell. // Later updated for PvE only: // http://acpedia.org/wiki/Announcements_-_2004/07_-_Treaties_in_Stone#Letter_to_the_Players // Currently when a War Magic spell scores a critical hit, it adds a multiple of the base damage of the spell to a normal damage roll. // Starting in July, War Magic critical hits will instead add a multiple of the maximum damage of the spell. // No more crits that do less damage than non-crits! if (isPVP) // PvP: 50% of the MIN damage added to normal damage roll { critDamageBonus = Spell.MinDamage * 0.5f; } else // PvE: 50% of the MAX damage added to normal damage roll { critDamageBonus = Spell.MaxDamage * 0.5f; } weaponCritDamageMod = GetWeaponCritDamageMod(sourceCreature, attackSkill, target); critDamageBonus *= weaponCritDamageMod; } /* War Magic skill-based damage bonus * http://acpedia.org/wiki/Announcements_-_2002/08_-_Atonement#Letter_to_the_Players */ if (sourcePlayer != null) { // per retail stats, level 8 difficulty is capped to 350 instead of 400 // without this, level 7s have the potential to deal more damage than level 8s var difficulty = Math.Min(Spell.Power, 350); // was skillMod possibility capped to 1.3x for level 7 spells in retail, instead of level 8 difficulty cap? var magicSkill = sourcePlayer.GetCreatureSkill(Spell.School).Current; if (magicSkill > difficulty) { // Bonus clamped to a maximum of 50% //var percentageBonus = Math.Clamp((magicSkill - Spell.Power) / 100.0f, 0.0f, 0.5f); var percentageBonus = (magicSkill - difficulty) / 1000.0f; skillBonus = Spell.MinDamage * percentageBonus; } } baseDamage = ThreadSafeRandom.Next(Spell.MinDamage, Spell.MaxDamage); weaponResistanceMod = GetWeaponResistanceModifier(sourceCreature, attackSkill, Spell.DamageType); // if attacker/weapon has IgnoreMagicResist directly, do not transfer to spell projectile // only pass if SpellProjectile has it directly, such as 2637 - Invoking Aun Tanua resistanceMod = (float)Math.Max(0.0f, target.GetResistanceMod(resistanceType, this, null, weaponResistanceMod)); if (sourcePlayer != null && targetPlayer != null && Spell.DamageType == DamageType.Nether) { // for direct damage from void spells in pvp, // apply void_pvp_modifier *on top of* the player's natural resistance to nether // this supposedly brings the direct damage from void spells in pvp closer to retail resistanceMod *= (float)PropertyManager.GetDouble("void_pvp_modifier").Item; } finalDamage = baseDamage + critDamageBonus + skillBonus; finalDamage *= elementalDamageMod * slayerMod * resistanceMod * absorbMod; } // show debug info if (sourceCreature != null && sourceCreature.DebugDamage.HasFlag(Creature.DebugDamageType.Attacker)) { ShowInfo(sourceCreature, Spell, attackSkill, criticalChance, criticalHit, critDefended, overpower, weaponCritDamageMod, skillBonus, baseDamage, critDamageBonus, elementalDamageMod, slayerMod, weaponResistanceMod, resistanceMod, absorbMod, LifeProjectileDamage, lifeMagicDamage, finalDamage); } if (target.DebugDamage.HasFlag(Creature.DebugDamageType.Defender)) { ShowInfo(target, Spell, attackSkill, criticalChance, criticalHit, critDefended, overpower, weaponCritDamageMod, skillBonus, baseDamage, critDamageBonus, elementalDamageMod, slayerMod, weaponResistanceMod, resistanceMod, absorbMod, LifeProjectileDamage, lifeMagicDamage, finalDamage); } return(finalDamage); }
/// <summary> /// Creates the Life Magic spell /// </summary> /// <param name="target"></param> /// <param name="spell"></param> /// <param name="spellStatMod"></param> /// <param name="message"></param> /// <param name="castByItem"></param> /// <returns></returns> protected bool LifeMagic(WorldObject target, SpellBase spell, Database.Models.World.Spell spellStatMod, out string message, string castByItem = null) { string srcVital, destVital, action; string targetMsg = null; Player player = null; Creature creature = null; if (WeenieClassId == 1) { player = (Player)this; } else if (WeenieType == WeenieType.Creature) { creature = (Creature)this; } Creature spellTarget; if (spell.BaseRangeConstant == 0) { spellTarget = (Creature)this; } else { spellTarget = (Creature)target; } int newSpellTargetVital; switch (spell.MetaSpellType) { case SpellType.Boost: int minBoostValue, maxBoostValue; if ((spellStatMod.BoostVariance + spellStatMod.Boost) < spellStatMod.Boost) { minBoostValue = (int)(spellStatMod.BoostVariance + spellStatMod.Boost); maxBoostValue = (int)spellStatMod.Boost; } else { minBoostValue = (int)spellStatMod.Boost; maxBoostValue = (int)(spellStatMod.BoostVariance + spellStatMod.Boost); } int boost = Physics.Common.Random.RollDice(minBoostValue, maxBoostValue); if (boost < 0) { action = "drain"; } else { action = "restore"; } switch (spellStatMod.DamageType) { case 512: // Mana newSpellTargetVital = (int)(spellTarget.Mana.Current + boost); srcVital = "mana"; if (newSpellTargetVital < spellTarget.Mana.MaxValue) { if (newSpellTargetVital <= 0) { spellTarget.UpdateVital(spellTarget.Mana, 0); } else { spellTarget.UpdateVital(spellTarget.Mana, (uint)newSpellTargetVital); } } else { spellTarget.UpdateVital(spellTarget.Mana, spellTarget.Mana.MaxValue); } break; case 256: // Stamina newSpellTargetVital = (int)(spellTarget.Stamina.Current + boost); srcVital = "stamina"; if (newSpellTargetVital < spellTarget.Stamina.MaxValue) { if (newSpellTargetVital <= 0) { spellTarget.UpdateVital(spellTarget.Stamina, 0); } else { spellTarget.UpdateVital(spellTarget.Stamina, (uint)newSpellTargetVital); } } else { spellTarget.UpdateVital(spellTarget.Stamina, spellTarget.Stamina.MaxValue); } break; default: // Health newSpellTargetVital = (int)(spellTarget.Health.Current + boost); srcVital = "health"; if (newSpellTargetVital < spellTarget.Health.MaxValue) { if (newSpellTargetVital <= 0) { spellTarget.UpdateVital(spellTarget.Health, 0); } else { spellTarget.UpdateVital(spellTarget.Health, (uint)newSpellTargetVital); } } else { spellTarget.UpdateVital(spellTarget.Health, spellTarget.Health.MaxValue); } break; } if (this is Player) { if (spell.BaseRangeConstant == 0) { message = $"You {action} {Math.Abs(boost).ToString()} {srcVital}"; } else { message = $"You {action} {Math.Abs(boost).ToString()} points of {srcVital} from {spellTarget.Name}"; } } else { message = null; } if (target is Player && spell.BaseRangeConstant > 0) { targetMsg = $"{Name} casts {spell.Name} and {action}s {Math.Abs(boost)} points of your {srcVital}."; } break; case SpellType.Transfer: // Calculate the change in vitals of the target Creature caster; if (spell.BaseRangeConstant == 0 && spell.BaseRangeMod == 1) { caster = spellTarget; } else { caster = (Creature)this; } uint vitalChange, casterVitalChange; ResistanceType resistanceDrain, resistanceBoost; if (spellStatMod.Source == (int)PropertyAttribute2nd.Mana) { resistanceDrain = ResistanceType.ManaDrain; } else if (spellStatMod.Source == (int)PropertyAttribute2nd.Stamina) { resistanceDrain = ResistanceType.StaminaDrain; } else { resistanceDrain = ResistanceType.HealthDrain; } vitalChange = (uint)((spellTarget.GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source) * spellStatMod.Proportion) * spellTarget.GetNaturalResistence(resistanceDrain)); if (spellStatMod.TransferCap != 0) { if (vitalChange > spellStatMod.TransferCap) { vitalChange = (uint)spellStatMod.TransferCap; } } if (spellStatMod.Destination == (int)PropertyAttribute2nd.Mana) { resistanceBoost = ResistanceType.ManaDrain; } else if (spellStatMod.Source == (int)PropertyAttribute2nd.Stamina) { resistanceBoost = ResistanceType.StaminaDrain; } else { resistanceBoost = ResistanceType.HealthDrain; } casterVitalChange = (uint)((vitalChange * (1.0f - spellStatMod.LossPercent)) * spellTarget.GetNaturalResistence(resistanceBoost)); vitalChange = (uint)(casterVitalChange / (1.0f - spellStatMod.LossPercent)); newSpellTargetVital = (int)(spellTarget.GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source) - vitalChange); // Apply the change in vitals to the target switch (spellStatMod.Source) { case (int)PropertyAttribute2nd.Mana: srcVital = "mana"; if (newSpellTargetVital <= 0) { spellTarget.UpdateVital(spellTarget.Mana, 0); } else { spellTarget.UpdateVital(spellTarget.Mana, (uint)newSpellTargetVital); } break; case (int)PropertyAttribute2nd.Stamina: srcVital = "stamina"; if (newSpellTargetVital <= 0) { spellTarget.UpdateVital(spellTarget.Stamina, 0); } else { spellTarget.UpdateVital(spellTarget.Stamina, (uint)newSpellTargetVital); } break; default: // Health srcVital = "health"; if (newSpellTargetVital <= 0) { spellTarget.UpdateVital(spellTarget.Health, 0); } else { spellTarget.UpdateVital(spellTarget.Health, (uint)newSpellTargetVital); } break; } // Apply the scaled change in vitals to the caster uint newCasterVital; switch (spellStatMod.Destination) { case (int)PropertyAttribute2nd.Mana: destVital = "mana"; newCasterVital = caster.Mana.Current + casterVitalChange; caster.UpdateVital(caster.Mana, newCasterVital); break; case (int)PropertyAttribute2nd.Stamina: destVital = "stamina"; newCasterVital = caster.Stamina.Current + casterVitalChange; caster.UpdateVital(caster.Stamina, newCasterVital); break; default: // Health destVital = "health"; newCasterVital = caster.Mana.Current + casterVitalChange; caster.UpdateVital(caster.Health, newCasterVital); break; } if (WeenieClassId == 1) { if (target.Guid == Guid) { message = $"You drain {vitalChange.ToString()} points of {srcVital} and apply {casterVitalChange.ToString()} points of {destVital} to yourself"; } else { message = $"You drain {vitalChange.ToString()} points of {srcVital} from {spellTarget.Name} and apply {casterVitalChange.ToString()} to yourself"; } } else { message = null; } if (target is Player && target != this) { targetMsg = $"You lose {vitalChange} points of {srcVital} due to {Name} casting {spell.Name} on you"; } break; case SpellType.LifeProjectile: caster = (Creature)this; uint damage; if (spell.Name.Contains("Blight")) { damage = (uint)(caster.GetCurrentCreatureVital(PropertyAttribute2nd.Mana) * caster.GetNaturalResistence(ResistanceType.ManaDrain)); newCasterVital = caster.Mana.Current - damage; if (newCasterVital <= 0) { caster.UpdateVital(caster.Mana, 0); } else { caster.UpdateVital(caster.Mana, (uint)newCasterVital); } } else if (spell.Name.Contains("Tenacity")) { damage = (uint)(spellTarget.GetCurrentCreatureVital(PropertyAttribute2nd.Stamina) * spellTarget.GetNaturalResistence(ResistanceType.StaminaDrain)); newCasterVital = caster.Stamina.Current - damage; if (newCasterVital <= 0) { caster.UpdateVital(caster.Stamina, 0); } else { caster.UpdateVital(caster.Stamina, (uint)newCasterVital); } } else { damage = (uint)(spellTarget.GetCurrentCreatureVital(PropertyAttribute2nd.Stamina) * spellTarget.GetNaturalResistence(ResistanceType.HealthDrain)); newCasterVital = caster.Health.Current - damage; if (newCasterVital <= 0) { caster.UpdateVital(caster.Health, 0); } else { caster.UpdateVital(caster.Health, (uint)newCasterVital); } } CreateSpellProjectile(this, target, spell.MetaSpellId, (uint)spellStatMod.Wcid, damage); if (caster.Health.Current <= 0) { caster.Die(); if (caster.WeenieClassId == 1) { Strings.DeathMessages.TryGetValue(DamageType.Base, out var messages); player.Session.Network.EnqueueSend(new GameMessageSystemChat("You have killed yourself", ChatMessageType.Broadcast)); } } message = null; break; case SpellType.Dispel: message = "Spell not implemented, yet!"; break; case SpellType.Enchantment: message = CreateEnchantment(target, spell, spellStatMod, castByItem); break; default: message = "Spell not implemented, yet!"; break; } if (targetMsg != null) { var playerTarget = target as Player; playerTarget.Session.Network.EnqueueSend(new GameMessageSystemChat(targetMsg, ChatMessageType.Magic)); } if (spellTarget.Health.Current == 0) { return(true); } else { return(false); } }