using System; using System.Collections.Generic; using Server.Items; using Server.Network; using Server.Spells.Bushido; using Server.Spells.Ninjitsu; using Server.Spells.SkillMasteries; namespace Server.Spells { public abstract class SpecialMove { public virtual int BaseMana { get { return 0; } } public virtual SkillName MoveSkill { get { return SkillName.Bushido; } } public virtual double RequiredSkill { get { return 0.0; } } public virtual TextDefinition AbilityMessage { get { return 0; } } public virtual bool BlockedByAnimalForm { get { return true; } } public virtual bool DelayedContext { get { return false; } } public virtual int GetAccuracyBonus(Mobile attacker) { return 0; } public virtual double GetDamageScalar(Mobile attacker, Mobile defender) { return 1.0; } // Called before swinging, to make sure the accuracy scalar is to be computed. public virtual bool OnBeforeSwing(Mobile attacker, Mobile defender) { return true; } // Called when a hit connects, but before damage is calculated. public virtual bool OnBeforeDamage(Mobile attacker, Mobile defender) { return true; } // Called as soon as the ability is used. public virtual void OnUse(Mobile from) { } // Called when a hit connects, at the end of the weapon.OnHit() method. public virtual void OnHit(Mobile attacker, Mobile defender, int damage) { } // Called when a hit misses. public virtual void OnMiss(Mobile attacker, Mobile defender) { } // Called when the move is cleared. public virtual void OnClearMove(Mobile from) { } public virtual void SendAbilityMessage(Mobile m) { TextDefinition.SendMessageTo(m, AbilityMessage); } public virtual bool IgnoreArmor(Mobile attacker) { return false; } public virtual double GetPropertyBonus(Mobile attacker) { return 1.0; } public virtual bool CheckSkills(Mobile m) { if (m.Skills[this.MoveSkill].Value < this.RequiredSkill) { string args = String.Format("{0}\t{1}\t ", this.RequiredSkill.ToString("F1"), this.MoveSkill.ToString()); m.SendLocalizedMessage(1063013, args); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability. return false; } return true; } public virtual int ScaleMana(Mobile m, int mana) { double scalar = 1.0; if (ManaPhasingOrb.IsInManaPhase(m)) { ManaPhasingOrb.RemoveFromTable(m); return 0; } if (!Server.Spells.Necromancy.MindRotSpell.GetMindRotScalar(m, ref scalar)) { scalar = 1.0; } if (Server.Spells.Mysticism.PurgeMagicSpell.IsUnderCurseEffects(m)) { scalar += .5; } // Lower Mana Cost = 40% int lmc = Math.Min(AosAttributes.GetValue(m, AosAttribute.LowerManaCost), 40); lmc += BaseArmor.GetInherentLowerManaCost(m); scalar -= (double)lmc / 100; int total = (int)(mana * scalar); if (m.Skills[this.MoveSkill].Value < 50.0 && GetContext(m) != null) total *= 2; return total; } public virtual bool CheckMana(Mobile from, bool consume) { int mana = this.ScaleMana(from, this.BaseMana); if (from.Mana < mana) { from.SendLocalizedMessage(1060181, mana.ToString()); // You need ~1_MANA_REQUIREMENT~ mana to perform that attack return false; } if (consume) { if (!this.DelayedContext) this.SetContext(from); from.Mana -= mana; } return true; } public virtual void SetContext(Mobile from) { if (GetContext(from) == null) { if (this.DelayedContext || from.Skills[this.MoveSkill].Value < 50.0) { Timer timer = new SpecialMoveTimer(from); timer.Start(); AddContext(from, new SpecialMoveContext(timer, this.GetType())); } } } public virtual bool Validate(Mobile from) { if (!from.Player) return true; if (Bushido.HonorableExecution.IsUnderPenalty(from)) { from.SendLocalizedMessage(1063024); // You cannot perform this special move right now. return false; } if (Ninjitsu.AnimalForm.UnderTransformation(from)) { from.SendLocalizedMessage(1063024); // You cannot perform this special move right now. return false; } return this.CheckSkills(from) && this.CheckMana(from, false); } public virtual void CheckGain(Mobile m) { m.CheckSkill(this.MoveSkill, this.RequiredSkill, this.RequiredSkill + 37.5); } private static readonly Dictionary m_Table = new Dictionary(); public static Dictionary Table { get { return m_Table; } } public static void ClearAllMoves(Mobile m) { foreach (KeyValuePair kvp in SpellRegistry.SpecialMoves) { int moveID = kvp.Key; if (moveID != -1) m.Send(new ToggleSpecialAbility(moveID + 1, false)); } } public virtual bool ValidatesDuringHit { get { return true; } } public static SpecialMove GetCurrentMove(Mobile m) { if (m == null) return null; if (!Core.SE) { ClearCurrentMove(m); return null; } SpecialMove move = null; m_Table.TryGetValue(m, out move); if (move != null && move.ValidatesDuringHit && !move.Validate(m)) { ClearCurrentMove(m); return null; } return move; } public static bool SetCurrentMove(Mobile m, SpecialMove move) { if (!Core.SE) { ClearCurrentMove(m); return false; } if (move != null && !move.Validate(m)) { ClearCurrentMove(m); return false; } bool sameMove = (move == GetCurrentMove(m)); ClearCurrentMove(m); if (sameMove) return true; if (move != null) { WeaponAbility.ClearCurrentAbility(m); m_Table[m] = move; move.OnUse(m); int moveID = SpellRegistry.GetRegistryNumber(move); if (moveID > 0) m.Send(new ToggleSpecialAbility(moveID + 1, true)); move.SendAbilityMessage(m); SkillMasterySpell.CancelSpecialMove(m); } return true; } public static void ClearCurrentMove(Mobile m) { SpecialMove move = null; m_Table.TryGetValue(m, out move); if (move != null) { move.OnClearMove(m); int moveID = SpellRegistry.GetRegistryNumber(move); if (moveID > 0) m.Send(new ToggleSpecialAbility(moveID + 1, false)); } m_Table.Remove(m); } public SpecialMove() { } private static readonly Dictionary m_PlayersTable = new Dictionary(); private static void AddContext(Mobile m, SpecialMoveContext context) { m_PlayersTable[m] = context; } private static void RemoveContext(Mobile m) { SpecialMoveContext context = GetContext(m); if (context != null) { m_PlayersTable.Remove(m); context.Timer.Stop(); } } private static SpecialMoveContext GetContext(Mobile m) { return (m_PlayersTable.ContainsKey(m) ? m_PlayersTable[m] : null); } public static bool GetContext(Mobile m, Type type) { SpecialMoveContext context = null; m_PlayersTable.TryGetValue(m, out context); if (context == null) return false; return (context.Type == type); } private class SpecialMoveTimer : Timer { private readonly Mobile m_Mobile; public SpecialMoveTimer(Mobile from) : base(TimeSpan.FromSeconds(3.0)) { this.m_Mobile = from; this.Priority = TimerPriority.TwentyFiveMS; } protected override void OnTick() { RemoveContext(this.m_Mobile); } } public class SpecialMoveContext { private readonly Timer m_Timer; private readonly Type m_Type; public Timer Timer { get { return this.m_Timer; } } public Type Type { get { return this.m_Type; } } public SpecialMoveContext(Timer timer, Type type) { this.m_Timer = timer; this.m_Type = type; } } } }