/*
 * Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using Framework.Constants;
using Framework.Database;
using Game.DataStorage;
using Game.Entities;
using Game.Spells;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Game.AI
{
    public class SmartAIManager : Singleton<SmartAIManager>
    {
        SmartAIManager() { }

        public void LoadFromDB()
        {
            uint oldMSTime = Time.GetMSTime();

            PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.SEL_SMART_SCRIPTS);
            SQLResult result = DB.World.Query(stmt);
            if (result.IsEmpty())
            {
                Log.outInfo(LogFilter.ServerLoading, "Loaded 0 SmartAI scripts. DB table `smartai_scripts` is empty.");
                return;
            }

            int count = 0;
            do
            {
                SmartScriptHolder temp = new SmartScriptHolder();

                temp.entryOrGuid = result.Read<int>(0);
                SmartScriptType source_type = (SmartScriptType)result.Read<byte>(1);
                if (source_type >= SmartScriptType.Max)
                {
                    Log.outError(LogFilter.Sql, "SmartAIMgr.LoadSmartAI: invalid source_type ({0}), skipped loading.", source_type);
                    continue;
                }
                if (temp.entryOrGuid >= 0)
                {
                    switch (source_type)
                    {
                        case SmartScriptType.Creature:
                            if (Global.ObjectMgr.GetCreatureTemplate((uint)temp.entryOrGuid) == null)
                            {
                                Log.outError(LogFilter.Sql, "SmartAIMgr.LoadSmartAI: Creature entry ({0}) does not exist, skipped loading.", temp.entryOrGuid);
                                continue;
                            }
                            break;

                        case SmartScriptType.GameObject:
                            {
                                if (Global.ObjectMgr.GetGameObjectTemplate((uint)temp.entryOrGuid) == null)
                                {
                                    Log.outError(LogFilter.Sql, "SmartAIMgr.LoadSmartAI: GameObject entry ({0}) does not exist, skipped loading.", temp.entryOrGuid);
                                    continue;
                                }
                                break;
                            }
                        case SmartScriptType.AreaTrigger:
                            {
                                if (CliDB.AreaTableStorage.LookupByKey((uint)temp.entryOrGuid) == null)
                                {
                                    Log.outError(LogFilter.Sql, "SmartAIMgr.LoadSmartAI: AreaTrigger entry ({0}) does not exist, skipped loading.", temp.entryOrGuid);
                                    continue;
                                }
                                break;
                            }
                        case SmartScriptType.Scene:
                            {
                                if (Global.ObjectMgr.GetSceneTemplate((uint)temp.entryOrGuid) == null)
                                {
                                    Log.outError(LogFilter.Sql, "SmartAIMgr.LoadSmartAIFromDB: Scene id ({0}) does not exist, skipped loading.", temp.entryOrGuid);
                                    continue;
                                }
                                break;
                            }
                        case SmartScriptType.TimedActionlist:
                            break;//nothing to check, really
                        default:
                            Log.outError(LogFilter.Sql, "SmartAIMgr.LoadSmartAIFromDB: not yet implemented source_type {0}", source_type);
                            continue;
                    }
                }
                else
                {
                    if (Global.ObjectMgr.GetCreatureData((uint)Math.Abs(temp.entryOrGuid)) == null)
                    {
                        Log.outError(LogFilter.Sql, "SmartAIMgr.LoadSmartAI: Creature guid ({0}) does not exist, skipped loading.", Math.Abs(temp.entryOrGuid));
                        continue;
                    }
                }

                temp.source_type = source_type;
                temp.event_id = result.Read<ushort>(2);
                temp.link = result.Read<ushort>(3);
                temp.Event.type = (SmartEvents)result.Read<byte>(4);
                temp.Event.event_phase_mask = result.Read<byte>(5);
                temp.Event.event_chance = result.Read<byte>(6);
                temp.Event.event_flags = (SmartEventFlags)result.Read<ushort>(7);

                temp.Event.raw.param1 = result.Read<uint>(8);
                temp.Event.raw.param2 = result.Read<uint>(9);
                temp.Event.raw.param3 = result.Read<uint>(10);
                temp.Event.raw.param4 = result.Read<uint>(11);
                temp.Event.param_string = result.Read<string>(12);

                temp.Action.type = (SmartActions)result.Read<byte>(13);
                temp.Action.raw.param1 = result.Read<uint>(14);
                temp.Action.raw.param2 = result.Read<uint>(15);
                temp.Action.raw.param3 = result.Read<uint>(16);
                temp.Action.raw.param4 = result.Read<uint>(17);
                temp.Action.raw.param5 = result.Read<uint>(18);
                temp.Action.raw.param6 = result.Read<uint>(19);

                temp.Target.type = (SmartTargets)result.Read<byte>(20);
                temp.Target.raw.param1 = result.Read<uint>(21);
                temp.Target.raw.param2 = result.Read<uint>(22);
                temp.Target.raw.param3 = result.Read<uint>(23);
                temp.Target.x = result.Read<float>(24);
                temp.Target.y = result.Read<float>(25);
                temp.Target.z = result.Read<float>(26);
                temp.Target.o = result.Read<float>(27);

                //check target
                if (!IsTargetValid(temp))
                    continue;

                // check all event and action params
                if (!IsEventValid(temp))
                    continue;

                // creature entry / guid not found in storage, create empty event list for it and increase counters
                if (mEventMap[(int)source_type] == null)
                    mEventMap[(int)source_type] = new MultiMap<int, SmartScriptHolder>();

                if (!mEventMap[(int)source_type].ContainsKey(temp.entryOrGuid))
                    ++count;

                // store the new event
                mEventMap[(uint)source_type].Add(temp.entryOrGuid, temp);
            } while (result.NextRow());

            // Post Loading Validation
            for (byte i = 0; i < (int)SmartScriptType.Max; ++i)
            {
                if (mEventMap[i] == null)
                    continue;

                foreach (var key in mEventMap[i].Keys)
                {
                    var list = mEventMap[i].LookupByKey(key);
                    foreach (var e in list)
                    {
                        if (e.link != 0)
                        {
                            if (FindLinkedEvent(list, e.link) == null)
                            {
                                Log.outError(LogFilter.Sql, "SmartAIMgr.LoadFromDB: Entry {0} SourceType {1}, Event {2}, Link Event {3} not found or invalid.",
                                        e.entryOrGuid, e.GetScriptType(), e.event_id, e.link);
                            }
                        }

                        if (e.GetEventType() == SmartEvents.Link)
                        {
                            if (FindLinkedSourceEvent(list, e.event_id) == null)
                            {
                                Log.outError(LogFilter.Sql, "SmartAIMgr.LoadFromDB: Entry {0} SourceType {1}, Event {2}, Link Source Event not found or invalid. Event will never trigger.",
                                        e.entryOrGuid, e.GetScriptType(), e.event_id);
                            }
                        }
                    }
                }
            }

            Log.outInfo(LogFilter.ServerLoading, "Loaded {0} SmartAI scripts in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
        }
        public void LoadWaypointFromDB()
        {
            uint oldMSTime = Time.GetMSTime();

            waypoint_map.Clear();

            PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.SEL_SMARTAI_WP);
            SQLResult result = DB.World.Query(stmt);

            if (result.IsEmpty())
            {
                Log.outInfo(LogFilter.ServerLoading, "Loaded 0 SmartAI Waypoint Paths. DB table `waypoints` is empty.");

                return;
            }

            uint count = 0;
            uint total = 0;
            uint last_entry = 0;
            uint last_id = 1;

            do
            {
                uint entry = result.Read<uint>(0);
                uint id = result.Read<uint>(1);
                float x, y, z;
                x = result.Read<float>(2);
                y = result.Read<float>(3);
                z = result.Read<float>(4);

                if (last_entry != entry)
                {
                    waypoint_map[entry] = new Dictionary<uint, SmartPath>();
                    last_id = 1;
                    count++;
                }

                if (last_id != id)
                    Log.outError(LogFilter.Sql, "SmartWaypointMgr.LoadFromDB: Path entry {0}, unexpected point id {1}, expected {2}.", entry, id, last_id);

                last_id++;
                waypoint_map[entry][id] = new SmartPath(id, x, y, z);

                last_entry = entry;
                total++;
            }
            while (result.NextRow());

            Log.outInfo(LogFilter.ServerLoading, "Loaded {0} SmartAI waypoint paths (total {1} waypoints) in {2} ms", count, total, Time.GetMSTimeDiffToNow(oldMSTime));
        }

        bool IsTargetValid(SmartScriptHolder e)
        {
            if (Math.Abs(e.Target.o) > 2 * MathFunctions.PI)
                Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} has abs(`target.o` = {4}) > 2*PI (orientation is expressed in radians)",
                    e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Target.o);

            if (e.GetActionType() == SmartActions.InstallAiTemplate)
                return true; // AI template has special handling

            switch (e.GetTargetType())
            {
                case SmartTargets.CreatureDistance:
                case SmartTargets.CreatureRange:
                    {
                        if (e.Target.unitDistance.creature != 0 && Global.ObjectMgr.GetCreatureTemplate(e.Target.unitDistance.creature) == null)
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Creature entry {4} as target_param1, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Target.unitDistance.creature);
                            return false;
                        }
                        break;
                    }
                case SmartTargets.GameobjectDistance:
                case SmartTargets.GameobjectRange:
                    {
                        if (e.Target.goDistance.entry != 0 && Global.ObjectMgr.GetGameObjectTemplate(e.Target.goDistance.entry) == null)
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent GameObject entry {4} as target_param1, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Target.goDistance.entry);
                            return false;
                        }
                        break;
                    }
                case SmartTargets.CreatureGuid:
                    {
                        if (e.Target.unitGUID.entry != 0 && !IsCreatureValid(e, e.Target.unitGUID.entry))
                            return false;
                        break;
                    }
                case SmartTargets.GameobjectGuid:
                    {
                        if (e.Target.goGUID.entry != 0 && !IsGameObjectValid(e, e.Target.goGUID.entry))
                            return false;
                        break;
                    }
                case SmartTargets.PlayerDistance:
                case SmartTargets.ClosestPlayer:
                    {
                        if (e.Target.playerDistance.dist == 0)
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} has maxDist 0 as target_param1, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
                            return false;
                        }
                        break;
                    }
                case SmartTargets.PlayerRange:
                case SmartTargets.Self:
                case SmartTargets.Victim:
                case SmartTargets.HostileSecondAggro:
                case SmartTargets.HostileLastAggro:
                case SmartTargets.HostileRandom:
                case SmartTargets.HostileRandomNotTop:
                case SmartTargets.ActionInvoker:
                case SmartTargets.InvokerParty:
                case SmartTargets.Position:
                case SmartTargets.None:
                case SmartTargets.ActionInvokerVehicle:
                case SmartTargets.OwnerOrSummoner:
                case SmartTargets.ThreatList:
                case SmartTargets.ClosestGameobject:
                case SmartTargets.ClosestCreature:
                case SmartTargets.ClosestEnemy:
                case SmartTargets.ClosestFriendly:
                case SmartTargets.Stored:
                case SmartTargets.LootRecipients:
                case SmartTargets.VehicleAccessory:
                    break;
                default:
                    Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Not handled target_type({0}), Entry {1} SourceType {2} Event {3} Action {4}, skipped.", e.GetTargetType(), e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
                    return false;
            }
            return true;
        }

        bool IsEventValid(SmartScriptHolder e)
        {
            if (e.Event.type >= SmartEvents.End)
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: EntryOrGuid {0} using event({1}) has invalid event type ({2}), skipped.", e.entryOrGuid, e.event_id, e.GetEventType());
                return false;
            }

            // in SMART_SCRIPT_TYPE_TIMED_ACTIONLIST all event types are overriden by core
            if (e.GetScriptType() != SmartScriptType.TimedActionlist && !Convert.ToBoolean(SmartAIEventMask[e.Event.type] & SmartAITypeMask[e.GetScriptType()]))
            {
                Log.outError(LogFilter.Scripts, "SmartAIMgr: EntryOrGuid {0}, event type {1} can not be used for Script type {2}", e.entryOrGuid, e.GetEventType(), e.GetScriptType());
                return false;
            }
            if (e.Action.type <= 0 || e.Action.type >= SmartActions.End)
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: EntryOrGuid {0} using event({1}) has invalid action type ({2}), skipped.", e.entryOrGuid, e.event_id, e.GetActionType());
                return false;
            }
            if (e.Event.event_phase_mask > (uint)SmartPhase.All)
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: EntryOrGuid {0} using event({1}) has invalid phase mask ({2}), skipped.", e.entryOrGuid, e.event_id, e.Event.event_phase_mask);
                return false;
            }
            if (e.Event.event_flags > SmartEventFlags.All)
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: EntryOrGuid {0} using event({1}) has invalid event flags ({2}), skipped.", e.entryOrGuid, e.event_id, e.Event.event_flags);
                return false;
            }
            if (e.link != 0 && e.link == e.event_id)
            {
                Log.outError(LogFilter.Sql, "SmartAIMgr: EntryOrGuid {0} SourceType {1}, Event {2}, Event is linking self (infinite loop), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id);
                return false;
            }
            if (e.GetScriptType() == SmartScriptType.TimedActionlist)
            {
                e.Event.type = SmartEvents.UpdateOoc;//force default OOC, can change when calling the script!
                if (!IsMinMaxValid(e, e.Event.minMaxRepeat.min, e.Event.minMaxRepeat.max))
                    return false;

                if (!IsMinMaxValid(e, e.Event.minMaxRepeat.repeatMin, e.Event.minMaxRepeat.repeatMax))
                    return false;
            }
            else
            {
                switch (e.Event.type)
                {
                    case SmartEvents.Update:
                    case SmartEvents.UpdateIc:
                    case SmartEvents.UpdateOoc:
                    case SmartEvents.HealtPct:
                    case SmartEvents.ManaPct:
                    case SmartEvents.TargetHealthPct:
                    case SmartEvents.TargetManaPct:
                    case SmartEvents.Range:
                    case SmartEvents.Damaged:
                    case SmartEvents.DamagedTarget:
                    case SmartEvents.ReceiveHeal:
                        if (!IsMinMaxValid(e, e.Event.minMaxRepeat.min, e.Event.minMaxRepeat.max))
                            return false;

                        if (!IsMinMaxValid(e, e.Event.minMaxRepeat.repeatMin, e.Event.minMaxRepeat.repeatMax))
                            return false;
                        break;
                    case SmartEvents.SpellHit:
                    case SmartEvents.SpellhitTarget:
                        if (e.Event.spellHit.spell != 0)
                        {
                            SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(e.Event.spellHit.spell);
                            if (spellInfo == null)
                            {
                                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Spell entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Event.spellHit.spell);
                                return false;
                            }
                            if (e.Event.spellHit.school != 0 && ((SpellSchoolMask)e.Event.spellHit.school & spellInfo.SchoolMask) != spellInfo.SchoolMask)
                            {
                                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses Spell entry {4} with invalid school mask, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Event.spellHit.spell);
                                return false;
                            }
                        }
                        if (!IsMinMaxValid(e, e.Event.spellHit.cooldownMin, e.Event.spellHit.cooldownMax))
                            return false;
                        break;
                    case SmartEvents.OocLos:
                    case SmartEvents.IcLos:
                        if (!IsMinMaxValid(e, e.Event.los.cooldownMin, e.Event.los.cooldownMax))
                            return false;
                        break;
                    case SmartEvents.Respawn:
                        if (e.Event.respawn.type == (uint)SmartRespawnCondition.Map && CliDB.MapStorage.LookupByKey(e.Event.respawn.map) == null)
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Map entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Event.respawn.map);
                            return false;
                        }
                        if (e.Event.respawn.type == (uint)SmartRespawnCondition.Area && !CliDB.AreaTableStorage.ContainsKey(e.Event.respawn.area))
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Area entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Event.respawn.area);
                            return false;
                        }
                        break;
                    case SmartEvents.FriendlyHealth:
                        if (!NotNULL(e, e.Event.friendlyHealth.radius))
                            return false;

                        if (!IsMinMaxValid(e, e.Event.friendlyHealth.repeatMin, e.Event.friendlyHealth.repeatMax))
                            return false;
                        break;
                    case SmartEvents.FriendlyIsCc:
                        if (!IsMinMaxValid(e, e.Event.friendlyCC.repeatMin, e.Event.friendlyCC.repeatMax))
                            return false;
                        break;
                    case SmartEvents.FriendlyMissingBuff:
                        {
                            if (!IsSpellValid(e, e.Event.missingBuff.spell))
                                return false;

                            if (!NotNULL(e, e.Event.missingBuff.radius))
                                return false;

                            if (!IsMinMaxValid(e, e.Event.missingBuff.repeatMin, e.Event.missingBuff.repeatMax))
                                return false;
                            break;
                        }
                    case SmartEvents.Kill:
                        if (!IsMinMaxValid(e, e.Event.kill.cooldownMin, e.Event.kill.cooldownMax))
                            return false;

                        if (e.Event.kill.creature != 0 && !IsCreatureValid(e, e.Event.kill.creature))
                            return false;
                        break;
                    case SmartEvents.VictimCasting:
                    case SmartEvents.PassengerBoarded:
                    case SmartEvents.PassengerRemoved:
                        if (!IsMinMaxValid(e, e.Event.minMax.repeatMin, e.Event.minMax.repeatMax))
                            return false;
                        break;
                    case SmartEvents.SummonDespawned:
                    case SmartEvents.SummonedUnit:
                        if (e.Event.summoned.creature != 0 && !IsCreatureValid(e, e.Event.summoned.creature))
                            return false;

                        if (!IsMinMaxValid(e, e.Event.summoned.cooldownMin, e.Event.summoned.cooldownMax))
                            return false;
                        break;
                    case SmartEvents.AcceptedQuest:
                    case SmartEvents.RewardQuest:
                        if (e.Event.quest.questId != 0 && !IsQuestValid(e, e.Event.quest.questId))
                            return false;
                        break;
                    case SmartEvents.ReceiveEmote:
                        {
                            if (e.Event.emote.emoteId != 0 && !IsTextEmoteValid(e, e.Event.emote.emoteId))
                                return false;

                            if (!IsMinMaxValid(e, e.Event.emote.cooldownMin, e.Event.emote.cooldownMax))
                                return false;
                            break;
                        }
                    case SmartEvents.HasAura:
                    case SmartEvents.TargetBuffed:
                        {
                            if (!IsSpellValid(e, e.Event.aura.spell))
                                return false;

                            if (!IsMinMaxValid(e, e.Event.aura.repeatMin, e.Event.aura.repeatMax))
                                return false;
                            break;
                        }
                    case SmartEvents.TransportAddcreature:
                        {
                            if (e.Event.transportAddCreature.creature != 0 && !IsCreatureValid(e, e.Event.transportAddCreature.creature))
                                return false;
                            break;
                        }
                    case SmartEvents.Movementinform:
                        {
                            if (e.Event.movementInform.type >= (uint)MovementGeneratorType.Max)
                            {
                                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses invalid Motion type {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Event.movementInform.type);
                                return false;
                            }
                            break;
                        }
                    case SmartEvents.DataSet:
                        {
                            if (!IsMinMaxValid(e, e.Event.dataSet.cooldownMin, e.Event.dataSet.cooldownMax))
                                return false;
                            break;
                        }
                    case SmartEvents.AreatriggerOntrigger:
                        {
                            if (e.Event.areatrigger.id != 0 && !IsAreaTriggerValid(e, e.Event.areatrigger.id))
                                return false;
                            break;
                        }
                    case SmartEvents.TextOver:
                        {
                            if (!IsTextValid(e, e.Event.textOver.textGroupID))
                                return false;
                            break;
                        }
                    case SmartEvents.DummyEffect:
                        {
                            if (!IsSpellValid(e, e.Event.dummy.spell))
                                return false;

                            if (e.Event.dummy.effIndex > 2)
                                return false;
                            break;
                        }
                    case SmartEvents.IsBehindTarget:
                        {
                            if (!IsMinMaxValid(e, e.Event.behindTarget.cooldownMin, e.Event.behindTarget.cooldownMax))
                                return false;
                            break;
                        }
                    case SmartEvents.GameEventStart:
                    case SmartEvents.GameEventEnd:
                        {
                            var events = Global.GameEventMgr.GetEventMap();
                            if (e.Event.gameEvent.gameEventId >= events.Length || !events[e.Event.gameEvent.gameEventId].isValid())
                                return false;

                            break;
                        }
                    case SmartEvents.ActionDone:
                        {
                            if (e.Event.doAction.eventId > EventId.Charge)
                            {
                                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses invalid event id {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Event.doAction.eventId);
                                return false;
                            }
                            break;
                        }
                    case SmartEvents.FriendlyHealthPCT:
                        if (!IsMinMaxValid(e, e.Event.friendlyHealthPct.repeatMin, e.Event.friendlyHealthPct.repeatMax))
                            return false;

                        if (e.Event.friendlyHealthPct.maxHpPct > 100 || e.Event.friendlyHealthPct.minHpPct > 100)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} has pct value above 100, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
                            return false;
                        }

                        switch (e.GetTargetType())
                        {
                            case SmartTargets.CreatureRange:
                            case SmartTargets.CreatureGuid:
                            case SmartTargets.CreatureDistance:
                            case SmartTargets.ClosestCreature:
                            case SmartTargets.ClosestPlayer:
                            case SmartTargets.PlayerRange:
                            case SmartTargets.PlayerDistance:
                                break;
                            default:
                                Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses invalid target_type {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.GetTargetType());
                                return false;
                        }
                        break;
                    case SmartEvents.DistanceCreature:
                        if (e.Event.distance.guid == 0 && e.Event.distance.entry == 0)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_DISTANCE_CREATURE did not provide creature guid or entry, skipped.");
                            return false;
                        }

                        if (e.Event.distance.guid != 0 && e.Event.distance.entry != 0)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_DISTANCE_CREATURE provided both an entry and guid, skipped.");
                            return false;
                        }

                        if (e.Event.distance.guid != 0 && Global.ObjectMgr.GetCreatureData(e.Event.distance.guid) == null)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_DISTANCE_CREATURE using invalid creature guid {0}, skipped.", e.Event.distance.guid);
                            return false;
                        }

                        if (e.Event.distance.entry != 0 && Global.ObjectMgr.GetCreatureTemplate(e.Event.distance.entry) == null)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_DISTANCE_CREATURE using invalid creature entry {0}, skipped.", e.Event.distance.entry);
                            return false;
                        }
                        break;
                    case SmartEvents.DistanceGameobject:
                        if (e.Event.distance.guid == 0 && e.Event.distance.entry == 0)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT did not provide gameobject guid or entry, skipped.");
                            return false;
                        }

                        if (e.Event.distance.guid != 0 && e.Event.distance.entry != 0)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT provided both an entry and guid, skipped.");
                            return false;
                        }

                        if (e.Event.distance.guid != 0 && Global.ObjectMgr.GetGOData(e.Event.distance.guid) == null)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject guid {0}, skipped.", e.Event.distance.guid);
                            return false;
                        }

                        if (e.Event.distance.entry != 0 && Global.ObjectMgr.GetGameObjectTemplate(e.Event.distance.entry) == null)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject entry {0}, skipped.", e.Event.distance.entry);
                            return false;
                        }
                        break;
                    case SmartEvents.CounterSet:
                        if (!IsMinMaxValid(e, e.Event.counter.cooldownMin, e.Event.counter.cooldownMax))
                            return false;

                        if (e.Event.counter.id == 0)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_COUNTER_SET using invalid counter id {0}, skipped.", e.Event.counter.id);
                            return false;
                        }

                        if (e.Event.counter.value == 0)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_COUNTER_SET using invalid value {0}, skipped.", e.Event.counter.value);
                            return false;
                        }
                        break;
                    case SmartEvents.Link:
                    case SmartEvents.GoStateChanged:
                    case SmartEvents.GoEventInform:
                    case SmartEvents.TimedEventTriggered:
                    case SmartEvents.InstancePlayerEnter:
                    case SmartEvents.TransportRelocate:
                    case SmartEvents.Charmed:
                    case SmartEvents.CharmedTarget:
                    case SmartEvents.CorpseRemoved:
                    case SmartEvents.AiInit:
                    case SmartEvents.TransportAddplayer:
                    case SmartEvents.TransportRemovePlayer:
                    case SmartEvents.Aggro:
                    case SmartEvents.Death:
                    case SmartEvents.Evade:
                    case SmartEvents.ReachedHome:
                    case SmartEvents.Reset:
                    case SmartEvents.QuestAccepted:
                    case SmartEvents.QuestObjCompletion:
                    case SmartEvents.QuestCompletion:
                    case SmartEvents.QuestRewarded:
                    case SmartEvents.QuestFail:
                    case SmartEvents.JustSummoned:
                    case SmartEvents.WaypointStart:
                    case SmartEvents.WaypointReached:
                    case SmartEvents.WaypointPaused:
                    case SmartEvents.WaypointResumed:
                    case SmartEvents.WaypointStopped:
                    case SmartEvents.WaypointEnded:
                    case SmartEvents.GossipSelect:
                    case SmartEvents.GossipHello:
                    case SmartEvents.JustCreated:
                    case SmartEvents.FollowCompleted:
                    case SmartEvents.OnSpellclick:
                    case SmartEvents.SceneStart:
                    case SmartEvents.SceneCancel:
                    case SmartEvents.SceneComplete:
                    case SmartEvents.SceneTrigger:
                        break;
                    default:
                        Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Not handled event_type({0}), Entry {1} SourceType {2} Event {3} Action {4}, skipped.", e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
                        return false;
                }
            }

            switch (e.GetActionType())
            {
                case SmartActions.Talk:
                case SmartActions.SimpleTalk:
                    {
                        if (!IsTextValid(e, e.Action.talk.textGroupId))
                            return false;
                        break;
                    }
                case SmartActions.SetFaction:
                    if (e.Action.faction.factionID != 0 && CliDB.FactionTemplateStorage.LookupByKey(e.Action.faction.factionID) == null)
                    {
                        Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Faction {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.faction.factionID);
                        return false;
                    }
                    break;
                case SmartActions.MorphToEntryOrModel:
                case SmartActions.MountToEntryOrModel:
                    if (e.Action.morphOrMount.creature != 0 || e.Action.morphOrMount.model != 0)
                    {
                        if (e.Action.morphOrMount.creature > 0 && Global.ObjectMgr.GetCreatureTemplate(e.Action.morphOrMount.creature) == null)
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Creature entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.morphOrMount.creature);
                            return false;
                        }

                        if (e.Action.morphOrMount.model != 0)
                        {
                            if (e.Action.morphOrMount.creature != 0)
                            {
                                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} has ModelID set with also set CreatureId, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
                                return false;
                            }
                            else if (!CliDB.CreatureDisplayInfoStorage.ContainsKey(e.Action.morphOrMount.model))
                            {
                                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Model id {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.morphOrMount.model);
                                return false;
                            }
                        }
                    }
                    break;
                case SmartActions.Sound:
                    if (!IsSoundValid(e, e.Action.sound.soundId))
                        return false;
                    break;
                case SmartActions.SetEmoteState:
                case SmartActions.PlayEmote:
                    if (!IsEmoteValid(e, e.Action.emote.emoteId))
                        return false;
                    break;
                case SmartActions.PlayAnimkit:
                    if (e.Action.animKit.animKit != 0 && !IsAnimKitValid(e, e.Action.animKit.animKit))
                        return false;

                    if (e.Action.animKit.type > 3)
                    {
                        Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses invalid AnimKit type {4}, skipped.", 
                            e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.animKit.type);
                        return false;
                    }
                    break;
                case SmartActions.FailQuest:
                case SmartActions.OfferQuest:
                    if (e.Action.quest.questId == 0 || !IsQuestValid(e, e.Action.quest.questId))
                        return false;
                    break;
                case SmartActions.ActivateTaxi:
                    {
                        if (!CliDB.TaxiPathStorage.ContainsKey(e.Action.taxi.id))
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses invalid Taxi path ID {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.taxi.id);
                            return false;
                        }
                        break;
                    }
                case SmartActions.RandomEmote:
                    if (e.Action.randomEmote.emote1 != 0 && !IsEmoteValid(e, e.Action.randomEmote.emote1))
                        return false;
                    if (e.Action.randomEmote.emote2 != 0 && !IsEmoteValid(e, e.Action.randomEmote.emote2))
                        return false;
                    if (e.Action.randomEmote.emote3 != 0 && !IsEmoteValid(e, e.Action.randomEmote.emote3))
                        return false;
                    if (e.Action.randomEmote.emote4 != 0 && !IsEmoteValid(e, e.Action.randomEmote.emote4))
                        return false;
                    if (e.Action.randomEmote.emote5 != 0 && !IsEmoteValid(e, e.Action.randomEmote.emote5))
                        return false;
                    if (e.Action.randomEmote.emote6 != 0 && !IsEmoteValid(e, e.Action.randomEmote.emote6))
                        return false;
                    break;
                case SmartActions.RandomSound:
                    if (e.Action.randomSound.sound1 != 0 && !IsSoundValid(e, e.Action.randomSound.sound1))
                        return false;
                    if (e.Action.randomSound.sound2 != 0 && !IsSoundValid(e, e.Action.randomSound.sound2))
                        return false;
                    if (e.Action.randomSound.sound3 != 0 && !IsSoundValid(e, e.Action.randomSound.sound3))
                        return false;
                    if (e.Action.randomSound.sound4 != 0 && !IsSoundValid(e, e.Action.randomSound.sound4))
                        return false;
                    if (e.Action.randomSound.sound5 != 0 && !IsSoundValid(e, e.Action.randomSound.sound5))
                        return false;
                    break;
                case SmartActions.Cast:
                    {
                        if (!IsSpellValid(e, e.Action.cast.spell))
                            return false;

                        SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(e.Action.cast.spell);
                        foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
                        {
                            if (effect != null && (effect.IsEffect(SpellEffectName.KillCredit) || effect.IsEffect(SpellEffectName.KillCredit2)))
                            {
                                if (effect.TargetA.GetTarget() == Targets.UnitCaster)
                                    Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} Effect: SPELL_EFFECT_KILL_CREDIT: (SpellId: {4} targetA: {5} - targetB: {6}) has invalid target for this Action",
                                    e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.cast.spell, effect.TargetA.GetTarget(), effect.TargetB.GetTarget());
                            }
                        }
                        break;
                    }
                case SmartActions.AddAura:
                case SmartActions.InvokerCast:
                    if (!IsSpellValid(e, e.Action.cast.spell))
                        return false;
                    break;
                case SmartActions.CallAreaexploredoreventhappens:
                case SmartActions.CallGroupeventhappens:
                    Quest qid = Global.ObjectMgr.GetQuestTemplate(e.Action.quest.questId);
                    if (qid != null)
                    {
                        if (!qid.HasSpecialFlag(QuestSpecialFlags.ExplorationOrEvent))
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} SpecialFlags for Quest entry {4} does not include FLAGS_EXPLORATION_OR_EVENT(2), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.quest.questId);
                            return false;
                        }
                    }
                    else
                    {
                        Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Quest entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.quest.questId);
                        return false;
                    }
                    break;
                case SmartActions.SetEventPhase:
                    if (e.Action.setEventPhase.phase >= (uint)SmartPhase.Max)
                    {
                        Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} attempts to set phase {4}. Phase mask cannot be used past phase {5}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.setEventPhase.phase, SmartPhase.Max - 1);
                        return false;
                    }
                    break;
                case SmartActions.IncEventPhase:
                    if (e.Action.incEventPhase.inc == 0 && e.Action.incEventPhase.dec == 0)
                    {
                        Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} is incrementing phase by 0, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
                        return false;
                    }
                    else if (e.Action.incEventPhase.inc > (uint)SmartPhase.Max || e.Action.incEventPhase.dec > (uint)SmartPhase.Max)
                    {
                        Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} attempts to increment phase by too large value, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
                        return false;
                    }
                    break;
                case SmartActions.Removeaurasfromspell:
                    if (e.Action.removeAura.spell != 0 && !IsSpellValid(e, e.Action.removeAura.spell))
                        return false;
                    break;
                case SmartActions.RandomPhase:
                    {
                        if (e.Action.randomPhase.phase1 >= (uint)SmartPhase.Max ||
                            e.Action.randomPhase.phase2 >= (uint)SmartPhase.Max ||
                            e.Action.randomPhase.phase3 >= (uint)SmartPhase.Max ||
                            e.Action.randomPhase.phase4 >= (uint)SmartPhase.Max ||
                            e.Action.randomPhase.phase5 >= (uint)SmartPhase.Max ||
                            e.Action.randomPhase.phase6 >= (uint)SmartPhase.Max)
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} attempts to set invalid phase, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
                            return false;
                        }
                    }
                    break;
                case SmartActions.RandomPhaseRange:       //PhaseMin, PhaseMax
                    {
                        if (e.Action.randomPhaseRange.phaseMin >= (uint)SmartPhase.Max ||
                            e.Action.randomPhaseRange.phaseMax >= (uint)SmartPhase.Max)
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} attempts to set invalid phase, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
                            return false;
                        }
                        if (!IsMinMaxValid(e, e.Action.randomPhaseRange.phaseMin, e.Action.randomPhaseRange.phaseMax))
                            return false;
                        break;
                    }
                case SmartActions.SummonCreature:
                    if (!IsCreatureValid(e, e.Action.summonCreature.creature))
                        return false;

                    if (e.Action.summonCreature.type < (uint)TempSummonType.TimedOrDeadDespawn || e.Action.summonCreature.type > (uint)TempSummonType.ManualDespawn)
                    {
                        Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses incorrect TempSummonType {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.summonCreature.type);
                        return false;
                    }
                    break;
                case SmartActions.CallKilledmonster:
                    if (!IsCreatureValid(e, e.Action.killedMonster.creature))
                        return false;

                    if (e.GetTargetType() == SmartTargets.Position)
                    {
                        Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses incorrect TargetType {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.GetTargetType());
                        return false;
                    }
                    break;
                case SmartActions.UpdateTemplate:
                    if (e.Action.updateTemplate.creature != 0 && !IsCreatureValid(e, e.Action.updateTemplate.creature))
                        return false;
                    break;
                case SmartActions.SetSheath:
                    if (e.Action.setSheath.sheath != 0 && e.Action.setSheath.sheath >= (uint)SheathState.Max)
                    {
                        Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses incorrect Sheath state {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.setSheath.sheath);
                        return false;
                    }
                    break;
                case SmartActions.SetReactState:
                    {
                        if (e.Action.react.state > (uint)ReactStates.Aggressive)
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Creature {0} Event {1} Action {2} uses invalid React State {3}, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.Action.react.state);
                            return false;
                        }
                        break;
                    }
                case SmartActions.SummonGo:
                    if (!IsGameObjectValid(e, e.Action.summonGO.entry))
                        return false;
                    break;
                case SmartActions.RemoveItem:
                    if (!IsItemValid(e, e.Action.item.entry))
                        return false;

                    if (!NotNULL(e, e.Action.item.count))
                        return false;
                    break;
                case SmartActions.AddItem:
                    if (!IsItemValid(e, e.Action.item.entry))
                        return false;

                    if (!NotNULL(e, e.Action.item.count))
                        return false;
                    break;
                case SmartActions.Teleport:
                    if (!CliDB.MapStorage.ContainsKey(e.Action.teleport.mapID))
                    {
                        Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Map entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.teleport.mapID);
                        return false;
                    }
                    break;
                case SmartActions.SetCounter:
                    if (e.Action.setCounter.counterId == 0)
                    {
                        Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses wrong counterId {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.setCounter.counterId);
                        return false;
                    }

                    if (e.Action.setCounter.value == 0)
                    {
                        Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses wrong value {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.setCounter.value);
                        return false;
                    }

                    break;
                case SmartActions.InstallAiTemplate:
                    if (e.Action.installTtemplate.id >= (uint)SmartAITemplate.End)
                    {
                        Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Creature {0} Event {1} Action {2} uses non-existent AI template id {3}, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.Action.installTtemplate.id);
                        return false;
                    }
                    break;
                case SmartActions.WpStop:
                    if (e.Action.wpStop.quest != 0 && !IsQuestValid(e, e.Action.wpStop.quest))
                        return false;
                    break;
                case SmartActions.WpStart:
                    {
                        if (Global.SmartAIMgr.GetPath(e.Action.wpStart.pathID) == null)
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Creature {0} Event {1} Action {2} uses non-existent WaypointPath id {3}, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.Action.wpStart.pathID);
                            return false;
                        }
                        if (e.Action.wpStart.quest != 0 && !IsQuestValid(e, e.Action.wpStart.quest))
                            return false;
                        if (e.Action.wpStart.reactState > (uint)ReactStates.Aggressive)
                        {
                            Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Creature {0} Event {1} Action {2} uses invalid React State {3}, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.Action.wpStart.reactState);
                            return false;
                        }
                        break;
                    }
                case SmartActions.CreateTimedEvent:
                    {
                        if (!IsMinMaxValid(e, e.Action.timeEvent.min, e.Action.timeEvent.max))
                            return false;

                        if (!IsMinMaxValid(e, e.Action.timeEvent.repeatMin, e.Action.timeEvent.repeatMax))
                            return false;
                        break;
                    }
                case SmartActions.CallRandomRangeTimedActionlist:
                    {
                        if (!IsMinMaxValid(e, e.Action.randTimedActionList.entry1, e.Action.randTimedActionList.entry2))
                            return false;
                        break;
                    }
                case SmartActions.SetPower:
                case SmartActions.AddPower:
                case SmartActions.RemovePower:
                    if (e.Action.power.powerType > (int)PowerType.Max)
                    {
                        Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Power {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.power.powerType);
                        return false;
                    }
                    break;
                case SmartActions.GameEventStop:
                    {
                        uint eventId = e.Action.gameEventStop.id;

                        var events = Global.GameEventMgr.GetEventMap();
                        if (eventId < 1 || eventId >= events.Length)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent event, eventId {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.gameEventStop.id);
                            return false;
                        }

                        GameEventData eventData = events[eventId];
                        if (!eventData.isValid())
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent event, eventId {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.gameEventStop.id);
                            return false;
                        }
                        break;
                    }
                case SmartActions.GameEventStart:
                    {
                        uint eventId = e.Action.gameEventStart.id;

                        var events = Global.GameEventMgr.GetEventMap();
                        if (eventId < 1 || eventId >= events.Length)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent event, eventId {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.gameEventStart.id);
                            return false;
                        }

                        GameEventData eventData = events[eventId];
                        if (!eventData.isValid())
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent event, eventId {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.gameEventStart.id);
                            return false;
                        }
                        break;
                    }
                case SmartActions.Equip:
                    {
                        if (e.GetScriptType() == SmartScriptType.Creature)
                        {
                            sbyte equipId = (sbyte)e.Action.equip.entry;

                            if (equipId != 0 && Global.ObjectMgr.GetEquipmentInfo((uint)e.entryOrGuid, equipId) == null)
                            {
                                Log.outError(LogFilter.Sql, "SmartScript: SMART_ACTION_EQUIP uses non-existent equipment info id {0} for creature {1}, skipped.", equipId, e.entryOrGuid);
                                return false;
                            }
                        }
                        break;
                    }
                case SmartActions.SetInstData:
                    {
                        if (e.Action.setInstanceData.type > 1)
                        {
                            Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses invalid data type {4} (value range 0-1), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.setInstanceData.type);
                            return false;
                        }
                        else if (e.Action.setInstanceData.type == 1)
                        {
                            if (e.Action.setInstanceData.data > (int)EncounterState.ToBeDecided)
                            {
                                Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses invalid boss state {4} (value range 0-5), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Action.setInstanceData.data);
                                return false;
                            }
                        }
                        break;
                    }
                case SmartActions.SetIngamePhaseId:
                    {
                        uint phaseId = e.Action.ingamePhaseId.id;
                        uint apply = e.Action.ingamePhaseId.apply;

                        if (apply != 0 && apply != 1)
                        {
                            Log.outError(LogFilter.Sql, "SmartScript: SMART_ACTION_SET_INGAME_PHASE_ID uses invalid apply value {0} (Should be 0 or 1) for creature {1}, skipped", apply, e.entryOrGuid);
                            return false;
                        }

                        if (!CliDB.PhaseStorage.ContainsKey(phaseId))
                        {
                            Log.outError(LogFilter.Sql, "SmartScript: SMART_ACTION_SET_INGAME_PHASE_ID uses invalid phaseid {0} for creature {1}, skipped", phaseId, e.entryOrGuid);
                            return false;
                        }
                        break;
                    }
                case SmartActions.SetIngamePhaseGroup:
                    {
                        uint phaseGroup = e.Action.ingamePhaseGroup.groupId;
                        uint apply = e.Action.ingamePhaseGroup.apply;

                        if (apply != 0 && apply != 1)
                        {
                            Log.outError(LogFilter.Sql, "SmartScript: SMART_ACTION_SET_INGAME_PHASE_GROUP uses invalid apply value {0} (Should be 0 or 1) for creature {1}, skipped", apply, e.entryOrGuid);
                            return false;
                        }

                        if (Global.DB2Mgr.GetPhasesForGroup(phaseGroup).Empty())
                        {
                            Log.outError(LogFilter.Sql, "SmartScript: SMART_ACTION_SET_INGAME_PHASE_GROUP uses invalid phase group id {0} for creature {1}, skipped", phaseGroup, e.entryOrGuid);
                            return false;
                        }
                        break;
                    }
                case SmartActions.ScenePlay:
                    {
                        if (Global.ObjectMgr.GetSceneTemplate(e.Action.scene.sceneId) == null)
                        {
                            Log.outError(LogFilter.Sql, "SmartScript: SMART_ACTION_SCENE_PLAY uses sceneId {0} but scene don't exist, skipped", e.Action.scene.sceneId);
                            return false;
                        }

                        break;
                    }
                case SmartActions.SceneCancel:
                    {
                        if (Global.ObjectMgr.GetSceneTemplate(e.Action.scene.sceneId) == null)
                        {
                            Log.outError(LogFilter.Sql, "SmartScript: SMART_ACTION_SCENE_CANCEL uses sceneId {0} but scene don't exist, skipped", e.Action.scene.sceneId);
                            return false;
                        }

                        break;
                    }
                case SmartActions.Follow:
                case SmartActions.SetOrientation:
                case SmartActions.StoreTargetList:
                case SmartActions.Evade:
                case SmartActions.FleeForAssist:
                case SmartActions.CombatStop:
                case SmartActions.Die:
                case SmartActions.SetInCombatWithZone:
                case SmartActions.SetActive:
                case SmartActions.WpResume:
                case SmartActions.KillUnit:
                case SmartActions.SetInvincibilityHpLevel:
                case SmartActions.ResetGobject:
                case SmartActions.AttackStart:
                case SmartActions.ThreatAllPct:
                case SmartActions.ThreatSinglePct:
                case SmartActions.SetInstData64:
                case SmartActions.AutoAttack:
                case SmartActions.AllowCombatMovement:
                case SmartActions.CallForHelp:
                case SmartActions.SetData:
                case SmartActions.SetVisibility:
                case SmartActions.WpPause:
                case SmartActions.SetFly:
                case SmartActions.SetRun:
                case SmartActions.SetSwim:
                case SmartActions.ForceDespawn:
                case SmartActions.SetUnitFlag:
                case SmartActions.RemoveUnitFlag:
                case SmartActions.Playmovie:
                case SmartActions.MoveToPos:
                case SmartActions.RespawnTarget:
                case SmartActions.CloseGossip:
                case SmartActions.TriggerTimedEvent:
                case SmartActions.RemoveTimedEvent:
                case SmartActions.OverrideScriptBaseObject:
                case SmartActions.ResetScriptBaseObject:
                case SmartActions.ActivateGobject:
                case SmartActions.CallScriptReset:
                case SmartActions.SetRangedMovement:
                case SmartActions.CallTimedActionlist:
                case SmartActions.SetNpcFlag:
                case SmartActions.AddNpcFlag:
                case SmartActions.RemoveNpcFlag:
                case SmartActions.CrossCast:
                case SmartActions.CallRandomTimedActionlist:
                case SmartActions.RandomMove:
                case SmartActions.SetUnitFieldBytes1:
                case SmartActions.RemoveUnitFieldBytes1:
                case SmartActions.InterruptSpell:
                case SmartActions.SendGoCustomAnim:
                case SmartActions.SetDynamicFlag:
                case SmartActions.AddDynamicFlag:
                case SmartActions.RemoveDynamicFlag:
                case SmartActions.JumpToPos:
                case SmartActions.SendGossipMenu:
                case SmartActions.GoSetLootState:
                case SmartActions.SendTargetToTarget:
                case SmartActions.SetHomePos:
                case SmartActions.SetHealthRegen:
                case SmartActions.SetRoot:
                case SmartActions.SetGoFlag:
                case SmartActions.AddGoFlag:
                case SmartActions.RemoveGoFlag:
                case SmartActions.SummonCreatureGroup:
                case SmartActions.MoveOffset:
                case SmartActions.SetCorpseDelay:
                case SmartActions.DisableEvade:
                    break;
                default:
                    Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Not handled action_type({0}), event_type({1}), Entry {2} SourceType {3} Event {4}, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
                    return false;
            }

            return true;
        }
        bool IsAnimKitValid(SmartScriptHolder e, uint entry)
        {
            if (!CliDB.AnimKitStorage.ContainsKey(entry))
            {
                Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent AnimKit entry {4}, skipped.",
                    e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
                return false;
            }
            return true;
        }
        bool IsTextValid(SmartScriptHolder e, uint id)
        {
            if (e.GetScriptType() != SmartScriptType.Creature)
                return true;

            uint entry = 0;
            if (e.GetEventType() == SmartEvents.TextOver)
            {
                entry = e.Event.textOver.creatureEntry;
            }
            else
            {
                switch (e.GetTargetType())
                {
                    case SmartTargets.CreatureDistance:
                    case SmartTargets.CreatureRange:
                    case SmartTargets.ClosestCreature:
                        return true; // ignore
                    default:
                        if (e.entryOrGuid < 0)
                        {
                            ulong guid = (ulong)-e.entryOrGuid;
                            CreatureData data = Global.ObjectMgr.GetCreatureData(guid);
                            if (data == null)
                            {
                                Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} using non-existent Creature guid {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), guid);
                                return false;
                            }
                            else
                                entry = data.id;
                        }
                        else
                            entry = (uint)e.entryOrGuid;
                        break;
                }
            }

            if (entry == 0 || !Global.CreatureTextMgr.TextExist(entry, (byte)id))
            {
                Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} using non-existent Text id {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), id);
                return false;
            }

            return true;
        }
        bool IsCreatureValid(SmartScriptHolder e, uint entry)
        {
            if (Global.ObjectMgr.GetCreatureTemplate(entry) == null)
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Creature entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
                return false;
            }
            return true;
        }
        bool IsGameObjectValid(SmartScriptHolder e, uint entry)
        {
            if (Global.ObjectMgr.GetGameObjectTemplate(entry) == null)
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent GameObject entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
                return false;
            }
            return true;
        }
        bool IsQuestValid(SmartScriptHolder e, uint entry)
        {
            if (Global.ObjectMgr.GetQuestTemplate(entry) == null)
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Quest entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
                return false;
            }
            return true;
        }
        bool IsSpellValid(SmartScriptHolder e, uint entry)
        {
            if (!Global.SpellMgr.HasSpellInfo(entry))
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Spell entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
                return false;
            }
            return true;
        }
        bool IsMinMaxValid(SmartScriptHolder e, uint min, uint max)
        {
            if (max < min)
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses min/max params wrong ({4}/{5}), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), min, max);
                return false;
            }
            return true;
        }
        bool NotNULL(SmartScriptHolder e, uint data)
        {
            if (data == 0)
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} Parameter can not be NULL, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
                return false;
            }
            return true;
        }
        bool IsEmoteValid(SmartScriptHolder e, uint entry)
        {
            if (!CliDB.EmotesStorage.ContainsKey(entry))
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Emote entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
                return false;
            }
            return true;
        }
        bool IsItemValid(SmartScriptHolder e, uint entry)
        {
            if (!CliDB.ItemSparseStorage.ContainsKey(entry))
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Item entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
                return false;
            }
            return true;
        }
        bool IsTextEmoteValid(SmartScriptHolder e, uint entry)
        {
            if (!CliDB.EmotesTextStorage.ContainsKey(entry))
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Text Emote entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
                return false;
            }
            return true;
        }
        bool IsAreaTriggerValid(SmartScriptHolder e, uint entry)
        {
            if (!CliDB.AreaTriggerStorage.ContainsKey(entry))
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent AreaTrigger entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
                return false;
            }
            return true;
        }
        bool IsSoundValid(SmartScriptHolder e, uint entry)
        {
            if (!CliDB.SoundKitStorage.ContainsKey(entry))
            {
                Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Sound entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
                return false;
            }
            return true;
        }

        public List<SmartScriptHolder> GetScript(int entry, SmartScriptType type)
        {
            List<SmartScriptHolder> temp = new List<SmartScriptHolder>();
            if (mEventMap[(uint)type].ContainsKey(entry))
            {
                foreach (var holder in mEventMap[(uint)type][entry])
                    temp.Add(new SmartScriptHolder(holder));
            }
            else
            {
                if (entry > 0)//first search is for guid (negative), do not drop error if not found
                    Log.outDebug(LogFilter.ScriptsAi, "SmartAIMgr.GetScript: Could not load Script for Entry {0} ScriptType {1}.", entry, type);
            }

            return temp;
        }

        public Dictionary<uint, SmartPath> GetPath(uint id)
        {
            return waypoint_map.LookupByKey(id);
        }

        public SmartScriptHolder FindLinkedSourceEvent(List<SmartScriptHolder> list, uint eventId)
        {
            var sch = list.Find(p => p.link == eventId);
            if (sch != null)
                return sch;

            return null;
        }

        public SmartScriptHolder FindLinkedEvent(List<SmartScriptHolder> list, uint link)
        {
            var sch = list.Find(p => p.event_id == link && p.GetEventType() == SmartEvents.Link);
            if (sch != null)
                return sch;

            return null;
        }

        MultiMap<int, SmartScriptHolder>[] mEventMap = new MultiMap<int, SmartScriptHolder>[(int)SmartScriptType.Max];
        Dictionary<uint, Dictionary<uint, SmartPath>> waypoint_map = new Dictionary<uint, Dictionary<uint, SmartPath>>();

        Dictionary<SmartScriptType, uint> SmartAITypeMask = new Dictionary<SmartScriptType, uint>
        {
            { SmartScriptType.Creature,         SmartScriptTypeMaskId.Creature },
            { SmartScriptType.GameObject,       SmartScriptTypeMaskId.Gameobject },
            { SmartScriptType.AreaTrigger,      SmartScriptTypeMaskId.Areatrigger },
            { SmartScriptType.Event,            SmartScriptTypeMaskId.Event },
            { SmartScriptType.Gossip,           SmartScriptTypeMaskId.Gossip },
            { SmartScriptType.Quest,            SmartScriptTypeMaskId.Quest },
            { SmartScriptType.Spell,            SmartScriptTypeMaskId.Spell },
            { SmartScriptType.Transport,        SmartScriptTypeMaskId.Transport },
            { SmartScriptType.Instance,         SmartScriptTypeMaskId.Instance },
            { SmartScriptType.TimedActionlist,  SmartScriptTypeMaskId.TimedActionlist },
            { SmartScriptType.Scene,            SmartScriptTypeMaskId.Scene }
        };

        Dictionary<SmartEvents, uint> SmartAIEventMask = new Dictionary<SmartEvents, uint>
        {
            { SmartEvents.UpdateIc,                 SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.TimedActionlist },
            { SmartEvents.UpdateOoc,                SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject + SmartScriptTypeMaskId.Instance },
            { SmartEvents.HealtPct,                 SmartScriptTypeMaskId.Creature },
            { SmartEvents.ManaPct,                  SmartScriptTypeMaskId.Creature },
            { SmartEvents.Aggro,                    SmartScriptTypeMaskId.Creature },
            { SmartEvents.Kill,                     SmartScriptTypeMaskId.Creature },
            { SmartEvents.Death,                    SmartScriptTypeMaskId.Creature },
            { SmartEvents.Evade,                    SmartScriptTypeMaskId.Creature },
            { SmartEvents.SpellHit,                 SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.Range,                    SmartScriptTypeMaskId.Creature },
            { SmartEvents.OocLos,                   SmartScriptTypeMaskId.Creature },
            { SmartEvents.Respawn,                  SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.TargetHealthPct,          SmartScriptTypeMaskId.Creature },
            { SmartEvents.VictimCasting,            SmartScriptTypeMaskId.Creature },
            { SmartEvents.FriendlyHealth,           SmartScriptTypeMaskId.Creature },
            { SmartEvents.FriendlyIsCc,             SmartScriptTypeMaskId.Creature },
            { SmartEvents.FriendlyMissingBuff,      SmartScriptTypeMaskId.Creature },
            { SmartEvents.SummonedUnit,             SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.TargetManaPct,            SmartScriptTypeMaskId.Creature },
            { SmartEvents.AcceptedQuest,            SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.RewardQuest,              SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.ReachedHome,              SmartScriptTypeMaskId.Creature },
            { SmartEvents.ReceiveEmote,             SmartScriptTypeMaskId.Creature },
            { SmartEvents.HasAura,                  SmartScriptTypeMaskId.Creature },
            { SmartEvents.TargetBuffed,             SmartScriptTypeMaskId.Creature },
            { SmartEvents.Reset,                    SmartScriptTypeMaskId.Creature },
            { SmartEvents.IcLos,                    SmartScriptTypeMaskId.Creature },
            { SmartEvents.PassengerBoarded,         SmartScriptTypeMaskId.Creature },
            { SmartEvents.PassengerRemoved,         SmartScriptTypeMaskId.Creature },
            { SmartEvents.Charmed,                  SmartScriptTypeMaskId.Creature },
            { SmartEvents.CharmedTarget,            SmartScriptTypeMaskId.Creature },
            { SmartEvents.SpellhitTarget,           SmartScriptTypeMaskId.Creature },
            { SmartEvents.Damaged,                  SmartScriptTypeMaskId.Creature },
            { SmartEvents.DamagedTarget,            SmartScriptTypeMaskId.Creature },
            { SmartEvents.Movementinform,           SmartScriptTypeMaskId.Creature },
            { SmartEvents.SummonDespawned,          SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.CorpseRemoved,            SmartScriptTypeMaskId.Creature },
            { SmartEvents.AiInit,                   SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.DataSet,                  SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.WaypointStart,            SmartScriptTypeMaskId.Creature },
            { SmartEvents.WaypointReached,          SmartScriptTypeMaskId.Creature },
            { SmartEvents.TransportAddplayer,       SmartScriptTypeMaskId.Transport },
            { SmartEvents.TransportAddcreature,     SmartScriptTypeMaskId.Transport },
            { SmartEvents.TransportRemovePlayer,    SmartScriptTypeMaskId.Transport },
            { SmartEvents.TransportRelocate,        SmartScriptTypeMaskId.Transport },
            { SmartEvents.InstancePlayerEnter,      SmartScriptTypeMaskId.Instance },
            { SmartEvents.AreatriggerOntrigger,     SmartScriptTypeMaskId.Areatrigger },
            { SmartEvents.QuestAccepted,            SmartScriptTypeMaskId.Quest },
            { SmartEvents.QuestObjCompletion,       SmartScriptTypeMaskId.Quest },
            { SmartEvents.QuestRewarded,            SmartScriptTypeMaskId.Quest },
            { SmartEvents.QuestCompletion,          SmartScriptTypeMaskId.Quest },
            { SmartEvents.QuestFail,                SmartScriptTypeMaskId.Quest },
            { SmartEvents.TextOver,                 SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.ReceiveHeal,              SmartScriptTypeMaskId.Creature },
            { SmartEvents.JustSummoned,             SmartScriptTypeMaskId.Creature },
            { SmartEvents.WaypointPaused,           SmartScriptTypeMaskId.Creature },
            { SmartEvents.WaypointResumed,          SmartScriptTypeMaskId.Creature },
            { SmartEvents.WaypointStopped,          SmartScriptTypeMaskId.Creature },
            { SmartEvents.WaypointEnded,            SmartScriptTypeMaskId.Creature },
            { SmartEvents.TimedEventTriggered,      SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.Update,                   SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.Link,                     SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject + SmartScriptTypeMaskId.Areatrigger + SmartScriptTypeMaskId.Event + SmartScriptTypeMaskId.Gossip + SmartScriptTypeMaskId.Quest + SmartScriptTypeMaskId.Spell + SmartScriptTypeMaskId.Transport + SmartScriptTypeMaskId.Instance },
            { SmartEvents.GossipSelect,             SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.JustCreated,              SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.GossipHello,              SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.FollowCompleted,          SmartScriptTypeMaskId.Creature },
            { SmartEvents.DummyEffect,              SmartScriptTypeMaskId.Spell    },
            { SmartEvents.IsBehindTarget,           SmartScriptTypeMaskId.Creature },
            { SmartEvents.GameEventStart,           SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.GameEventEnd,             SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.GoStateChanged,           SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.GoEventInform,            SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.ActionDone,               SmartScriptTypeMaskId.Creature },
            { SmartEvents.OnSpellclick,             SmartScriptTypeMaskId.Creature },
            { SmartEvents.FriendlyHealthPCT,        SmartScriptTypeMaskId.Creature },
            { SmartEvents.DistanceCreature,         SmartScriptTypeMaskId.Creature },
            { SmartEvents.DistanceGameobject,       SmartScriptTypeMaskId.Creature },
            { SmartEvents.CounterSet,               SmartScriptTypeMaskId.Creature + SmartScriptTypeMaskId.Gameobject },
            { SmartEvents.SceneStart,               SmartScriptTypeMaskId.Scene },
            { SmartEvents.SceneTrigger,             SmartScriptTypeMaskId.Scene },
            { SmartEvents.SceneCancel,              SmartScriptTypeMaskId.Scene },
            { SmartEvents.SceneComplete,            SmartScriptTypeMaskId.Scene }
        };
    }

    public class SmartPath
    {
        public SmartPath(uint _id, float _x, float _y, float _z)
        {
            id = _id;
            x = _x;
            y = _y;
            z = _z;
        }

        public uint id;
        public float x;
        public float y;
        public float z;
    }

    public class SmartScriptHolder
    {
        public SmartScriptHolder() { }
        public SmartScriptHolder(SmartScriptHolder other)
        {
            entryOrGuid = other.entryOrGuid;
            source_type = other.source_type;
            event_id = other.event_id;
            link = other.link;
            Event = other.Event;
            Action = other.Action;
            Target = other.Target;
            timer = other.timer;
            active = other.active;
            runOnce = other.runOnce;
            enableTimed = other.enableTimed;
        }

        public SmartScriptType GetScriptType() { return source_type; }
        public SmartEvents GetEventType() { return Event.type; }
        public SmartActions GetActionType() { return Action.type; }
        public SmartTargets GetTargetType() { return Target.type; }

        public int entryOrGuid;
        public SmartScriptType source_type;
        public uint event_id;
        public uint link;
        public SmartEvent Event;
        public SmartAction Action;
        public SmartTarget Target;
        public uint timer;
        public bool active;
        public bool runOnce;
        public bool enableTimed;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct SmartEvent
    {
        [FieldOffset(0)]
        public SmartEvents type;

        [FieldOffset(4)]
        public uint event_phase_mask;

        [FieldOffset(8)]
        public uint event_chance;

        [FieldOffset(12)]
        public SmartEventFlags event_flags;

        [FieldOffset(16)]
        public MinMaxRepeat minMaxRepeat;

        [FieldOffset(16)]
        public Kill kill;

        [FieldOffset(16)]
        public SpellHit spellHit;

        [FieldOffset(16)]
        public Los los;

        [FieldOffset(16)]
        public Respawn respawn;

        [FieldOffset(16)]
        public MinMax minMax;

        [FieldOffset(16)]
        public TargetCasting targetCasting;

        [FieldOffset(16)]
        public FriendlyHealt friendlyHealth;

        [FieldOffset(16)]
        public FriendlyCC friendlyCC;

        [FieldOffset(16)]
        public MissingBuff missingBuff;

        [FieldOffset(16)]
        public Summoned summoned;

        [FieldOffset(16)]
        public Quest quest;

        [FieldOffset(16)]
        public Emote emote;

        [FieldOffset(16)]
        public Aura aura;

        [FieldOffset(16)]
        public Charm charm;

        [FieldOffset(16)]
        public TargetAura targetAura;

        [FieldOffset(16)]
        public MovementInform movementInform;

        [FieldOffset(16)]
        public DataSet dataSet;

        [FieldOffset(16)]
        public Waypoint waypoint;

        [FieldOffset(16)]
        public TransportAddCreature transportAddCreature;

        [FieldOffset(16)]
        public TransportRelocate transportRelocate;

        [FieldOffset(16)]
        public InstancePlayerEnter instancePlayerEnter;

        [FieldOffset(16)]
        public Areatrigger areatrigger;

        [FieldOffset(16)]
        public TextOver textOver;

        [FieldOffset(16)]
        public TimedEvent timedEvent;

        [FieldOffset(16)]
        public Gossip gossip;

        [FieldOffset(16)]
        public Dummy dummy;

        [FieldOffset(16)]
        public BehindTarget behindTarget;

        [FieldOffset(16)]
        public GameEvent gameEvent;

        [FieldOffset(16)]
        public GoStateChanged goStateChanged;

        [FieldOffset(16)]
        public EventInform eventInform;

        [FieldOffset(16)]
        public DoAction doAction;

        [FieldOffset(16)]
        public FriendlyHealthPct friendlyHealthPct;

        [FieldOffset(16)]
        public Distance distance;

        [FieldOffset(16)]
        public Counter counter;

        [FieldOffset(16)]
        public Scene scene;

        [FieldOffset(16)]
        public Raw raw;

        [FieldOffset(32)]
        public string param_string;

        #region Structs
        public struct MinMaxRepeat
        {
            public uint min;
            public uint max;
            public uint repeatMin;
            public uint repeatMax;
        }
        public struct Kill
        {
            public uint cooldownMin;
            public uint cooldownMax;
            public uint playerOnly;
            public uint creature;
        }
        public struct SpellHit
        {
            public uint spell;
            public uint school;
            public uint cooldownMin;
            public uint cooldownMax;
        }
        public struct Los
        {
            public uint noHostile;
            public uint maxDist;
            public uint cooldownMin;
            public uint cooldownMax;
        }
        public struct Respawn
        {
            public uint type;
            public uint map;
            public uint area;
        }
        public struct MinMax
        {
            public uint repeatMin;
            public uint repeatMax;
        }
        public struct TargetCasting
        {
            public uint repeatMin;
            public uint repeatMax;
            public uint spellId;
        }
        public struct FriendlyHealt
        {
            public uint hpDeficit;
            public uint radius;
            public uint repeatMin;
            public uint repeatMax;
        }
        public struct FriendlyCC
        {
            public uint radius;
            public uint repeatMin;
            public uint repeatMax;
        }
        public struct MissingBuff
        {
            public uint spell;
            public uint radius;
            public uint repeatMin;
            public uint repeatMax;
        }
        public struct Summoned
        {
            public uint creature;
            public uint cooldownMin;
            public uint cooldownMax;
        }
        public struct Quest
        {
            public uint questId;
        }
        public struct Emote
        {
            public uint emoteId;
            public uint cooldownMin;
            public uint cooldownMax;
        }
        public struct Aura
        {
            public uint spell;
            public uint count;
            public uint repeatMin;
            public uint repeatMax;
        }
        public struct Charm
        {
            public uint onRemove;
        }
        public struct TargetAura
        {
            public uint spell;
            public uint count;
            public uint repeatMin;
            public uint repeatMax;
        }
        public struct MovementInform
        {
            public uint type;
            public uint id;
        }
        public struct DataSet
        {
            public uint id;
            public uint value;
            public uint cooldownMin;
            public uint cooldownMax;
        }
        public struct Waypoint
        {
            public uint pointID;
            public uint pathID;
        }
        public struct TransportAddCreature
        {
            public uint creature;
        }
        public struct TransportRelocate
        {
            public uint pointID;
        }
        public struct InstancePlayerEnter
        {
            public uint team;
            public uint cooldownMin;
            public uint cooldownMax;
        }
        public struct Areatrigger
        {
            public uint id;
        }
        public struct TextOver
        {
            public uint textGroupID;
            public uint creatureEntry;
        }
        public struct TimedEvent
        {
            public uint id;
        }
        public struct Gossip
        {
            public uint sender;
            public uint action;
        }
        public struct Dummy
        {
            public uint spell;
            public uint effIndex;
        }
        public struct BehindTarget
        {
            public uint cooldownMin;
            public uint cooldownMax;
        }
        public struct GameEvent
        {
            public uint gameEventId;
        }
        public struct GoStateChanged
        {
            public uint state;
        }
        public struct EventInform
        {
            public uint eventId;
        }
        public struct DoAction
        {
            public uint eventId;
        }
        public struct FriendlyHealthPct
        {
            public uint minHpPct;
            public uint maxHpPct;
            public uint repeatMin;
            public uint repeatMax;
        }
        public struct Distance
        {
            public uint guid;
            public uint entry;
            public uint dist;
            public uint repeat;
        }
        public struct Counter
        {
            public uint id;
            public uint value;
            public uint cooldownMin;
            public uint cooldownMax;
        }
        public struct Scene
        {
            public uint sceneId;
        }
        public struct Raw
        {
            public uint param1;
            public uint param2;
            public uint param3;
            public uint param4;
        }
        #endregion
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct SmartAction
    {
        [FieldOffset(0)]
        public SmartActions type;

        [FieldOffset(4)]
        public Talk talk;

        [FieldOffset(4)]
        public Faction faction;

        [FieldOffset(4)]
        public MorphOrMount morphOrMount;

        [FieldOffset(4)]
        public Sound sound;

        [FieldOffset(4)]
        public Emote emote;

        [FieldOffset(4)]
        public Quest quest;

        [FieldOffset(4)]
        public QuestOffer questOffer;

        [FieldOffset(4)]
        public React react;

        [FieldOffset(4)]
        public RandomEmote randomEmote;

        [FieldOffset(4)]
        public Cast cast;

        [FieldOffset(4)]
        public CrossCast crossCast;

        [FieldOffset(4)]
        public SummonCreature summonCreature;

        [FieldOffset(4)]
        public ThreatPCT threatPCT;

        [FieldOffset(4)]
        public CastCreatureOrGO castCreatureOrGO;

        [FieldOffset(4)]
        public AddUnitFlag addUnitFlag;

        [FieldOffset(4)]
        public RemoveUnitFlag removeUnitFlag;

        [FieldOffset(4)]
        public AutoAttack autoAttack;

        [FieldOffset(4)]
        public CombatMove combatMove;

        [FieldOffset(4)]
        public SetEventPhase setEventPhase;

        [FieldOffset(4)]
        public IncEventPhase incEventPhase;

        [FieldOffset(4)]
        public CastedCreatureOrGO castedCreatureOrGO;

        [FieldOffset(4)]
        public RemoveAura removeAura;

        [FieldOffset(4)]
        public Follow follow;

        [FieldOffset(4)]
        public RandomPhase randomPhase;

        [FieldOffset(4)]
        public RandomPhaseRange randomPhaseRange;

        [FieldOffset(4)]
        public KilledMonster killedMonster;

        [FieldOffset(4)]
        public SetInstanceData setInstanceData;

        [FieldOffset(4)]
        public SetInstanceData64 setInstanceData64;

        [FieldOffset(4)]
        public UpdateTemplate updateTemplate;

        [FieldOffset(4)]
        public CallHelp callHelp;

        [FieldOffset(4)]
        public SetSheath setSheath;

        [FieldOffset(4)]
        public ForceDespawn forceDespawn;

        [FieldOffset(4)]
        public InvincHP invincHP;

        [FieldOffset(4)]
        public IngamePhaseId ingamePhaseId;

        [FieldOffset(4)]
        public IngamePhaseGroup ingamePhaseGroup;

        [FieldOffset(4)]
        public SetData setData;

        [FieldOffset(4)]
        public MoveRandom moveRandom;

        [FieldOffset(4)]
        public Visibility visibility;

        [FieldOffset(4)]
        public SummonGO summonGO;

        [FieldOffset(4)]
        public Active active;

        [FieldOffset(4)]
        public Taxi taxi;

        [FieldOffset(4)]
        public WpStart wpStart;

        [FieldOffset(4)]
        public WpPause wpPause;

        [FieldOffset(4)]
        public WpStop wpStop;

        [FieldOffset(4)]
        public Item item;

        [FieldOffset(4)]
        public InstallTtemplate installTtemplate;

        [FieldOffset(4)]
        public SetRun setRun;

        [FieldOffset(4)]
        public SetFly setFly;

        [FieldOffset(4)]
        public SetSwim setSwim;

        [FieldOffset(4)]
        public Teleport teleport;

        [FieldOffset(4)]
        public SetCounter setCounter;

        [FieldOffset(4)]
        public StoreVar storeVar;

        [FieldOffset(4)]
        public StoreTargets storeTargets;

        [FieldOffset(4)]
        public TimeEvent timeEvent;

        [FieldOffset(4)]
        public Movie movie;

        [FieldOffset(4)]
        public Equip equip;

        [FieldOffset(4)]
        public UnitFlag unitFlag;

        [FieldOffset(4)]
        public SetunitByte setunitByte;

        [FieldOffset(4)]
        public DelunitByte delunitByte;

        [FieldOffset(4)]
        public EnterVehicle enterVehicle;

        [FieldOffset(4)]
        public TimedActionList timedActionList;

        [FieldOffset(4)]
        public RandTimedActionList randTimedActionList;

        [FieldOffset(4)]
        public InterruptSpellCasting interruptSpellCasting;

        [FieldOffset(4)]
        public SendGoCustomAnim sendGoCustomAnim;

        [FieldOffset(4)]
        public Jump jump;

        [FieldOffset(4)]
        public Flee flee;

        [FieldOffset(4)]
        public RespawnTarget respawnTarget;

        [FieldOffset(4)]
        public MoveToPos moveToPos;

        [FieldOffset(4)]
        public SendGossipMenu sendGossipMenu;

        [FieldOffset(4)]
        public SetGoLootState setGoLootState;

        [FieldOffset(4)]
        public SendTargetToTarget sendTargetToTarget;

        [FieldOffset(4)]
        public SetRangedMovement setRangedMovement;

        [FieldOffset(4)]
        public SetHealthRegen setHealthRegen;

        [FieldOffset(4)]
        public SetRoot setRoot;

        [FieldOffset(4)]
        public GoFlag goFlag;

        [FieldOffset(4)]
        public CreatureGroup creatureGroup;

        [FieldOffset(4)]
        public Power power;

        [FieldOffset(4)]
        public GameEventStop gameEventStop;

        [FieldOffset(4)]
        public GameEventStart gameEventStart;

        [FieldOffset(4)]
        public ClosestWaypointFromList closestWaypointFromList;

        [FieldOffset(4)]
        public RandomSound randomSound;

        [FieldOffset(4)]
        public CorpseDelay corpseDelay;

        [FieldOffset(4)]
        public DisableEvade disableEvade;

        [FieldOffset(4)]
        public AnimKit animKit;

        [FieldOffset(4)]
        public Scene scene;

        [FieldOffset(4)]
        public Raw raw;

        #region Stucts
        public struct Talk
        {
            public uint textGroupId;
            public uint duration;
            public uint useTalkTarget;
        }
        public struct Faction
        {
            public uint factionID;
        }
        public struct MorphOrMount
        {
            public uint creature;
            public uint model;
        }
        public struct Sound
        {
            public uint soundId;
            public uint onlySelf;
        }
        public struct Emote
        {
            public uint emoteId;
        }
        public struct Quest
        {
            public uint questId;
        }
        public struct QuestOffer
        {
            public uint questId;
            public uint directAdd;
        }
        public struct React
        {
            public uint state;
        }
        public struct RandomEmote
        {
            public uint emote1;
            public uint emote2;
            public uint emote3;
            public uint emote4;
            public uint emote5;
            public uint emote6;
        }
        public struct Cast
        {
            public uint spell;
            public uint castFlags;
            public uint triggerFlags;
        }
        public struct CrossCast
        {
            public uint spell;
            public uint castFlags;
            public uint targetType;
            public uint targetParam1;
            public uint targetParam2;
            public uint targetParam3;
        }
        public struct SummonCreature
        {
            public uint creature;
            public uint type;
            public uint duration;
            public uint storageID;
            public uint attackInvoker;
        }
        public struct ThreatPCT
        {
            public uint threatINC;
            public uint threatDEC;
        }
        public struct CastCreatureOrGO
        {
            public uint quest;
            public uint spell;
        }
        public struct AddUnitFlag
        {
            public uint flag1;
            public uint flag2;
            public uint flag3;
            public uint flag4;
            public uint flag5;
            public uint flag6;
        }
        public struct RemoveUnitFlag
        {
            public uint flag1;
            public uint flag2;
            public uint flag3;
            public uint flag4;
            public uint flag5;
            public uint flag6;
        }
        public struct AutoAttack
        {
            public uint attack;
        }
        public struct CombatMove
        {
            public uint move;
        }
        public struct SetEventPhase
        {
            public uint phase;
        }
        public struct IncEventPhase
        {
            public uint inc;
            public uint dec;
        }
        public struct CastedCreatureOrGO
        {
            public uint creature;
            public uint spell;
        }
        public struct RemoveAura
        {
            public uint spell;
        }
        public struct Follow
        {
            public uint dist;
            public uint angle;
            public uint entry;
            public uint credit;
            public uint creditType;
        }
        public struct RandomPhase
        {
            public uint phase1;
            public uint phase2;
            public uint phase3;
            public uint phase4;
            public uint phase5;
            public uint phase6;
        }
        public struct RandomPhaseRange
        {
            public uint phaseMin;
            public uint phaseMax;
        }
        public struct KilledMonster
        {
            public uint creature;
        }
        public struct SetInstanceData
        {
            public uint field;
            public uint data;
            public uint type;
        }
        public struct SetInstanceData64
        {
            public uint field;
        }
        public struct UpdateTemplate
        {
            public uint creature;
            public uint updateLevel;
        }
        public struct CallHelp
        {
            public uint range;
            public uint withEmote;
        }
        public struct SetSheath
        {
            public uint sheath;
        }
        public struct ForceDespawn
        {
            public uint delay;
        }
        public struct InvincHP
        {
            public uint minHP;
            public uint percent;
        }
        public struct IngamePhaseId
        {
            public uint id;
            public uint apply;
        }
        public struct IngamePhaseGroup
        {
            public uint groupId;
            public uint apply;
        }
        public struct SetData
        {
            public uint field;
            public uint data;
        }
        public struct MoveRandom
        {
            public uint distance;
        }
        public struct Visibility
        {
            public uint state;
        }
        public struct SummonGO
        {
            public uint entry;
            public uint despawnTime;
        }
        public struct Active
        {
            public uint state;
        }
        public struct Taxi
        {
            public uint id;
        }
        public struct WpStart
        {
            public uint run;
            public uint pathID;
            public uint repeat;
            public uint quest;
            public uint despawnTime;
            public uint reactState;
        }
        public struct WpPause
        {
            public uint delay;
        }
        public struct WpStop
        {
            public uint despawnTime;
            public uint quest;
            public uint fail;
        }
        public struct Item
        {
            public uint entry;
            public uint count;
        }
        public struct InstallTtemplate
        {
            public uint id;
            public uint param1;
            public uint param2;
            public uint param3;
            public uint param4;
            public uint param5;
        }
        public struct SetRun
        {
            public uint run;
        }
        public struct SetFly
        {
            public uint fly;
        }
        public struct SetSwim
        {
            public uint swim;
        }
        public struct Teleport
        {
            public uint mapID;
        }
        public struct SetCounter
        {
            public uint counterId;
            public uint value;
            public uint reset;
        }
        public struct StoreVar
        {
            public uint id;
            public uint number;
        }
        public struct StoreTargets
        {
            public uint id;
        }
        public struct TimeEvent
        {
            public uint id;
            public uint min;
            public uint max;
            public uint repeatMin;
            public uint repeatMax;
            public uint chance;
        }
        public struct Movie
        {
            public uint entry;
        }

        public struct Equip
        {
            public uint entry;
            public uint mask;
            public uint slot1;
            public uint slot2;
            public uint slot3;
        }
        public struct UnitFlag
        {
            public uint flag;
            public uint type;
        }
        public struct SetunitByte
        {
            public uint byte1;
            public uint type;
        }
        public struct DelunitByte
        {
            public uint byte1;
            public uint type;
        }
        public struct EnterVehicle
        {
            public uint seat;
        }
        public struct TimedActionList
        {
            public uint id;
            public uint timerType;
        }
        public struct RandTimedActionList
        {
            public uint entry1;
            public uint entry2;
            public uint entry3;
            public uint entry4;
            public uint entry5;
            public uint entry6;
        }
        public struct InterruptSpellCasting
        {
            public uint withDelayed;
            public uint spell_id;
            public uint withInstant;
        }
        public struct SendGoCustomAnim
        {
            public uint anim;
        }
        public struct Jump
        {
            public uint speedxy;
            public uint speedz;
        }
        public struct Flee
        {
            public uint withEmote;
        }
        public struct RespawnTarget
        {
            public uint goRespawnTime;
        }
        public struct MoveToPos
        {
            public uint pointId;
            public uint transport;
            public uint disablePathfinding;
        }
        public struct SendGossipMenu
        {
            public uint gossipMenuId;
            public uint gossipNpcTextId;
        }
        public struct SetGoLootState
        {
            public uint state;
        }
        public struct SendTargetToTarget
        {
            public uint id;
        }
        public struct SetRangedMovement
        {
            public uint distance;
            public uint angle;
        }
        public struct SetHealthRegen
        {
            public uint regenHealth;
        }
        public struct SetRoot
        {
            public uint root;
        }
        public struct GoFlag
        {
            public uint flag;
        }
        public struct CreatureGroup
        {
            public uint group;
            public uint attackInvoker;
        }
        public struct Power
        {
            public uint powerType;
            public uint newPower;
        }
        public struct GameEventStop
        {
            public uint id;
        }
        public struct GameEventStart
        {
            public uint id;
        }
        public struct ClosestWaypointFromList
        {
            public uint wp1;
            public uint wp2;
            public uint wp3;
            public uint wp4;
            public uint wp5;
            public uint wp6;
        }
        public struct RandomSound
        {
            public uint sound1;
            public uint sound2;
            public uint sound3;
            public uint sound4;
            public uint sound5;
            public uint onlySelf;
        }
        public struct CorpseDelay
        {
            public uint timer;
        }
        public struct DisableEvade
        {
            public uint disable;
        }
        public struct AnimKit
        {
            public uint animKit;
            public uint type;
        }
        public struct Scene
        {
            public uint sceneId;
        }
        public struct Raw
        {
            public uint param1;
            public uint param2;
            public uint param3;
            public uint param4;
            public uint param5;
            public uint param6;
        }
        #endregion
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct SmartTarget
    {
        [FieldOffset(0)]
        public SmartTargets type;

        [FieldOffset(4)]
        public float x;

        [FieldOffset(8)]
        public float y;

        [FieldOffset(12)]
        public float z;

        [FieldOffset(16)]
        public float o;

        [FieldOffset(20)]
        public UnitRange unitRange;

        [FieldOffset(20)]
        public UnitGUID unitGUID;

        [FieldOffset(20)]
        public UnitDistance unitDistance;

        [FieldOffset(20)]
        public PlayerDistance playerDistance;

        [FieldOffset(20)]
        public PlayerRange playerRange;

        [FieldOffset(20)]
        public Stored stored;

        [FieldOffset(20)]
        public GoRange goRange;

        [FieldOffset(20)]
        public GoGUID goGUID;

        [FieldOffset(20)]
        public GoDistance goDistance;

        [FieldOffset(20)]
        public Position postion;

        [FieldOffset(20)]
        public Closest closest;

        [FieldOffset(20)]
        public ClosestAttackable closestAttackable;

        [FieldOffset(20)]
        public ClosestFriendly closestFriendly;

        [FieldOffset(20)]
        public Vehicle vehicle;

        [FieldOffset(20)]
        public Raw raw;

        #region Structs

        public struct UnitRange
        {
            public uint creature;
            public uint minDist;
            public uint maxDist;
        }
        public struct UnitGUID
        {
            public uint dbGuid;
            public uint entry;
        }
        public struct UnitDistance
        {
            public uint creature;
            public uint dist;
        }
        public struct PlayerDistance
        {
            public uint dist;
        }
        public struct PlayerRange
        {
            public uint minDist;
            public uint maxDist;
        }
        public struct Stored
        {
            public uint id;
        }
        public struct GoRange
        {
            public uint entry;
            public uint minDist;
            public uint maxDist;
        }
        public struct GoGUID
        {
            public uint dbGuid;
            public uint entry;
        }
        public struct GoDistance
        {
            public uint entry;
            public uint dist;
        }
        public struct Position
        {
            public uint map;
        }
        public struct Closest
        {
            public uint entry;
            public uint dist;
            public uint dead;
        }
        public struct ClosestAttackable
        {
            public uint maxDist;
        }
        public struct ClosestFriendly
        {
            public uint maxDist;
        }
        public struct Vehicle
        {
            public uint seat;
        }
        public struct Raw
        {
            public uint param1;
            public uint param2;
            public uint param3;
        }
        #endregion
    }
}
