/// <summary>Called by CastSpell() & UseItemEffect().</summary>
        /// <returns>True if spell was successfully cast.</returns>
        /// <param name="slotId">Slot ID of the spell slot that is being cast: see SpellSlot enum.</param>
        /// <param name="targetId">Zero for no target</param>
        internal virtual bool BeginSpell(Spell spell, uint slotId, uint targetId)
        {
            uint origCastTime;
            _castingSpellSlotId = (ushort)slotId;

            // TODO: provide a parameter for a timer, timer duration and type?

            SaveSpellCastLoc();

            // If the spell doesn't require a target or it's a target optional spell and a target doesn't exist, then it's us;
            // unless TGB is on and the spell is TGB compatible
            if ((spell.IsGroupSpell ||
                spell.TargetType == (short)SpellTargetType.Self ||
                spell.TargetType == (short)SpellTargetType.AECaster ||
                spell.TargetType == (short)SpellTargetType.TargetOptional) && targetId == 0u)
            {
                _log.DebugFormat("Spell {0} auto-targeted the caster.  Group: {1}, Target Type: {2}", spell.SpellName, spell.IsGroupSpell, spell.TargetType);
                this.TargetMob = this;
            }

            origCastTime = spell.CastTime;
            if (spell.CastTime > 0)
                spell.CastTime = GetActualSpellCastTime(spell);     // Check for modified casting times

            // If we don't have a target, it's an issue
            if (this.TargetMob == null) {
                _log.DebugFormat("{0} cast spell {1} without a target - Canceling", this.Name, spell.SpellName);
                if (this is ZonePlayer) {
                    ((ZonePlayer)this).MsgMgr.SendMessageID((uint)MessageType.Common13, MessageStrings.SPELL_NEED_TAR);
                    InterruptSpell(null);
                }
                else
                    InterruptSpell(null);

                return false;
            }

            if (spell.Mana > 0)
                spell.Mana = GetActualManaCost(spell);  // Check for modified mana costs

            // Now see if we have enough mana to cast the spell
            if (spell.Mana > 0 && slotId != (uint)SpellSlot.UseItem) {
                if (this.Mana < spell.Mana) {

                    // Let NPCs with no mana cast the spell / use the ability
                    if (this is NpcMob && this.Mana == this.MaxMana)
                        spell.Mana = 0;
                    else {
                        _log.DebugFormat("{0} has insufficient mana for spell {1}: cur mana: {2}, mana cost: {3}", this.Name, spell.SpellName, this.Mana, spell.Mana);
                        if (this is ZonePlayer) {
                            ((ZonePlayer)this).MsgMgr.SendMessageID((uint)MessageType.Common13, MessageStrings.InsufficientMana);
                            InterruptSpell(null);
                        }
                        else
                            InterruptSpell(null);

                        return false;
                    }
                }
            }

            this.SpellTargetMob = this.TargetMob;   // Store the spell target and...
            _castingSpell = spell;                  // ...the spell for when the spell completes

            // If cast time is 0 finish it now
            if (spell.CastTime == 0) {
                FinishSpell(spell, slotId, null);
                return true;
            }

            _spellEndTimer.Start((int)spell.CastTime);   // Spell has a cast time so start the cast timer

            // If we're under AI, face our target (unless it's ourself)
            if (this.IsAIControlled) {
                this.RunAnimSpeed = 0;
                if (this != this.TargetMob)
                    FaceMob(this.TargetMob);
            }

            // Use original cast time, as the client calcs any reduced cast times on its own
            OnSpellCastBegun(new BeginCastEventArgs() { SpellId = spell.SpellID, CastTime = origCastTime });
            return true;
        }
 /// <summary>Based upon several factors, the spell being cast can have different targets.  This method determines those targets.</summary>
 protected bool DetermineSpellTargets(Spell spell, Mob target, ref Mob aeCenter, out CastActionType cat)
 {
     cat = CastActionType.AECaster;
     return false;   // TODO: unfinished
 }
 /// <summary>Resets casting state variables</summary>
 private void ResetCastingVars()
 {
     _hitWhileCastCount = 0;
     _spellEndTimer.Stop();
     _delayTimer = false;
     _castingSpell = null;
     _spellTarget = null;
     _castingSpellSlotId = 0;
     _castingSpellInvSlotId = 0;
     _castingSpellTimer = 0;
     _castingSpellTimerDuration = 0;
 }
 internal virtual short GetActualManaCost(Spell spell)
 {
     // TODO: implement checks for mana cost reducers
     return spell.Mana;
 }
 internal virtual uint GetActualSpellCastTime(Spell spell)
 {
     // TODO: implement checks for casting time reducers
     return spell.CastTime;
 }
        /// <summary>Called when a spell finishes its timer - from both normal spells as well as items.</summary>
        /// <param name="invSlotId">Null when not a spell cast from an item.</param>
        /// <remarks>Does not check if it is valid to cast the spell, this should already be done by this point.  Also any specialized
        /// checks should be done before calling this base implementation as it tries to apply the spell.</remarks>
        internal virtual bool FinishSpell(Spell spell, uint slotId, uint? invSlotId)
        {
            // Make sure we're only casting one timed spell at a time
            if (_castingSpell.SpellID != spell.SpellID) {
                _log.InfoFormat("{0}'s casting of {1} canceled: already casting {2}.", this.Name, spell.SpellName, _castingSpell.SpellName);
                if (this is ZonePlayer)
                    ((ZonePlayer)this).MsgMgr.SendMessageID((uint)MessageType.Common13, MessageStrings.ALREADY_CASTING);
                InterruptSpell(null, null); // TODO: does it matter which spell is being interrupted here?
                return false;
            }

            // Check for anything that might have interrupted the spell (moving, etc.)
            bool bardSongMode = false, regainConcen = false;
            if (this.Class == (byte)CharClasses.Bard) {     // Bards can move when casting any spell
                if (spell.IsBardSong) {
                    // TODO: apply some logic to bard songs
                }
            }
            else {  // Not bard, check movement
                if (_hitWhileCastCount > 0 || this.X != _spellX || this.Y != _spellY || this.Z != _spellZ) {
                    // We moved or were hit, check for regain concentration
                    _hitWhileCastCount = Math.Min(_hitWhileCastCount, 15);
                    float channelChance, distMoved, dX, dY, distanceMod;

                    // TODO: check for interrupts
                }
            }

            // TODO: try twin cast

            return ExecuteSpell(spell, this.SpellTargetMob, slotId, invSlotId);
        }
        /// <summary>Actually executes a spell and its effects on a specified target.</summary>
        /// <param name="spell"></param>
        /// <param name="target"></param>
        /// <param name="slotId"></param>
        /// <param name="invSlotId">Optional slot a used item is in that caused the spell.</param>
        /// <returns></returns>
        internal virtual bool ExecuteSpell(Spell spell, Mob target, uint slotId, uint? invSlotId)
        {
            CastActionType cat;
            Mob aeCenter = null;
            if (!DetermineSpellTargets(spell, target, ref aeCenter, out cat))
                return false;

            return true;    // TODO: unfinished
        }
 partial void DeleteSpell(Spell instance);
 partial void UpdateSpell(Spell instance);
 partial void InsertSpell(Spell instance);