void GameEventMgr::LoadFromDB()
{
    {
        QueryResult* result = WorldDatabase.Query("SELECT MAX(entry) FROM game_event");
        if (!result)
        {
            sLog.outString(">> Table game_event is empty.");
            sLog.outString();
            return;
        }

        Field* fields = result->Fetch();

        uint32 max_event_id = fields[0].GetUInt16();
        delete result;

        mGameEvent.resize(max_event_id + 1);
    }

    QueryResult* result = WorldDatabase.Query("SELECT entry,UNIX_TIMESTAMP(start_time),UNIX_TIMESTAMP(end_time),occurence,length,holiday,linkedTo,description FROM game_event");
    if (!result)
    {
        mGameEvent.clear();
        sLog.outString(">> Table game_event is empty!");
        sLog.outString();
        return;
    }

    uint32 count = 0;

    {
        BarGoLink bar(result->GetRowCount());
        do
        {
            Field* fields = result->Fetch();

            bar.step();

            uint16 event_id = fields[0].GetUInt16();
            if (event_id == 0)
            {
                sLog.outErrorDb("`game_event` game event id (%i) is reserved and can't be used.", event_id);
                continue;
            }

            GameEventData& pGameEvent = mGameEvent[event_id];
            uint64 starttime        = fields[1].GetUInt64();
            pGameEvent.start        = time_t(starttime);
            uint64 endtime          = fields[2].GetUInt64();
            pGameEvent.end          = time_t(endtime);
            pGameEvent.occurence    = fields[3].GetUInt32();
            pGameEvent.length       = fields[4].GetUInt32();
            pGameEvent.holiday_id   = HolidayIds(fields[5].GetUInt32());
            pGameEvent.linkedTo     = fields[6].GetUInt32();
            pGameEvent.description  = fields[7].GetCppString();

            if (pGameEvent.occurence == 0)
            {
                sLog.outBasic("Event %u (%s) disabled", event_id, pGameEvent.description.c_str());
                pGameEvent.start = time_t(FAR_FUTURE);
                pGameEvent.occurence = pGameEvent.length;
            }
            if (pGameEvent.length == 0)                     // length>0 is validity check
            {
                sLog.outErrorDb("`game_event` game event id (%i) have length 0 and can't be used.", event_id);
                pGameEvent.start = time_t(FAR_FUTURE);
            }

            if (pGameEvent.occurence < pGameEvent.length)   // occurence < length is useless. This also asserts that occurence > 0!
            {
                sLog.outErrorDb("`game_event` game event id (%i) has occurence %u  < length %u and can't be used.", event_id, pGameEvent.occurence, pGameEvent.length);
                pGameEvent.start = time_t(FAR_FUTURE);
            }

            ++count;
        }
        while (result->NextRow());
        delete result;

        for (uint16 itr = 1; itr < mGameEvent.size(); ++itr)
        {
            if (mGameEvent[itr].isValid() && mGameEvent[itr].linkedTo != 0 && (mGameEvent[itr].linkedTo >= mGameEvent.size() || mGameEvent[itr].linkedTo < 0 || !mGameEvent[mGameEvent[itr].linkedTo].isValid()))
            {
                sLog.outErrorDb("`game_event` game event id (%i) is Linked to invalid event %u", itr, mGameEvent[itr].linkedTo);
                mGameEvent[itr].linkedTo = 0;
            }
        }
        sLog.outString();
        sLog.outString(">> Loaded %u game events", count);
    }

    std::map<uint16, int16> pool2event;                     // for check unique spawn event associated with pool
    std::map<uint32, int16> creature2event;                 // for check unique spawn event associated with creature
    std::map<uint32, int16> go2event;                       // for check unique spawn event associated with gameobject

    // list only positive event top pools, filled at creature/gameobject loading
    mGameEventSpawnPoolIds.resize(mGameEvent.size());

    mGameEventCreatureGuids.resize(mGameEvent.size() * 2 - 1);
    //                                   1              2
    result = WorldDatabase.Query("SELECT creature.guid, game_event_creature.event "
                                 "FROM creature JOIN game_event_creature ON creature.guid = game_event_creature.guid");

    count = 0;
    if (!result)
    {
        BarGoLink bar(1);
        bar.step();

        sLog.outString();
        sLog.outString(">> Loaded %u creatures in game events", count);
    }
    else
    {
        BarGoLink bar(result->GetRowCount());
        do
        {
            Field* fields = result->Fetch();

            bar.step();

            uint32 guid    = fields[0].GetUInt32();
            int16 event_id = fields[1].GetInt16();

            if (event_id == 0)
            {
                sLog.outErrorDb("`game_event_creature` game event id (%i) not allowed", event_id);
                continue;
            }

            if (!IsValidEvent(std::abs(event_id)))
            {
                sLog.outErrorDb("`game_event_creature` game event id (%i) not exist in `game_event`", event_id);
                continue;
            }

            int32 internal_event_id = mGameEvent.size() + event_id - 1;

            ++count;

            // spawn objects at event can be grouped in pools and then affected pools have stricter requirements for this case
            if (event_id > 0)
            {
                creature2event[guid] = event_id;

                // not list explicitly creatures from pools in event creature list
                if (uint16 topPoolId =  sPoolMgr.IsPartOfTopPool<Creature>(guid))
                {
                    int16& eventRef = pool2event[topPoolId];
                    if (eventRef != 0)
                    {
                        if (eventRef != event_id)
                            sLog.outErrorDb("`game_event_creature` have creature (GUID: %u) for event %i from pool or subpool of pool (ID: %u) but pool have already content from event %i. Pool don't must have content for different events!", guid, event_id, topPoolId, eventRef);
                    }
                    else
                    {
                        eventRef = event_id;
                        mGameEventSpawnPoolIds[event_id].push_back(topPoolId);
                        sPoolMgr.RemoveAutoSpawnForPool(topPoolId);
                    }

                    continue;
                }
            }

            GuidList& crelist = mGameEventCreatureGuids[internal_event_id];
            crelist.push_back(guid);
        }
        while (result->NextRow());
        delete result;

        sLog.outString();
        sLog.outString(">> Loaded %u creatures in game events", count);
    }

    mGameEventGameobjectGuids.resize(mGameEvent.size() * 2 - 1);
    //                                   1                2
    result = WorldDatabase.Query("SELECT gameobject.guid, game_event_gameobject.event "
                                 "FROM gameobject JOIN game_event_gameobject ON gameobject.guid=game_event_gameobject.guid");

    count = 0;
    if (!result)
    {
        BarGoLink bar(1);
        bar.step();

        sLog.outString();
        sLog.outString(">> Loaded %u gameobjects in game events", count);
    }
    else
    {
        BarGoLink bar(result->GetRowCount());
        do
        {
            Field* fields = result->Fetch();

            bar.step();

            uint32 guid    = fields[0].GetUInt32();
            int16 event_id = fields[1].GetInt16();

            if (event_id == 0)
            {
                sLog.outErrorDb("`game_event_gameobject` game event id (%i) not allowed", event_id);
                continue;
            }

            if (!IsValidEvent(std::abs(event_id)))
            {
                sLog.outErrorDb("`game_event_gameobject` game event id (%i) not exist in `game_event`", event_id);
                continue;
            }

            int32 internal_event_id = mGameEvent.size() + event_id - 1;

            ++count;

            // spawn objects at event can be grouped in pools and then affected pools have stricter requirements for this case
            if (event_id > 0)
            {
                go2event[guid] = event_id;

                // not list explicitly gameobjects from pools in event gameobject list
                if (uint16 topPoolId =  sPoolMgr.IsPartOfTopPool<GameObject>(guid))
                {
                    int16& eventRef = pool2event[topPoolId];
                    if (eventRef != 0)
                    {
                        if (eventRef != event_id)
                            sLog.outErrorDb("`game_event_gameobject` have gameobject (GUID: %u) for event %i from pool or subpool of pool (ID: %u) but pool have already content from event %i. Pool don't must have content for different events!", guid, event_id, topPoolId, eventRef);
                    }
                    else
                    {
                        eventRef = event_id;
                        mGameEventSpawnPoolIds[event_id].push_back(topPoolId);
                        sPoolMgr.RemoveAutoSpawnForPool(topPoolId);
                    }

                    continue;
                }
            }

            GuidList& golist = mGameEventGameobjectGuids[internal_event_id];
            golist.push_back(guid);
        }
        while (result->NextRow());
        delete result;

        sLog.outString();
        sLog.outString(">> Loaded %u gameobjects in game events", count);
    }

    // now recheck that all eventPools linked with events after our skip pools with parents
    for (std::map<uint16, int16>::const_iterator itr = pool2event.begin(); itr != pool2event.end();  ++itr)
    {
        uint16 pool_id = itr->first;
        int16 event_id = itr->second;

        sPoolMgr.CheckEventLinkAndReport(pool_id, event_id, creature2event, go2event);
    }

    mGameEventCreatureData.resize(mGameEvent.size());
    //                                   0              1                             2
    result = WorldDatabase.Query("SELECT creature.guid, game_event_creature_data.event, game_event_creature_data.modelid,"
                                 //   3                                      4
                                 "game_event_creature_data.equipment_id, game_event_creature_data.entry_id, "
                                 //   5                                     6
                                 "game_event_creature_data.spell_start, game_event_creature_data.spell_end "
                                 "FROM creature JOIN game_event_creature_data ON creature.guid=game_event_creature_data.guid");

    count = 0;
    if (!result)
    {
        BarGoLink bar(1);
        bar.step();

        sLog.outString();
        sLog.outString(">> Loaded %u creature reactions at game events", count);
    }
    else
    {
        BarGoLink bar(result->GetRowCount());
        do
        {
            Field* fields = result->Fetch();

            bar.step();
            uint32 guid     = fields[0].GetUInt32();
            uint16 event_id = fields[1].GetUInt16();

            if (event_id == 0)
            {
                sLog.outErrorDb("`game_event_creature_data` game event id (%i) is reserved and can't be used." , event_id);
                continue;
            }

            if (!IsValidEvent(event_id))
            {
                sLog.outErrorDb("`game_event_creature_data` game event id (%u) not exist in `game_event`", event_id);
                continue;
            }

            ++count;
            GameEventCreatureDataList& equiplist = mGameEventCreatureData[event_id];
            GameEventCreatureData newData;
            newData.modelid = fields[2].GetUInt32();
            newData.equipment_id = fields[3].GetUInt32();
            newData.entry_id = fields[4].GetUInt32();
            newData.spell_id_start = fields[5].GetUInt32();
            newData.spell_id_end = fields[6].GetUInt32();

            if (newData.equipment_id && !sObjectMgr.GetEquipmentInfo(newData.equipment_id))
            {
                sLog.outErrorDb("Table `game_event_creature_data` have creature (Guid: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", guid, newData.equipment_id);
                newData.equipment_id = 0;
            }

            if (newData.entry_id && !ObjectMgr::GetCreatureTemplate(newData.entry_id))
            {
                sLog.outErrorDb("Table `game_event_creature_data` have creature (Guid: %u) with event time entry %u not found in table `creature_template`, set to no 0.", guid, newData.entry_id);
                newData.entry_id = 0;
            }

            if (newData.spell_id_start && !sSpellTemplate.LookupEntry<SpellEntry>(newData.spell_id_start))
            {
                sLog.outErrorDb("Table `game_event_creature_data` have creature (Guid: %u) with nonexistent spell_start %u, set to no start spell.", guid, newData.spell_id_start);
                newData.spell_id_start = 0;
            }

            if (newData.spell_id_end && !sSpellTemplate.LookupEntry<SpellEntry>(newData.spell_id_end))
            {
                sLog.outErrorDb("Table `game_event_creature_data` have creature (Guid: %u) with nonexistent spell_end %u, set to no end spell.", guid, newData.spell_id_end);
                newData.spell_id_end = 0;
            }

            equiplist.push_back(GameEventCreatureDataPair(guid, newData));
            mGameEventCreatureDataPerGuid.insert(GameEventCreatureDataPerGuidMap::value_type(guid, event_id));
        }
        while (result->NextRow());
        delete result;

        sLog.outString();
        sLog.outString(">> Loaded %u creature reactions at game events", count);
    }

    mGameEventQuests.resize(mGameEvent.size());

    result = WorldDatabase.Query("SELECT quest, event FROM game_event_quest");

    count = 0;
    if (!result)
    {
        BarGoLink bar(1);
        bar.step();

        sLog.outString();
        sLog.outString(">> Loaded %u quests additions in game events", count);
    }
    else
    {
        BarGoLink bar(result->GetRowCount());
        do
        {
            Field* fields = result->Fetch();

            bar.step();
            uint32 quest    = fields[0].GetUInt32();
            uint16 event_id = fields[1].GetUInt16();

            if (event_id == 0)
            {
                sLog.outErrorDb("`game_event_quest` game event id (%i) is reserved and can't be used.", event_id);
                continue;
            }

            if (!IsValidEvent(event_id))
            {
                sLog.outErrorDb("`game_event_quest` game event id (%u) not exist in `game_event`", event_id);
                continue;
            }

            const Quest* pQuest = sObjectMgr.GetQuestTemplate(quest);

            if (!pQuest)
            {
                sLog.outErrorDb("Table `game_event_quest` contain entry for quest %u (event %u) but this quest does not exist. Skipping.", quest, event_id);
                continue;
            }

            // disable any event specific quest (for cases where creature is spawned, but event not active).
            const_cast<Quest*>(pQuest)->SetQuestActiveState(false);

            ++count;

            QuestList& questlist = mGameEventQuests[event_id];
            questlist.push_back(quest);
        }
        while (result->NextRow());
        delete result;

        sLog.outString();
        sLog.outString(">> Loaded %u quest additions in game events", count);
    }

    mGameEventMails.resize(mGameEvent.size() * 2 - 1);

    result = WorldDatabase.Query("SELECT event, raceMask, quest, mailTemplateId, senderEntry FROM game_event_mail");

    count = 0;
    if (!result)
    {
        BarGoLink bar(1);
        bar.step();

        sLog.outString();
        sLog.outString(">> Loaded %u start/end game event mails", count);
    }
    else
    {
        BarGoLink bar(result->GetRowCount());
        do
        {
            Field* fields = result->Fetch();

            bar.step();
            uint16 event_id = fields[0].GetUInt16();

            GameEventMail mail;
            mail.raceMask       = fields[1].GetUInt32();
            mail.questId        = fields[2].GetUInt32();
            mail.mailTemplateId = fields[3].GetUInt32();
            mail.senderEntry    = fields[4].GetUInt32();

            if (event_id == 0)
            {
                sLog.outErrorDb("`game_event_mail` game event id (%i) not allowed", event_id);
                continue;
            }

            if (!IsValidEvent(event_id))
            {
                sLog.outErrorDb("`game_event_mail` game event id (%u) not exist in `game_event`", event_id);
                continue;
            }

            int32 internal_event_id = mGameEvent.size() + event_id - 1;

            if (!(mail.raceMask & RACEMASK_ALL_PLAYABLE))
            {
                sLog.outErrorDb("Table `game_event_mail` have raceMask (%u) requirement for game event %i that not include any player races, ignoring.", mail.raceMask, event_id);
                continue;
            }

            if (mail.questId && !sObjectMgr.GetQuestTemplate(mail.questId))
            {
                sLog.outErrorDb("Table `game_event_mail` have nonexistent quest (%u) requirement for game event %i, ignoring.", mail.questId, event_id);
                continue;
            }

            if (!sMailTemplateStore.LookupEntry(mail.mailTemplateId))
            {
                sLog.outErrorDb("Table `game_event_mail` have invalid mailTemplateId (%u) for game event %i that invalid not include any player races, ignoring.", mail.mailTemplateId, event_id);
                continue;
            }

            if (!ObjectMgr::GetCreatureTemplate(mail.senderEntry))
            {
                sLog.outErrorDb("Table `game_event_mail` have nonexistent sender creature entry (%u) for game event %i that invalid not include any player races, ignoring.", mail.senderEntry, event_id);
                continue;
            }

            ++count;

            MailList& maillist = mGameEventMails[internal_event_id];
            maillist.push_back(mail);
        }
        while (result->NextRow());
        delete result;

        sLog.outString();
        sLog.outString(">> Loaded %u start/end game event mails", count);
    }
}
	void BGASprite::Setup(float Time, Sprite& sprite)
	{
		auto T = &pivotTransform[mOrigin];

		if (!mParent) return; // We need this.
		if (mImageIndex == -1) // We haven't initialized from the parent's data yet? Alright.
		{
			assert(mParent != nullptr);
			mImageIndex = mParent->GetIndexFromFilename(mFile);
			mTransform.ChainTransformation(&mParent->GetTransformation());
		}

		sprite.ChainTransformation(T);
		T->ChainTransformation(&mTransform);

		// Now get the values for all the different stuff.
		// Set the image and reset size since we're using the pivot.
		// Also forces the matrix to be recalculated.
		sprite.SetImage(mParent->GetImageFromIndex(mImageIndex), false);
		sprite.SetSize(1, 1);

		if (sprite.GetImage())
		{
			auto i = sprite.GetImage();
			T->SetSize(i->w, i->h);
		}

		// Okay, a pretty long function follows. Fade first.
		auto fade_evt = GetEvent(Time, EVT_FADE);
		
		if (IsValidEvent(fade_evt, EVT_FADE))
			sprite.Alpha = event_cast<FadeEvent>(fade_evt)->LerpValue(Time);
		else sprite.Alpha = 0;

		// Now position.	
		auto movx_evt = GetEvent(Time, EVT_MOVEX);
		if (IsValidEvent(movx_evt, EVT_MOVEX))
			mTransform.SetPositionX(event_cast<MoveXEvent>(movx_evt)->LerpValue(Time));
		else mTransform.SetPositionX(mStartPos.x);

		auto movy_evt = GetEvent(Time, EVT_MOVEY);
		if (IsValidEvent(movy_evt, EVT_MOVEY))
			mTransform.SetPositionY(event_cast<MoveYEvent>(movy_evt)->LerpValue(Time));
		else mTransform.SetPositionY(mStartPos.y);

		// We already unpacked move events, so no need for this next snip.
		/* auto mov_evt = GetEvent(Time, EVT_MOVE);
		if (IsValidEvent(mov_evt, EVT_MOVE))
			mTransform.SetPosition(event_cast<MoveEvent>(mov_evt)->LerpValue(Time)); */

		// Now scale and rotation.
		auto scale_evt = GetEvent(Time, EVT_SCALE);
		if (IsValidEvent(scale_evt, EVT_SCALE))
			mTransform.SetScale(event_cast<ScaleEvent>(scale_evt)->LerpValue(Time));
		else mTransform.SetScale(1);

		// Since scale is just applied to size straight up, we can use this extra scale
		// defaulting at 1,1 to be our vector scale. That way they'll pile up.
		auto vscale_evt = GetEvent(Time, EVT_SCALEVEC);
		if (IsValidEvent(scale_evt, EVT_SCALEVEC))
			mTransform.SetSize(event_cast<VectorScaleEvent>(vscale_evt)->LerpValue(Time));
		else mTransform.SetSize(1, 1);

		auto rot_evt = GetEvent(Time, EVT_ROTATE);
		if (IsValidEvent(rot_evt, EVT_ROTATE))
			mTransform.SetRotation(glm::degrees(event_cast<RotateEvent>(rot_evt)->LerpValue(Time)));
		else mTransform.SetRotation(0);

		auto additive_evt = GetEvent(Time, EVT_ADDITIVE);
		if (IsValidEvent(additive_evt, EVT_ADDITIVE))
			sprite.SetBlendMode(BLEND_ADD);
		else sprite.SetBlendMode(BLEND_ALPHA);

		// Flip events are skipped for now.
	}