/// <summary>Processes the (re)spawning of entities in the zone.  Iterates the spawns for this zone and then adds a corresponding
        /// NPC to the mob list if various spawn conditions are met.</summary>
        private void SpawnTimerCallback(object state)
        {
            NpcMob npcMob = null;
            using (EmuDataContext dbCtx = new EmuDataContext())
            {
                dbCtx.ObjectTrackingEnabled = false;

                List<SkillCap> skillCaps = dbCtx.SkillCaps.OrderBy(sc => sc.SkillID).ThenBy(sc => sc.Class).ThenBy(sc => sc.Level).ToList();

                foreach (Internals.Data.Spawn s in this.Zone.Spawns)
                {
                    // TODO: Handle timeleft on spawn entity (may be able to wait until persistant zone state is in)

                    // TODO: Process spawn conditions

                    if (!s.ReadyForRespawn())
                        continue;

                    try
                    {
                        Npc npc = s.SpawnGroup.PickNPC();   // Get an Npc
                        if (npc != null)
                        {
                            npcMob = new NpcMob(GetNewEntityId(), npc, s.PathGrid, s.X, s.Y, s.Z, s.Heading);

                            // Clean up the npc's name and make it unique (def. before we add to list)
                            npcMob.Name = npcMob.Name.RemoveDigits();
                            npcMob.Name = GetUniqueMobName(npcMob.Name);

                            var npcSkillCaps = skillCaps.Where(sc => sc.Class == npcMob.Class && sc.Level == npcMob.Level);

                            foreach (SkillCap cap in npcSkillCaps)
                                npcMob.SetSkillLevel((Skill)cap.SkillID, cap.Level);

                            //_log.DebugFormat("Trying to add npc with entity id of {0}", npcMob.ID);
                            AddNpc(npcMob, true, false);
                            // TODO: add to npc limit list

                            s.LastSpawned = DateTime.Now;
                            // TODO: set roambox?
                            npcMob.LoadGrid(this.Zone.ZoneID, dbCtx);
                        }
                        else
                        {
                            s.LastSpawned = DateTime.MaxValue;  // better than tracking a separate list of deletes and delete routine?
                            _log.DebugFormat("Spawn {0} removed due to lack of NPCs, spawn groups or cumulative spawn chance of zero.", s.SpawnID);
                        }
                    }
                    catch (Exception ex)
                    {
                        _log.Error("Error in SpawnTimerCallback", ex);
                    }
                }
            }
        }
        private void NpcDeath(NpcMob npc, Mob lastAttacker, uint spellId, byte damageType, uint damage)
        {
            RemoveFromAllTargets(npc);  // Remove NPC from any entity's target & hate lists

            // Send Death Packet
            Death d = new Death()
            {
                SpawnId = (uint)npc.ID,
                KillerId = lastAttacker == null ? 0u : (uint)lastAttacker.ID,
                BindToZoneId = 0u,
                SpellId = spellId == 0u ? 0xFFFFFFFF : spellId,
                AttackSkillId = damageType,
                Damage = damage
            };
            EQApplicationPacket<Death> dPack = new EQApplicationPacket<Death>(AppOpCode.Death, d);
            _zoneSvr.QueuePacketToClients(lastAttacker, dPack, false);  // TODO: this should be cool with a null lastAttacker, right?

            // TODO: something about respawn?

            Mob killer = npc.HateMgr.GetTopHated();
            Mob xpMob = npc.HateMgr.GetTopDamager();

            // Give xp out
            if (xpMob == null)
                xpMob = killer;
            if (xpMob == null)
                xpMob = lastAttacker;

            // TODO: if xpMob is pet, get the owner

            ZonePlayer xpClient = xpMob as ZonePlayer;
            if (xpClient != null) {
                // TODO: handle raid split, LDON adventure crap, etc.

                float groupBonus = 1.0f;
                if (xpClient.IsGrouped) {
                    // TODO: handle group split

                    // TODO: add group xp bonus
                }
                else {
                    ConLevel conLvl = Mob.GetConsiderDificulty(xpClient.Level, npc.Level);
                    if (conLvl != ConLevel.Green) {
                        // TODO: figure high con bonus, if any
                        int baseXp = (int)(npc.Level * npc.Level * groupBonus * _zoneSvr.Zone.XPMultiplier);
                        xpClient.GiveXP((int)(npc.Level * npc.Level * 75 * _zoneSvr.Zone.XPMultiplier), conLvl);
                    }
                }

                // TODO: raise death merit event?
            }

            // TODO: faction hits

            // Make a corpse and add to the manager
            if (npc.IsLootable) {   // TODO: add additional checks for stuff like a guard killing the mob, etc.
                Corpse c = new Corpse(npc, npc.LootItems);
                AddCorpse(c);

                // TODO: if killer is a pet, get the owner
                if (xpClient != null)
                    c.AllowLooter(killer as ZonePlayer);
            }

            // TODO: raise script event
            _log.DebugFormat("NPC {0} has died", npc.ID);
        }
        private void AddNpc(NpcMob npcMob, bool sendSpawnPacket, bool dontQueue)
        {
            // TODO: try to see if we can get away with only one list for mobs & npcs... npcs are in the mob list, so hey, it might work
            //_npcMobs.Add(npcMob.NpcID, npcMob); // Added by id of the npc db entity
            AddMob(npcMob);

            if (sendSpawnPacket)
            {
                if (dontQueue)
                {
                    EQApplicationPacket<Internals.Packets.Spawn> spawnPack = new EQApplicationPacket<Internals.Packets.Spawn>(AppOpCode.NewSpawn, npcMob.GetSpawn());
                    QueuePacketToClients(null, spawnPack, true);
                    // TODO: raise spawn event
                }
                else
                    if (_clients.Count > 0) // no clients, no send anything
                    {
                        lock (((ICollection)_spawnQueue).SyncRoot)
                        {
                            _spawnQueue.Enqueue(npcMob.GetSpawn());
                        }
                    }

                // TODO: raise spawn event - nah, better place is where it actually spawns, yea?
            }
        }