aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/mangos.sql26
-rw-r--r--sql/updates/7627_01_mangos_achievement_criteria_data.sql10
-rw-r--r--src/game/AchievementMgr.cpp243
-rw-r--r--src/game/AchievementMgr.h77
-rw-r--r--src/game/World.cpp1
5 files changed, 277 insertions, 80 deletions
diff --git a/sql/mangos.sql b/sql/mangos.sql
index 83ac68521e4..4ae50613a6d 100644
--- a/sql/mangos.sql
+++ b/sql/mangos.sql
@@ -22,7 +22,7 @@
DROP TABLE IF EXISTS `db_version`;
CREATE TABLE `db_version` (
`version` varchar(120) default NULL,
- `required_7622_03_mangos_creature_ai_texts` bit(1) default NULL
+ `required_7627_01_mangos_achievement_criteria_data` bit(1) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes';
--
@@ -37,6 +37,28 @@ INSERT INTO `db_version` VALUES
UNLOCK TABLES;
--
+-- Table structure for table `achievement_criteria_data`
+--
+
+DROP TABLE IF EXISTS `achievement_criteria_data`;
+CREATE TABLE `achievement_criteria_data` (
+ `criteria_id` mediumint(8) NOT NULL,
+ `type` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `value1` mediumint(8) unsigned NOT NULL DEFAULT '0',
+ `value2` mediumint(8) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`criteria_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Achievment system';
+
+--
+-- Dumping data for table `achievement_criteria_data`
+--
+
+LOCK TABLES `achievement_criteria_data` WRITE;
+/*!40000 ALTER TABLE `achievement_criteria_data` DISABLE KEYS */;
+/*!40000 ALTER TABLE `achievement_criteria_data` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
-- Table structure for table `achievement_reward`
--
@@ -50,7 +72,7 @@ CREATE TABLE `achievement_reward` (
`subject` varchar(255) default NULL,
`text` text,
PRIMARY KEY (`entry`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System';
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Achievment system';
--
-- Dumping data for table `achievement_reward`
diff --git a/sql/updates/7627_01_mangos_achievement_criteria_data.sql b/sql/updates/7627_01_mangos_achievement_criteria_data.sql
new file mode 100644
index 00000000000..29a095c5dd5
--- /dev/null
+++ b/sql/updates/7627_01_mangos_achievement_criteria_data.sql
@@ -0,0 +1,10 @@
+ALTER TABLE db_version CHANGE COLUMN required_7622_03_mangos_creature_ai_texts required_7627_01_mangos_achievement_criteria_data bit;
+
+DROP TABLE IF EXISTS `achievement_criteria_data`;
+CREATE TABLE `achievement_criteria_data` (
+ `criteria_id` mediumint(8) NOT NULL,
+ `type` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `value1` mediumint(8) unsigned NOT NULL DEFAULT '0',
+ `value2` mediumint(8) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`criteria_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Achievment system';
diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp
index da192844a70..c76a6b223c6 100644
--- a/src/game/AchievementMgr.cpp
+++ b/src/game/AchievementMgr.cpp
@@ -37,56 +37,6 @@
INSTANTIATE_SINGLETON_1(AchievementGlobalMgr);
-const CriteriaCastSpellRequirement AchievementGlobalMgr::m_criteriaCastSpellRequirements[CRITERIA_CAST_SPELL_REQ_COUNT] =
- {
- {5272, 3057, 0, 0},
- {5273, 2784, 0, 0},
- {5752, 9099, 0, 0},
- {5753, 8403, 0, 0},
- {5772, 0, 0, RACE_GNOME},
- {5774, 0, 0, RACE_BLOODELF},
- {5775, 0, 0, RACE_DRAENEI},
- {5776, 0, 0, RACE_DWARF},
- {5777, 0, 0, RACE_HUMAN},
- {5778, 0, 0, RACE_NIGHTELF},
- {5779, 0, 0, RACE_ORC},
- {5780, 0, 0, RACE_TAUREN},
- {5781, 0, 0, RACE_TROLL},
- {5782, 0, 0, RACE_UNDEAD_PLAYER},
- {6225, 5661, 0, 0},
- {6226, 26044, 0, 0},
- {6228, 739, 0, 0},
- {6229, 927, 0, 0},
- {6230, 1444, 0, 0},
- {6231, 8140, 0, 0},
- {6232, 5489, 0, 0},
- {6233,12336, 0, 0},
- {6234, 1351, 0, 0},
- {6235, 5484, 0, 0},
- {6236, 1182, 0, 0},
- {6237, 0, CLASS_DEATH_KNIGHT, RACE_ORC},
- {6238, 0, CLASS_WARRIOR, RACE_HUMAN},
- {6239, 0, CLASS_SHAMAN, RACE_TAUREN},
- {6240, 0, CLASS_DRUID, RACE_NIGHTELF},
- {6241, 0, CLASS_ROGUE, RACE_UNDEAD_PLAYER},
- {6242, 0, CLASS_HUNTER, RACE_TROLL},
- {6243, 0, CLASS_MAGE, RACE_GNOME},
- {6244, 0, CLASS_PALADIN, RACE_DWARF},
- {6245, 0, CLASS_WARLOCK, RACE_BLOODELF},
- {6246, 0, CLASS_PRIEST, RACE_DRAENEI},
- {6312, 0, CLASS_WARLOCK, RACE_GNOME},
- {6313, 0, CLASS_DEATH_KNIGHT, RACE_HUMAN},
- {6314, 0, CLASS_PRIEST, RACE_NIGHTELF},
- {6315, 0, CLASS_SHAMAN, RACE_ORC},
- {6316, 0, CLASS_DRUID, RACE_TAUREN},
- {6317, 0, CLASS_ROGUE, RACE_TROLL},
- {6318, 0, CLASS_WARRIOR, RACE_UNDEAD_PLAYER},
- {6319, 0, CLASS_MAGE, RACE_BLOODELF},
- {6320, 0, CLASS_PALADIN, RACE_DRAENEI},
- {6321, 0, CLASS_HUNTER, RACE_DWARF},
- {6662, 31261, 0, 0}
- };
-
namespace MaNGOS
{
class AchievementChatBuilder
@@ -117,6 +67,114 @@ namespace MaNGOS
};
} // namespace MaNGOS
+
+bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
+{
+ if(dataType >= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE)
+ {
+ sLog.outErrorDb( "Table `achievement_criteria_data` for criteria (Entry: %u) have wrong data type (%u), ignore.", criteria->ID,dataType);
+ return false;
+ }
+
+ switch(criteria->requiredType)
+ {
+ case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
+ switch(dataType)
+ {
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE:
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE:
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH:
+ break;
+ default:
+ sLog.outErrorDb( "Table `achievement_criteria_data` for criteria (Entry: %u Type: %u) have wrong data type (%u), ignore.", criteria->ID, criteria->requiredType,dataType);
+ return false;
+ }
+ break;
+ default:
+ sLog.outErrorDb( "Table `achievement_criteria_data` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria->ID, criteria->requiredType);
+ return false;
+ }
+
+ switch(dataType)
+ {
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
+ return true;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE:
+ if(!creature.id || !objmgr.GetCreatureTemplate(creature.id))
+ {
+ sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) have not existed creature id in value1 (%u), ignore.",
+ criteria->ID, criteria->requiredType,dataType,creature.id);
+ return false;
+ }
+ return true;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE:
+ if(!classRace.class_id && !classRace.race_id)
+ {
+ sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE (%u) must have not 0 in one from value fields, ignore.",
+ criteria->ID, criteria->requiredType,dataType);
+ return false;
+ }
+ if(classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE)==0)
+ {
+ sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) have not existed class in value1 (%u), ignore.",
+ criteria->ID, criteria->requiredType,dataType,classRace.class_id);
+ return false;
+ }
+ if(classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE)==0)
+ {
+ sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) have not existed race in value2 (%u), ignore.",
+ criteria->ID, criteria->requiredType,dataType,classRace.race_id);
+ return false;
+ }
+ return true;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH:
+ if(health.percent < 1 || health.percent > 100)
+ {
+ sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH (%u) have prong percent value in value1 (%u), ignore.",
+ criteria->ID, criteria->requiredType,dataType,health.percent);
+ return false;
+ }
+ return true;
+ default:
+ sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) have data for not supported data type (%u), ignore.", criteria->ID, criteria->requiredType,dataType);
+ return false;
+ }
+ return false;
+}
+
+bool AchievementCriteriaData::Meets( Unit const* target ) const
+{
+ if (!target)
+ return false;
+
+ switch(dataType)
+ {
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
+ return true;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE:
+ if (target->GetTypeId()!=TYPEID_UNIT)
+ return false;
+ if (target->GetEntry() != creature.id)
+ return false;
+ return true;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE:
+ if (target->GetTypeId()!=TYPEID_PLAYER)
+ return false;
+ if(classRace.class_id && classRace.class_id != ((Player*)target)->getClass())
+ return false;
+ if(classRace.race_id && classRace.race_id != ((Player*)target)->getRace())
+ return false;
+ return true;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH:
+ if (target->GetTypeId()!=TYPEID_PLAYER)
+ return false;
+ return target->GetHealth()*100 <= health.percent*target->GetMaxHealth();
+ }
+
+ return false;
+}
+
AchievementMgr::AchievementMgr(Player *player)
{
m_player = player;
@@ -884,24 +942,19 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
break;
case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
{
+ if (!miscvalue1)
+ continue;
+
if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
continue;
// those requirements couldn't be found in the dbc
- if (CriteriaCastSpellRequirement const* requirement = AchievementGlobalMgr::GetCriteriaCastSpellRequirement(achievementCriteria))
- {
- if (!unit)
- continue;
-
- if (requirement->creatureEntry && unit->GetEntry() != requirement->creatureEntry)
- continue;
-
- if (requirement->playerRace && (unit->GetTypeId() != TYPEID_PLAYER || unit->getRace()!=requirement->playerRace))
- continue;
+ AchievementCriteriaData const* data = achievementmgr.GetCriteriaData(achievementCriteria);
+ if(!data)
+ continue;
- if (requirement->playerClass && (unit->GetTypeId() != TYPEID_PLAYER || unit->getClass()!=requirement->playerClass))
- continue;
- }
+ if(!data->Meets(unit))
+ continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
@@ -1388,6 +1441,70 @@ void AchievementGlobalMgr::LoadAchievementReferenceList()
sLog.outString(">> Loaded %u achievement references.",count);
}
+void AchievementGlobalMgr::LoadAchievementCriteriaData()
+{
+ QueryResult *result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_data");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_data` is empty.");
+ return;
+ }
+
+ uint32 count = 0;
+ barGoLink bar(result->GetRowCount());
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+ uint32 criteria_id = fields[0].GetUInt32();
+
+ AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id);
+
+ if (!criteria)
+ {
+ sLog.outErrorDb( "Table `achievement_criteria_data` have data for not existed criteria (Entry: %u), ignore.", criteria_id);
+ continue;
+ }
+
+ AchievementCriteriaData data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32());
+
+ if(!data.IsValid(criteria))
+ continue;
+
+ m_criteriaDataMap[criteria_id] = data;
+ ++count;
+ } while(result->NextRow());
+
+ delete result;
+
+ // post loading checks
+ for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
+ {
+ AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
+ if(!criteria)
+ continue;
+
+ switch(criteria->requiredType)
+ {
+ case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
+ if(!GetCriteriaData(criteria))
+ sLog.outErrorDb( "Table `achievement_criteria_data` not have expected data for for criteria (Entry: %u Type: %u).", criteria->ID, criteria->requiredType);
+ break;
+ default: // unexpected case processed in IsValid check
+ break;
+ }
+
+ }
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u additional achievement criteria data.",count);
+}
+
void AchievementGlobalMgr::LoadCompletedAchievements()
{
QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
diff --git a/src/game/AchievementMgr.h b/src/game/AchievementMgr.h
index b45c63f7301..faabaf011ef 100644
--- a/src/game/AchievementMgr.h
+++ b/src/game/AchievementMgr.h
@@ -27,9 +27,6 @@
#include <map>
#include <string>
-#define CRITERIA_CAST_SPELL_REQ_COUNT 46
-#define ACHIEVEMENT_REWARD_COUNT 57
-
typedef std::list<AchievementCriteriaEntry const*> AchievementCriteriaEntryList;
typedef std::list<AchievementEntry const*> AchievementEntryList;
@@ -43,14 +40,66 @@ struct CriteriaProgress
bool changed;
};
-struct CriteriaCastSpellRequirement
+enum AchievementCriteriaDataType
+{ // value1 value2 for the Condition enumed
+ ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE = 0, // 0 0
+ ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE = 1, // creature_id
+ ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE = 2, // class_id race_id
+ ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH = 3, // health_percent
+};
+
+#define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 4 // maximum value in AchievementCriteriaDataType enum
+
+class Unit;
+
+struct AchievementCriteriaData
{
- uint32 achievementCriteriaId;
- uint32 creatureEntry;
- uint8 playerClass;
- uint8 playerRace;
+ AchievementCriteriaDataType dataType;
+ union
+ {
+ // ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE
+ struct
+ {
+ uint32 id;
+ } creature;
+ // ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE
+ struct
+ {
+ uint32 class_id;
+ uint32 race_id;
+ } classRace;
+ // ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH
+ struct
+ {
+ uint32 percent;
+ } health;
+ // ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE
+ struct
+ {
+ uint32 value1;
+ uint32 value2;
+ } raw;
+ };
+
+ AchievementCriteriaData() : dataType(ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE)
+ {
+ raw.value1 = 0;
+ raw.value2 = 0;
+ }
+
+ AchievementCriteriaData(uint32 _dataType, uint32 _value1, uint32 _value2) : dataType(AchievementCriteriaDataType(_dataType))
+ {
+ raw.value1 = _value1;
+ raw.value2 = _value2;
+ }
+
+ bool IsValid(AchievementCriteriaEntry const* criteria);
+ // Checks correctness of values
+ bool Meets(Unit const* target) const;// Checks if the target meets the requirement
};
+typedef std::map<uint32,AchievementCriteriaData> AchievementCriteriaDataMap;
+
struct AchievementReward
{
uint32 titleId[2];
@@ -145,13 +194,10 @@ class AchievementGlobalMgr
return iter!=m_achievementRewardLocales.end() ? &iter->second : NULL;
}
- static CriteriaCastSpellRequirement const * GetCriteriaCastSpellRequirement(AchievementCriteriaEntry const *achievementCriteria)
+ AchievementCriteriaData const* GetCriteriaData(AchievementCriteriaEntry const *achievementCriteria)
{
- for (uint32 i=0; i < CRITERIA_CAST_SPELL_REQ_COUNT; ++i)
- if (m_criteriaCastSpellRequirements[i].achievementCriteriaId == achievementCriteria->ID)
- return &m_criteriaCastSpellRequirements[i];
-
- return NULL;
+ AchievementCriteriaDataMap::const_iterator iter = m_criteriaDataMap.find(achievementCriteria->ID);
+ return iter!=m_criteriaDataMap.end() ? &iter->second : NULL;
}
bool IsRealmCompleted(AchievementEntry const* achievement) const
@@ -165,12 +211,13 @@ class AchievementGlobalMgr
}
void LoadAchievementCriteriaList();
+ void LoadAchievementCriteriaData();
void LoadAchievementReferenceList();
void LoadCompletedAchievements();
void LoadRewards();
void LoadRewardLocales();
private:
- static const CriteriaCastSpellRequirement m_criteriaCastSpellRequirements[];
+ AchievementCriteriaDataMap m_criteriaDataMap;
// store achievement criterias by type to speed up lookup
AchievementCriteriaEntryList m_AchievementCriteriasByType[ACHIEVEMENT_CRITERIA_TYPE_TOTAL];
diff --git a/src/game/World.cpp b/src/game/World.cpp
index 4bf272a21c8..a8dbff76114 100644
--- a/src/game/World.cpp
+++ b/src/game/World.cpp
@@ -1372,6 +1372,7 @@ void World::SetInitialWorldSettings()
sLog.outString();
achievementmgr.LoadAchievementReferenceList();
achievementmgr.LoadAchievementCriteriaList();
+ achievementmgr.LoadAchievementCriteriaData();
achievementmgr.LoadRewards();
achievementmgr.LoadRewardLocales();
achievementmgr.LoadCompletedAchievements();