void InstanceScenario::SaveToDB()
{
    if (_criteriaProgress.empty())
        return;

    DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(_map->GetDifficultyID());
    if (!difficultyEntry || difficultyEntry->Flags & DIFFICULTY_FLAG_CHALLENGE_MODE) // Map should have some sort of "CanSave" boolean that returns whether or not the map is savable. (Challenge modes cannot be saved for example)
        return;

    uint32 id = _map->GetInstanceId();
    if (!id)
    {
        TC_LOG_DEBUG("scenario", "Scenario::SaveToDB: Can not save scenario progress without an instance save. Map::GetInstanceId() did not return an instance save.");
        return;
    }

    SQLTransaction trans = CharacterDatabase.BeginTransaction();
    for (auto iter = _criteriaProgress.begin(); iter != _criteriaProgress.end(); ++iter)
    {
        if (!iter->second.Changed)
            continue;

        Criteria const* criteria = sCriteriaMgr->GetCriteria(iter->first);
        switch (CriteriaTypes(criteria->Entry->Type))
        {
            // Blizzard only appears to store creature kills
            case CRITERIA_TYPE_KILL_CREATURE:
                break;
            default:
                continue;
        }

        if (iter->second.Counter)
        {
            PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_SCENARIO_INSTANCE_CRITERIA);
            stmt->setUInt32(0, id);
            stmt->setUInt32(1, iter->first);
            trans->Append(stmt);

            stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_SCENARIO_INSTANCE_CRITERIA);
            stmt->setUInt32(0, id);
            stmt->setUInt32(1, iter->first);
            stmt->setUInt64(2, iter->second.Counter);
            stmt->setUInt32(3, uint32(iter->second.Date));
            trans->Append(stmt);
        }

        iter->second.Changed = false;
    }

    CharacterDatabase.CommitTransaction(trans);
}
void QuestObjectiveCriteriaMgr::CheckAllQuestObjectiveCriteria(Player* referencePlayer)
{
    // suppress sending packets
    for (uint32 i = 0; i < CRITERIA_TYPE_TOTAL; ++i)
        UpdateCriteria(CriteriaTypes(i), 0, 0, 0, nullptr, referencePlayer);
}
void InstanceScenario::LoadInstanceData(uint32 instanceId)
{
    PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SCENARIO_INSTANCE_CRITERIA_FOR_INSTANCE);
    stmt->setUInt32(0, instanceId);

    PreparedQueryResult result = CharacterDatabase.Query(stmt);
    if (result)
    {
        SQLTransaction trans = CharacterDatabase.BeginTransaction();
        time_t now = time(nullptr);

        std::vector<CriteriaTree const*> criteriaTrees;
        do
        {
            Field* fields = result->Fetch();
            uint32 id = fields[0].GetUInt32();
            uint64 counter = fields[1].GetUInt64();
            time_t date = time_t(fields[2].GetUInt32());

            Criteria const* criteria = sCriteriaMgr->GetCriteria(id);
            if (!criteria)
            {
                // Removing non-existing criteria data for all instances
                TC_LOG_ERROR("criteria.instancescenarios", "Removing scenario criteria %u data from the table `instance_scenario_progress`.", id);

                stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_SCENARIO_INSTANCE_CRITERIA);
                stmt->setUInt32(0, instanceId);
                stmt->setUInt32(1, uint32(id));
                trans->Append(stmt);
                continue;
            }

            if (criteria->Entry->StartTimer && time_t(date + criteria->Entry->StartTimer) < now)
                continue;

            switch (CriteriaTypes(criteria->Entry->Type))
            {
                // Blizzard appears to only stores creatures killed progress for unknown reasons. Either technical shortcoming or intentional
                case CRITERIA_TYPE_KILL_CREATURE:
                    break;
                default:
                    continue;
            }

            SetCriteriaProgress(criteria, counter, nullptr, PROGRESS_SET);

            if (CriteriaTreeList const* trees = sCriteriaMgr->GetCriteriaTreesByCriteria(criteria->ID))
                for (CriteriaTree const* tree : *trees)
                    criteriaTrees.push_back(tree);
        }
        while (result->NextRow());

        CharacterDatabase.CommitTransaction(trans);

        for (CriteriaTree const* tree : criteriaTrees)
        {
            ScenarioStepEntry const* step = tree->ScenarioStep;
            if (!step)
                continue;

            if (IsCompletedCriteriaTree(tree))
                SetStepState(step, SCENARIO_STEP_DONE);
        }
    }
}
/**
* called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
*/
void AchievementMgr::CheckAllAchievementCriteria(Player* referencePlayer)
{
    // suppress sending packets
    for (uint32 i = 0; i < CRITERIA_TYPE_TOTAL; ++i)
        UpdateCriteria(CriteriaTypes(i), 0, 0, 0, NULL, referencePlayer);
}