using System; using Server; using Server.Spells; using Server.Network; using Server.Mobiles; using System.Collections; using System.Collections.Generic; using Server.Engines.PartySystem; using Server.Targeting; using Server.Items; using Server.Commands; using System.Linq; namespace Server.Spells.SkillMasteries { public abstract class SkillMasterySpell : Spell { public static void Initialize() { CommandSystem.Register("LearnAllMasteries", AccessLevel.GameMaster, LearnAllSpells); CommandSystem.Register("RandomMastery", AccessLevel.GameMaster, RandomMastery); } public UpkeepTimer Timer { get; set; } public Mobile Target { get; set; } public List PartyList { get; set; } public DateTime Expires { get; set; } public virtual double RequiredSkill { get { return 90.0; } } public virtual double UpKeep { get { return 0; } } public virtual int RequiredMana { get { return 10; } } public virtual bool PartyEffects { get { return false; } } public virtual int DamageThreshold { get { return 45; } } public virtual bool DamageCanDisrupt { get { return false; } } public virtual double TickTime { get { return 2; } } public virtual int PartyRange { get { return 12; } } public virtual int UpkeepCancelMessage { get { return 1156111; } } // You do not have enough mana to keep your ability active. public virtual int OutOfRangeMessage { get { return 1156098; } } // Your target is no longer in range of your ability. public virtual int DisruptMessage { get { return 1156110; } } // Your ability was canceled. public virtual bool CancelsWeaponAbility { get { return false; } } public virtual bool CancelsSpecialMove { get { return CancelsWeaponAbility; } } public virtual bool RevealOnTick { get { return true; } } public virtual TimeSpan ExpirationPeriod { get { return TimeSpan.FromMinutes(30); } } public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds(2.25); } } public virtual double BaseSkillBonus { get { if(Caster == null) return 0.0; return (Caster.Skills[CastSkill].Value + Caster.Skills[DamageSkill].Value + (GetMasteryLevel() * 40)) / 3; } } public virtual double CollectiveBonus { get { return 0; } } public override bool ClearHandsOnCast{ get{ return false; } } public override bool BlocksMovement{ get{ return true; } } public SkillMasterySpell( Mobile caster, Item scroll, SpellInfo info ) : base( caster, null, info ) { } public override bool CheckCast() { int mana = ScaleMana( RequiredMana ); if ( !base.CheckCast() ) return false; if (IsInCooldown(Caster, this.GetType())) return false; if (Caster.Player && Caster.Skills[CastSkill].Value < RequiredSkill) Caster.SendLocalizedMessage(1115709); // Your skills are not high enough to invoke this mastery ability. else if (Caster is PlayerMobile && Caster.Skills.CurrentMastery != CastSkill) Caster.SendLocalizedMessage(1115664); // You are not on the correct path for using this mastery ability. else if (Caster is PlayerMobile && !MasteryInfo.HasLearned(Caster, CastSkill)) Caster.SendLocalizedMessage(1115664); // You are not on the correct path for using this mastery ability. else if (Caster.Mana < mana) Caster.SendLocalizedMessage(1060174, mana.ToString()); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. else { if (CancelsWeaponAbility) WeaponAbility.ClearCurrentAbility(Caster); if (CancelsSpecialMove) SpecialMove.ClearCurrentMove(Caster); return true; } return false; } public override void DoFizzle() { Caster.LocalOverheadMessage(MessageType.Regular, 0x3B2, 502632); // The spell fizzles. if (Caster.Player) { Caster.FixedParticles(0x3735, 1, 30, 9503, EffectLayer.Waist); //m_Caster.PlaySound(0x5C); } } public override void GetCastSkills( out double min, out double max ) { min = RequiredSkill; max = RequiredSkill + 25.0; } public override int GetMana() { return RequiredMana; } public BaseWeapon GetWeapon() { return Caster.Weapon as BaseWeapon; } public bool CheckWeapon() { if (!Caster.Player) return true; BaseWeapon wep = GetWeapon(); if (CastSkill == SkillName.Poisoning && wep != null && !(wep is Fists)) return true; return wep != null && wep.DefSkill == CastSkill; } public virtual bool OnTick() { UpdateParty(); if (RevealOnTick) { Caster.RevealingAction(); } int upkeep = ScaleUpkeep(); if (0.10 > Utility.RandomDouble()) { NegativeAttributes.OnCombatAction(Caster); } if ((Caster is PlayerMobile && Caster.NetState == null) || Expires < DateTime.UtcNow || !Caster.Alive || Caster.IsDeadBondedPet) { Expire(); } else if (Target != null && !Target.Alive) { Expire(); } else if (Target != null && !Caster.InRange(Target.Location, PartyRange)) { Expire(); if (OutOfRangeMessage > 0) Caster.SendLocalizedMessage(OutOfRangeMessage); } else if (Caster.Mana < upkeep) { if (UpkeepCancelMessage > 0) Caster.SendLocalizedMessage(UpkeepCancelMessage); Expire(); } else if (Caster.Player && Caster.Skills[CastSkill].Value < RequiredSkill) { Expire(); } else { DoEffects(); Caster.Mana -= upkeep; return true; } return false; } protected virtual void DoEffects() { } public virtual int PropertyBonus() { return 0; } public virtual int PropertyBonus2() { return 0; } //Used for hits/stam/mana public virtual int StatBonus() { return 0; } public virtual void AddStatMods() { } public virtual void RemoveStatMods() { } public virtual void EndEffects() { } public virtual int DamageBonus() { return 0; } public virtual void OnDamaged(Mobile attacker, Mobile defender, DamageType type, ref int damage) { } public virtual void OnHit(Mobile defender, ref int damage) { } public virtual void OnGotHit(Mobile attacker, ref int damage) { } public virtual void OnMiss(Mobile defender) { } public virtual void OnGotMiss(Mobile attacker) { } public virtual void OnParried(Mobile attacker) { } public virtual void OnGotParried(Mobile defender) { } public virtual void OnTargetDamaged(Mobile attacker, Mobile victim, DamageType type, ref int damage) { } public virtual void OnWeaponRemoved(BaseWeapon weapon) { } public virtual int ScaleUpkeep() { if (UpKeep == 0) return 0; double mod = CollectiveBonus; double upkeep = UpKeep; int mana = (int)(upkeep - ((upkeep * mod) / 4.5)); return ScaleMana(mana); } public virtual void Expire(bool disrupt = false) { if (Timer != null) { Timer.Stop(); Timer = null; } if(disrupt && DisruptMessage > 0) Caster.SendLocalizedMessage(DisruptMessage); Server.Timer.DelayCall(RemoveFromTable); RemoveStatMods(); EndEffects(); Caster.Delta(MobileDelta.WeaponDamage); if(Target != null) Target.Delta(MobileDelta.WeaponDamage); if (PartyList != null) { foreach(Mobile m in PartyList) m.Delta(MobileDelta.WeaponDamage); ColUtility.Free(PartyList); } OnExpire(); } public virtual void OnExpire() { } public virtual double DamageModifier(Mobile victim) { double dSkill = Caster.Skills[DamageSkill].Value; double vSkill = GetResistSkill(victim); double reduce = 1.0 - ((dSkill - vSkill) / dSkill); if(reduce < 0) reduce = 0; if(reduce > 1) reduce = 1; return reduce; } public virtual bool CheckResisted(Mobile target) { int volumeMod = GetMasteryLevel(); volumeMod *= 2; double n = GetResistPercent(target, volumeMod); n /= 100.0; if (n <= 0.0) return false; if (n >= 1.0) return true; int maxSkill = (1 + volumeMod) * 10; maxSkill += (1 + (volumeMod / 6)) * 25; if (target.Skills[SkillName.MagicResist].Value < maxSkill) target.CheckSkill(SkillName.MagicResist, 0.0, target.Skills[SkillName.MagicResist].Cap); return (n >= Utility.RandomDouble()); } public virtual double GetResistPercent(Mobile target, int level) { double value = GetResistSkill(target); double firstPercent = value / 5.0; double secondPercent = value - (((Caster.Skills[CastSkill].Value - 20.0) / 5.0) + (1 + level) * 5.0); return (firstPercent > secondPercent ? firstPercent : secondPercent) / 2.0; } public int GetMasteryLevel() { return (int)MasteryInfo.GetMasteryLevel(Caster, CastSkill); } public void UpdateParty() { UpdateParty(false); } public void UpdateParty(bool playersOnly) { var p = Party.Get(Caster); if (PartyList == null) { PartyList = new List(); } if (p != null) { foreach (var m in p.Members.Select(x => x.Mobile)) { if (!PartyList.Contains(m) && (!playersOnly || m is PlayerMobile) && ValidPartyMember(m)) { AddPartyMember(m); } else if (PartyList.Contains(m) && !ValidPartyMember(m)) { RemovePartyMember(m); } else { UpdatePets(m); } } } else if (!PartyList.Contains(Caster)) { AddPartyMember(Caster); } } public virtual bool CheckPartyEffects(Mobile m, bool beneficial = false) { if (m == Caster) return true; if (Caster.IsBeneficialCriminal(m)) { int casterNoto = Notoriety.Compute(Caster, m); int mNoto = Notoriety.Compute(m, Caster); if (casterNoto == Notoriety.Enemy || casterNoto != mNoto) { return false; } } if (beneficial) Caster.DoBeneficial(m); return true; } protected virtual bool ValidPartyMember(Mobile m) { return m.Alive && m.InRange(Caster, PartyRange) && m.Map == Caster.Map && CheckPartyEffects(m); } protected void AddPartyMember(Mobile m) { PartyList.Add(m); AddPartyEffects(m); if (m is PlayerMobile) { foreach (var pet in ((PlayerMobile)m).AllFollowers.Where(p => !PartyList.Contains(p) && ValidPartyMember(p))) { AddPartyMember(pet); } } } protected void RemovePartyMember(Mobile m) { PartyList.Remove(m); RemovePartyEffects(m); if (m is PlayerMobile) { foreach (var pet in ((PlayerMobile)m).AllFollowers.Where(p => PartyList.Contains(p))) { RemovePartyMember(pet); } } } private void UpdatePets(Mobile m) { if (m is PlayerMobile) { foreach (var pet in ((PlayerMobile)m).AllFollowers.Where(p => !PartyList.Contains(p) && ValidPartyMember(p))) { AddPartyMember(pet); } } } public virtual void AddPartyEffects(Mobile m) { } public virtual void RemovePartyEffects(Mobile m) { } public static void OnPartyRemoved(Mobile m, bool disband) { foreach (var spell in GetSpells(s => s.PartyEffects && s.PartyList != null && (s.Caster == m || s.PartyList.Contains(m)))) { if (disband) { spell.PartyList.IterateReverse(mob => { if (mob != spell.Caster || (mob is BaseCreature && ((BaseCreature)mob).GetMaster() != mob)) { spell.RemovePartyMember(mob); } }); } else { spell.RemovePartyMember(m); } } } private static Dictionary> m_Table = new Dictionary>(); public static SkillMasterySpell GetHarmfulSpell(Mobile target, Type type) { foreach (Mobile m in m_Table.Keys) { foreach (SkillMasterySpell spell in m_Table[m]) { if (spell != null && spell.GetType() == type && spell.Target == target) return spell; } } return null; } public static bool HasHarmfulEffects(Mobile target, Type type) { foreach (Mobile m in m_Table.Keys) { foreach (SkillMasterySpell spell in m_Table[m]) { if (spell != null && spell.GetType() == type && spell.Target == target) return true; } } return false; } public static SkillMasterySpell GetSpell(Mobile from, Type type) { CheckTable(from); if (m_Table.ContainsKey(from)) { return m_Table[from].FirstOrDefault(spell => spell != null && spell.GetType() == type); } return null; } public static TSpell GetSpell(Mobile m) where TSpell : SkillMasterySpell { if (m_Table.ContainsKey(m)) { return m_Table[m].FirstOrDefault(sms => sms.GetType() == typeof(TSpell)) as TSpell; } return null; } public static TSpell GetSpell(Mobile caster, Mobile target) where TSpell : SkillMasterySpell { if (m_Table.ContainsKey(caster)) { return m_Table[caster].FirstOrDefault(sms => sms.GetType() == typeof(TSpell) && sms.Target == target) as TSpell; } return null; } public static SkillMasterySpell GetSpell(Func predicate) { foreach (SkillMasterySpell spell in EnumerateAllSpells()) { if (predicate != null && predicate(spell)) return spell; } return null; } public static TSpell GetSpell(Func predicate) where TSpell : SkillMasterySpell { foreach (SkillMasterySpell spell in EnumerateAllSpells().Where(sp => sp.GetType() == typeof(TSpell))) { if (predicate != null && predicate(spell)) return spell as TSpell; } return null; } public static IEnumerable GetSpells(Func predicate) { foreach (SkillMasterySpell spell in EnumerateAllSpells()) { if (spell != null && predicate != null && predicate(spell)) { yield return spell; } } } public static IEnumerable GetSpells(Mobile caster, Func predicate) { foreach (SkillMasterySpell spell in EnumerateSpells(caster)) { if (spell != null && predicate != null && predicate(spell)) { yield return spell; } } } public static bool HasSpell(Mobile from, Type type) { CheckTable(from); if (m_Table.ContainsKey(from)) { return m_Table[from].Any(spell => spell.GetType() == type); } return false; } public static bool HasSpell(Mobile from) where TSpell : SkillMasterySpell { CheckTable(from); if (m_Table.ContainsKey(from)) { return m_Table[from].Any(spell => spell.GetType() == typeof(TSpell)); } return false; } public static bool UnderPartyEffects(Mobile from, Type type) { return GetSpellForParty(from, type) != null; } public static SkillMasterySpell GetSpellForParty(Mobile from, Type type) { CheckTable(from); Mobile check = from; if (from is BaseCreature && (((BaseCreature)from).Controlled || ((BaseCreature)from).Summoned) && ((BaseCreature)from).GetMaster() != null) { check = ((BaseCreature)from).GetMaster(); CheckTable(check); } //First checks the caster if (m_Table.ContainsKey(check)) { foreach (SkillMasterySpell spell in m_Table[check]) { if (spell != null && spell.GetType() == type) return spell; } } else { Party p = Party.Get(check); if (p != null) { foreach (PartyMemberInfo info in p.Members) { SkillMasterySpell spell = GetSpell(info.Mobile, type); if (spell != null && spell.PartyEffects && from.InRange(info.Mobile.Location, spell.PartyRange) && spell.CheckPartyEffects(info.Mobile)) return spell; } } } return null; } private static object _Lock = new object(); public static void CheckTable(Mobile m) { if (m == null) return; lock (_Lock) { if (m_Table.ContainsKey(m)) { if (m_Table[m] == null || m_Table[m].Count == 0) { m_Table.Remove(m); } } } } public static IEnumerable EnumerateAllSpells() { if (m_Table == null || m_Table.Count == 0) yield break; List list = new List(); lock (_Lock) { foreach (KeyValuePair> kvp in m_Table) { list.AddRange(kvp.Value); } } foreach (SkillMasterySpell spell in list) { yield return spell; } } public static IEnumerable EnumerateSpells(Mobile from, Type t = null) { if (m_Table == null || m_Table.Count == 0 || !m_Table.ContainsKey(from)) yield break; List list; lock (_Lock) { list = m_Table[from]; if (list == null || list.Count == 0) { yield break; } } IEnumerable e; lock (_Lock) { e = list.Where(s => s.GetType() == t || t == null); } foreach (SkillMasterySpell spell in e) { yield return spell; } } public static List GetSpells(Mobile m) { if (m_Table != null && m_Table.ContainsKey(m)) { return new List(m_Table[m]); } return null; } protected void AddToTable(Mobile from, SkillMasterySpell spell) { lock (_Lock) { if (!m_Table.ContainsKey(from)) { m_Table[from] = new List(); } if (!m_Table[from].Contains(spell)) { m_Table[from].Add(spell); } } } protected void RemoveFromTable() { if(m_Table.ContainsKey(Caster) && m_Table[Caster].Contains(this)) { lock (_Lock) { m_Table[Caster].Remove(this); if (m_Table[Caster].Count == 0) { m_Table.Remove(Caster); } } } } /// /// Called in Aos.cs, should include all damage types /// /// /// /// public static void OnDamage(Mobile victim, Mobile damager, DamageType type, ref int damage) { if(victim == null || damager == null) return; CheckTable(victim); foreach (SkillMasterySpell sp in EnumerateSpells(victim)) { if (sp.DamageCanDisrupt && damage > sp.DamageThreshold) sp.Expire(true); sp.OnDamaged(damager, victim, type, ref damage); } foreach (SkillMasterySpell sp in GetSpells(s => s.Target == victim)) { sp.OnTargetDamaged(damager, victim, type, ref damage); } SkillMasteryMove move = SpecialMove.GetCurrentMove(victim) as SkillMasteryMove; if (move != null) move.OnDamaged(damager, victim, type, ref damage); PerseveranceSpell preserve = SkillMasterySpell.GetSpellForParty(victim, typeof(PerseveranceSpell)) as PerseveranceSpell; if (preserve != null) preserve.AbsorbDamage(ref damage); InspireSpell inspire = SkillMasterySpell.GetSpellForParty(damager, typeof(InspireSpell)) as InspireSpell; if (inspire != null) inspire.DoDamage(ref damage); CombatTrainingSpell.CheckDamage(damager, victim, type, ref damage); } /// /// Called in BaseWeapon, intended as a melee/ranged hit /// /// /// /// public static void OnHit(Mobile attacker, Mobile defender, ref int damage) { if (attacker == null || defender == null) return; foreach(SkillMasterySpell spell in EnumerateSpells(attacker)) { spell.OnHit(defender, ref damage); } foreach (SkillMasterySpell spell in EnumerateSpells(defender)) { spell.OnGotHit(defender, ref damage); } SkillMasteryMove move = SpecialMove.GetCurrentMove(defender) as SkillMasteryMove; if (move != null) move.OnGotHit(attacker, defender, ref damage); if(attacker is BaseCreature || defender is BaseCreature) CombatTrainingSpell.OnCreatureHit(attacker, defender, ref damage); } /// /// Called in BaseWeapon, intended as a melee/ranged miss /// /// /// public static void OnMiss(Mobile attacker, Mobile defender) { if (attacker == null || defender == null) return; foreach (SkillMasterySpell spell in EnumerateSpells(attacker)) { spell.OnMiss(defender); } foreach (SkillMasterySpell spell in EnumerateSpells(defender)) { spell.OnGotMiss(attacker); } } /// /// Called in BaseWeapon, intended as a melee/ranged parry /// /// /// public static void OnParried(Mobile attacker, Mobile defender) { if (attacker == null || defender == null) return; foreach (SkillMasterySpell spell in EnumerateSpells(defender)) { spell.OnParried(attacker); } foreach (SkillMasterySpell spell in EnumerateSpells(attacker)) { spell.OnGotParried(defender); } } public static bool HasMastery(Mobile mobile, SkillName name) { Skill sk = mobile.Skills[name]; return sk.IsMastery && sk.VolumeLearned != 0; } public static bool SetActiveMastery(Mobile mobile, SkillName name) { return mobile.Skills[name].SetCurrent(); } protected virtual void BeginTimer() { if(Timer != null) { Timer.Stop(); Timer = null; } Timer = new UpkeepTimer(this); Timer.Start(); if(Expires < DateTime.UtcNow) Expires = DateTime.UtcNow + ExpirationPeriod; AddToTable(Caster, this); AddStatMods(); if (RevealOnTick) { Caster.RevealingAction(); } Caster.Delta(MobileDelta.WeaponDamage); if(Target != null) Target.Delta(MobileDelta.WeaponDamage); if (PartyList != null) { foreach (Mobile m in PartyList) m.Delta(MobileDelta.WeaponDamage); } } public int GetWeaponSkill() { double swrd = Caster.Skills[SkillName.Swords].Value; double fenc = Caster.Skills[SkillName.Fencing].Value; double mcng = Caster.Skills[SkillName.Macing].Value; double arch = Caster.Skills[SkillName.Archery].Value; double thro = Caster.Skills[SkillName.Throwing].Value; double wres = Caster.Skills[SkillName.Wrestling].Value; double val = swrd; if (fenc > val) val = fenc; if (mcng > val) val = mcng; if (arch > val) val = arch; if (thro > val) val = thro; if (wres > val) val = wres; return (int)val; } public static void OnWeaponRemoved(Mobile from, BaseWeapon weapon) { foreach (SkillMasterySpell spell in EnumerateSpells(from)) { spell.OnWeaponRemoved(weapon); } } public static void CancelWeaponAbility(Mobile attacker) { foreach (SkillMasterySpell spell in EnumerateSpells(attacker).Where(s => s.CancelsWeaponAbility)) { spell.Expire(); } } public static void CancelSpecialMove(Mobile attacker) { foreach (SkillMasterySpell spell in EnumerateSpells(attacker).Where(s => s.CancelsSpecialMove)) { spell.Expire(); } } private static Dictionary _Cooldown; protected void AddToCooldown(TimeSpan ts) { if (_Cooldown == null) _Cooldown = new Dictionary(); _Cooldown[this] = DateTime.UtcNow + ts; Server.Timer.DelayCall(ts, () => RemoveFromCooldown(this.GetType(), Caster)); } public static bool IsInCooldown(Mobile m, Type type, bool message = true) { CheckCooldown(); if (_Cooldown == null) return false; bool iscooling = false; foreach (KeyValuePair kvp in _Cooldown) { SkillMasterySpell spell = kvp.Key; DateTime dt = kvp.Value; if (!iscooling && spell.GetType() == type && spell.Caster == m) { if (message) { double left = (dt - DateTime.UtcNow).TotalMinutes; if (left > 1) m.SendLocalizedMessage(1155787, ((int)left).ToString()); // You must wait ~1_minutes~ minutes before you can use this ability. else { left = (_Cooldown[spell] - DateTime.UtcNow).TotalSeconds; if(left > 0) m.SendLocalizedMessage(1079335, left.ToString("F", System.Globalization.CultureInfo.InvariantCulture)); // You must wait ~1_seconds~ seconds before you can use this ability again. } } iscooling = true; } } return iscooling; } public static void RemoveFromCooldown(Type type, Mobile m) { if (_Cooldown == null) return; SkillMasterySpell spell = null; foreach (KeyValuePair kvp in _Cooldown) { if (kvp.Key.GetType() == type && kvp.Key.Caster == m) spell = kvp.Key; } if (spell != null) _Cooldown.Remove(spell); CheckCooldown(); } public static void CheckCooldown() { if (_Cooldown == null) return; List spells = new List(); foreach (KeyValuePair kvp in _Cooldown) { if (kvp.Value < DateTime.UtcNow) spells.Add(kvp.Key); } spells.ForEach(sp => _Cooldown.Remove(sp)); if (_Cooldown != null && _Cooldown.Count == 0) _Cooldown = null; } public static int GetAttributeBonus(Mobile m, AosAttribute attr) { int value = 0; SkillMasterySpell spell = null; switch (attr) { case AosAttribute.AttackChance: spell = SkillMasterySpell.GetSpellForParty(m, typeof(InspireSpell)); if (spell != null) value += spell.PropertyBonus(); spell = SkillMasterySpell.GetSpellForParty(m, typeof(TribulationSpell)); if (spell != null) value += spell.PropertyBonus(); value += FocusedEyeSpell.HitChanceBonus(m); value += PlayingTheOddsSpell.HitChanceBonus(m); value += CalledShotSpell.GetHitChanceBonus(m); value += CombatTrainingSpell.GetHitChanceBonus(m); value += MasteryInfo.SavingThrowChance(m, attr); break; case AosAttribute.DefendChance: spell = SkillMasterySpell.GetSpellForParty(m, typeof(PerseveranceSpell)); if (spell != null) value += spell.PropertyBonus(); if (Server.Spells.SkillMasteries.WhiteTigerFormSpell.IsActive(m)) value += 20; value += MasteryInfo.SavingThrowChance(m, attr); break; case AosAttribute.RegenHits: spell = SkillMasterySpell.GetSpellForParty(m, typeof(ResilienceSpell)); if (spell != null) value += spell.PropertyBonus(); break; case AosAttribute.RegenStam: spell = SkillMasterySpell.GetSpellForParty(m, typeof(ResilienceSpell)); if (spell != null) value += spell.PropertyBonus(); break; case AosAttribute.RegenMana: spell = SkillMasterySpell.GetSpellForParty(m, typeof(ResilienceSpell)); if (spell != null) value += spell.PropertyBonus(); break; case AosAttribute.WeaponDamage: spell = SkillMasterySpell.GetSpellForParty(m, typeof(InspireSpell)); if (spell != null) value += spell.DamageBonus(); value += MasteryInfo.SavingThrowChance(m, attr); break; case AosAttribute.SpellDamage: spell = SkillMasterySpell.GetSpellForParty(m, typeof(InspireSpell)); if (spell != null) value += spell.PropertyBonus(); break; case AosAttribute.WeaponSpeed: value += RampageSpell.GetBonus(m, RampageSpell.BonusType.SwingSpeed); value += PlayingTheOddsSpell.SwingSpeedBonus(m); value -= StaggerSpell.GetStagger(m); break; case AosAttribute.BonusStr: value += MasteryInfo.SavingThrowChance(m, attr); break; } return value; } public static int GetAttributeBonus(Mobile m, SAAbsorptionAttribute attr) { int value = 0; switch (attr) { case SAAbsorptionAttribute.CastingFocus: SkillMasterySpell spell = SkillMasterySpell.GetSpellForParty(m, typeof(PerseveranceSpell)); if (spell != null) value += spell.PropertyBonus2(); break; } return value; } protected virtual void OnTarget(object o) { } public class MasteryTarget : Target { public SkillMasterySpell Owner { get; set; } public bool AutoFinishSequence { get; set; } public MasteryTarget(SkillMasterySpell spell, int range = 10, bool allowGround = false, TargetFlags flags = TargetFlags.None, bool autoEnd = true) : base(range, allowGround, flags) { Owner = spell; AutoFinishSequence = autoEnd; } protected override void OnTarget(Mobile from, object o) { if (o == null) return; if (!from.CanSee(o)) from.SendLocalizedMessage(500237); // Target can not be seen. else { SpellHelper.Turn(from, o); Owner.OnTarget(o); } } protected override void OnTargetFinish(Mobile from) { if(AutoFinishSequence) Owner.FinishSequence(); } } public class UpkeepTimer : Timer { private SkillMasterySpell m_Spell; public SkillMasterySpell Spell { get { return m_Spell; } } public UpkeepTimer(SkillMasterySpell spell) : base(TimeSpan.FromSeconds(spell.TickTime), TimeSpan.FromSeconds(spell.TickTime)) { m_Spell = spell; } protected override void OnTick() { if(m_Spell != null) m_Spell.OnTick(); } } [Usage("LearnAllMasteries")] [Description("Select a target to learn all skill masteries.")] public static void LearnAllSpells(CommandEventArgs e) { e.Mobile.SendMessage("Target the mobile who will learn all skill masteries."); e.Mobile.BeginTarget(-1, false, TargetFlags.None, (mobile, targeted) => { if (targeted is Mobile) { Mobile m = targeted as Mobile; int count = 0; foreach (Skill sk in m.Skills) { if (sk.IsMastery) { count++; sk.VolumeLearned = 3; } } e.Mobile.SendMessage("They have learned {0} masteries!", count.ToString()); } }); } [Usage("RandomMastery")] [Description("Drops a random mastery primer on target.")] public static void RandomMastery(CommandEventArgs e) { e.Mobile.BeginTarget(-1, false, TargetFlags.None, (mobile, targeted) => { Item mastery = SkillMasteryPrimer.GetRandom(); if (targeted is Mobile) { Mobile m = targeted as Mobile; m.Backpack.DropItem(mastery); e.Mobile.SendMessage("A mastery has been added to your pack!"); } else if (targeted is IPoint3D) { IPoint3D p = targeted as IPoint3D; mastery.MoveToWorld(new Point3D(p.X, p.Y, p.Z), e.Mobile.Map); } else mastery.Delete(); }); } } }