diff --git a/sql/mangos.sql b/sql/mangos.sql index c3ee5fcaf..a0460f734 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -24,7 +24,7 @@ CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, `cache_id` int(10) default '0', - `required_10331_02_mangos_command` bit(1) default NULL + `required_10342_02_mangos_command` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -497,6 +497,11 @@ INSERT INTO `command` VALUES ('account set addon',3,'Syntax: .account set addon [#accountId|$accountName] #addon\r\n\r\nSet user (possible targeted) expansion addon level allowed. Addon values: 0 - normal, 1 - tbc, 2 - wotlk.'), ('account set gmlevel',4,'Syntax: .account set gmlevel [#accountId|$accountName] #level\r\n\r\nSet the security level for targeted player (can''t be used at self) or for #accountId or $accountName to a level of #level.\r\n\r\n#level may range from 0 to 3.'), ('account set password',4,'Syntax: .account set password (#accountId|$accountName) $password $password\r\n\r\nSet password for account.'), +('achievement',3,'Syntax: .achievement $playername #achivementid\r\n\r\nShow state achievment #achivmentid (can be shift link) and list of achievement criteria with progress data for selected player in game or by player name.'), +('achievement add',3,'Syntax: .achievement add $playername #achivementid\r\n\r\nComplete achievement and all it\'s criteria for selected player in game or by player name. Command can\'t be used for counter achievements.'), +('achievement remove',3,'Syntax: .achievement remove $playername #achivementid\r\n\r\nRemove complete state for achievement #achivmentid and reset all achievement\'s criteria for selected player in game or by player name. Also command can be used for reset counter achievements.'), +('achievement criteria add',3,'Syntax: .achievement criteria add $playername #criteriaid #change\r\n\r\nIncrease progress for non-completed criteria at #change for selected player in game or by player name. If #chnage not provided then non-counter criteria progress set to completed state. For counter criteria increased at 1.'), +('achievement criteria remove',3,'Syntax: .achievement criteria remove $playername #criteriaid #change\r\n\r\necrease progress for criteria at #change for selected player in game or by player name. If #chnage not provided then criteria progress reset to 0.'), ('additem',3,'Syntax: .additem #itemid/[#itemname]/#shift-click-item-link #itemcount\r\n\r\nAdds the specified number of items of id #itemid (or exact (!) name $itemname in brackets, or link created by shift-click at item in inventory or recipe) to your or selected character inventory. If #itemcount is omitted, only one item will be added.\r\n.'), ('additemset',3,'Syntax: .additemset #itemsetid\r\n\r\nAdd items from itemset of id #itemsetid to your or selected character inventory. Will add by one example each item from itemset.'), ('announce',1,'Syntax: .announce $MessageToBroadcast\r\n\r\nSend a global message to all players online in chat log.'), @@ -3728,6 +3733,10 @@ INSERT INTO `mangos_string` VALUES (1158,'You modify for %s hex field:%u %s %x = %x (hex)',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1159,'Modify %s float field:%u to sum with:%f = %f',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1160,'You modify for %s float field:%u to sum with:%f = %f',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1161,'Criteria:',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1162,' [counter]',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1163,'Achievement %u doesn\'t exist.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1164,'Achievement criteria %u doesn\'t exist.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1200,'You try to view cinemitic %u but it doesn\'t exist.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1201,'You try to view movie %u but it doesn\'t exist.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); /*!40000 ALTER TABLE `mangos_string` ENABLE KEYS */; diff --git a/sql/updates/10342_01_mangos_mangos_string.sql b/sql/updates/10342_01_mangos_mangos_string.sql new file mode 100644 index 000000000..cc8fa4487 --- /dev/null +++ b/sql/updates/10342_01_mangos_mangos_string.sql @@ -0,0 +1,9 @@ +ALTER TABLE db_version CHANGE COLUMN required_10331_02_mangos_command required_10342_01_mangos_mangos_string bit; + +DELETE FROM mangos_string WHERE entry IN (1161,1162,1163,1164); + +INSERT INTO mangos_string VALUES +(1161,'Criteria:',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1162,' [counter]',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1163,'Achievement %u doesn\'t exist.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1164,'Achievement criteria %u doesn\'t exist.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); diff --git a/sql/updates/10342_02_mangos_command.sql b/sql/updates/10342_02_mangos_command.sql new file mode 100644 index 000000000..871dae7a1 --- /dev/null +++ b/sql/updates/10342_02_mangos_command.sql @@ -0,0 +1,13 @@ +ALTER TABLE db_version CHANGE COLUMN required_10342_01_mangos_mangos_string required_10342_02_mangos_command bit; + +DELETE FROM command WHERE name IN ( + 'achievement','achievement add','achievement remove','achievement criteria add','achievement criteria remove' +); + +INSERT INTO command (name, security, help) VALUES +('achievement',3,'Syntax: .achievement $playername #achivementid\r\n\r\nShow state achievment #achivmentid (can be shift link) and list of achievement criteria with progress data for selected player in game or by player name.'), +('achievement add',3,'Syntax: .achievement add $playername #achivementid\r\n\r\nComplete achievement and all it\'s criteria for selected player in game or by player name. Command can\'t be used for counter achievements.'), +('achievement remove',3,'Syntax: .achievement remove $playername #achivementid\r\n\r\nRemove complete state for achievement #achivmentid and reset all achievement\'s criteria for selected player in game or by player name. Also command can be used for reset counter achievements.'), +('achievement criteria add',3,'Syntax: .achievement criteria add $playername #criteriaid #change\r\n\r\nIncrease progress for non-completed criteria at #change for selected player in game or by player name. If #chnage not provided then non-counter criteria progress set to completed state. For counter criteria increased at 1.'), +('achievement criteria remove',3,'Syntax: .achievement criteria remove $playername #criteriaid #change\r\n\r\necrease progress for criteria at #change for selected player in game or by player name. If #chnage not provided then criteria progress reset to 0.'); + diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index dfb81f1a1..e9b625b8e 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -74,6 +74,8 @@ pkgdata_DATA = \ 10331_02_mangos_command.sql \ 10332_01_characters_character_aura.sql \ 10332_02_characters_pet_aura.sql \ + 10342_01_mangos_mangos_string.sql \ + 10342_02_mangos_command.sql \ README ## Additional files to include when running 'make dist' @@ -128,4 +130,6 @@ EXTRA_DIST = \ 10331_02_mangos_command.sql \ 10332_01_characters_character_aura.sql \ 10332_02_characters_pet_aura.sql \ + 10342_01_mangos_mangos_string.sql \ + 10342_02_mangos_command.sql \ README diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index c03f7e446..4a0c19f06 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -430,12 +430,12 @@ void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uin case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: // have total statistic also not expected to be reset if (achievementCriteria->healing_done.flag == miscvalue1 && achievementCriteria->healing_done.mapid == miscvalue2) - SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET); + SetCriteriaProgress(achievementCriteria, achievement, 0, PROGRESS_SET); break; case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // have total statistic also not expected to be reset // reset only the criteria having the miscvalue1 condition if (achievementCriteria->win_rated_arena.flag == miscvalue1) - SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET); + SetCriteriaProgress(achievementCriteria, achievement, 0, PROGRESS_SET); break; default: // reset all cases break; @@ -690,6 +690,9 @@ static const uint32 achievIdForDangeon[][4] = { 0, false, false, false } }; +static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 }; +static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 }; + /** * this function will be called whenever the user might have done a criteria relevant action */ @@ -720,6 +723,10 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if (IsCompletedCriteria(achievementCriteria,achievement)) continue; + // init values, real set in switch + uint32 change = 0; + ProgressType progressType = PROGRESS_SET; + switch (type) { // std. case: increment at 1 @@ -736,7 +743,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case if(!miscvalue1) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; // std case: increment at miscvalue1 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: @@ -752,7 +760,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case if(!miscvalue1) continue; - SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); + change = miscvalue1; + progressType = PROGRESS_ACCUMULATE; break; // std case: high value at miscvalue1 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: @@ -764,7 +773,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case if(!miscvalue1) continue; - SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST); + change = miscvalue1; + progressType = PROGRESS_HIGHEST; break; // specialized cases @@ -814,7 +824,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui } } - SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); + change = miscvalue1; + progressType = PROGRESS_ACCUMULATE; break; } case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: @@ -830,37 +841,79 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(!data || !data->Meets(GetPlayer(),unit)) continue; - SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); + change = miscvalue2; + progressType = PROGRESS_ACCUMULATE; break; } case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: - SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel()); + { + bool ok = true; + + // skip wrong class achievements + for(uint8 i = 1; i < MAX_CLASSES; ++i) + { + if (achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass()) + { + ok = false; + break; + } + } + + if (!ok) + continue; + + // skip wrong race achievements + for(uint8 i = 1; i < MAX_RACES; ++i) + { + if (achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace()) + { + ok = false; + break; + } + } + + if (!ok) + continue; + + change = GetPlayer()->getLevel(); + progressType = PROGRESS_SET; break; + } case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: + { // update at loading or specific skill update if(miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID) continue; - if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID)) - SetCriteriaProgress(achievementCriteria, skillvalue); + change = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID); + progressType = PROGRESS_HIGHEST; break; + } case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: + { // update at loading or specific skill update if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID) continue; - if(uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID)) - SetCriteriaProgress(achievementCriteria, maxSkillvalue); + change = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID); + progressType = PROGRESS_HIGHEST; break; + } case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: - if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end()) - SetCriteriaProgress(achievementCriteria, 1); + { + if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) == m_completedAchievements.end()) + continue; + + change = 1; + progressType = PROGRESS_SET; break; + } case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: { uint32 counter =0; for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); ++itr) if(itr->second.m_rewarded) counter++; - SetCriteriaProgress(achievementCriteria, counter); + change = counter; + progressType = PROGRESS_SET; break; } case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: @@ -876,7 +929,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(itr->second.m_rewarded && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID) counter++; } - SetCriteriaProgress(achievementCriteria, counter); + change = counter; + progressType = PROGRESS_SET; break; } case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: @@ -885,7 +939,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID) continue; - SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); + change = miscvalue1; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case @@ -893,7 +948,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_DEATH: { @@ -916,7 +972,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(notfit) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; } case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: @@ -964,7 +1021,9 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui //FIXME: work only for instances where max==min for players if (map->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + + change = 1; + progressType = PROGRESS_ACCUMULATE; break; } @@ -974,7 +1033,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case @@ -985,7 +1045,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam()) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: { @@ -999,7 +1060,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; // miscvalue1 is the ingame fallheight*100 as stored in dbc - SetCriteriaProgress(achievementCriteria, miscvalue1); + change = miscvalue1; + progressType = PROGRESS_SET; break; } case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: @@ -1008,7 +1070,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if(miscvalue2 != achievementCriteria->death_from.type) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: { @@ -1046,7 +1109,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui } - SetCriteriaProgress(achievementCriteria, 1); + change = 1; + progressType = PROGRESS_SET; break; } case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: @@ -1063,7 +1127,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(!data->Meets(GetPlayer(),unit)) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; } case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: @@ -1080,15 +1145,19 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(!data->Meets(GetPlayer(),unit)) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; } case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID) continue; - if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID)) - SetCriteriaProgress(achievementCriteria, 1); + if(!GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID)) + continue; + + change = 1; + progressType = PROGRESS_SET; break; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: { @@ -1108,14 +1177,16 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; } - SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); + change = miscvalue2; + progressType = PROGRESS_ACCUMULATE; break; } case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: // speedup for non-login case if(miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1) continue; - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true)); + change = GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true); + progressType = PROGRESS_SET; break; case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // miscvalue1 contains the personal rating @@ -1130,12 +1201,13 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(!data || !data->Meets(GetPlayer(),unit,miscvalue1)) { // reset the progress as we have a win without the requirement. - SetCriteriaProgress(achievementCriteria, 0); + SetCriteriaProgress(achievementCriteria, achievement, 0); continue; } } - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case @@ -1143,7 +1215,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if(achievementCriteria->use_item.itemID != miscvalue1) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: // You _have_ to loot that item, just owning it when logging in does _not_ count! @@ -1151,7 +1224,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if(miscvalue1 != achievementCriteria->own_item.itemID) continue; - SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); + change = miscvalue2; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: { @@ -1180,12 +1254,16 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui } } - if(matchFound) - SetCriteriaProgress(achievementCriteria, 1); + if(!matchFound) + continue; + + change = 1; + progressType = PROGRESS_SET; break; } case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount()); + change = GetPlayer()->GetBankBagSlotCount(); + progressType = PROGRESS_SET; break; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: { @@ -1194,13 +1272,17 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID); - if (reputation > 0) - SetCriteriaProgress(achievementCriteria, reputation); + if (!reputation) + continue; + + change = reputation; + progressType = PROGRESS_SET; break; } case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: { - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount()); + change = GetPlayer()->GetReputationMgr().GetExaltedFactionCount(); + progressType = PROGRESS_SET; break; } case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: @@ -1208,7 +1290,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // skip for login case if(!miscvalue1) continue; - SetCriteriaProgress(achievementCriteria, 1); + change = 1; + progressType = PROGRESS_SET; break; } case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: @@ -1223,7 +1306,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria); if(!data || !data->Meets(GetPlayer(),unit,item_slot)) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_SET); + change = 1; + progressType = PROGRESS_SET; break; } case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: @@ -1243,7 +1327,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(!pProto || pProto->ItemLevel equip_item.itemID) continue; - SetCriteriaProgress(achievementCriteria, 1); + change = 1; + progressType = PROGRESS_SET; break; case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: // miscvalue1 = go entry @@ -1299,7 +1387,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if (miscvalue1 != achievementCriteria->use_gameobject.goEntry) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: { @@ -1315,7 +1404,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if (!data->Meets(GetPlayer(),unit)) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; } case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: @@ -1324,7 +1414,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: { @@ -1343,7 +1434,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui spellCount++; } } - SetCriteriaProgress(achievementCriteria, spellCount); + change = spellCount; + progressType = PROGRESS_SET; break; } case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: @@ -1362,16 +1454,20 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; } - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount()); + change = GetPlayer()->GetReputationMgr().GetReveredFactionCount(); + progressType = PROGRESS_SET; break; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount()); + change = GetPlayer()->GetReputationMgr().GetHonoredFactionCount(); + progressType = PROGRESS_SET; break; case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount()); + change = GetPlayer()->GetReputationMgr().GetVisibleFactionCount(); + progressType = PROGRESS_SET; break; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: @@ -1382,7 +1478,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui ItemPrototype const* proto = ObjectMgr::GetItemPrototype(miscvalue1); if (!proto || proto->Quality < ITEM_QUALITY_EPIC) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; } case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: @@ -1400,26 +1497,31 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if (skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine) spellCount++; } - SetCriteriaProgress(achievementCriteria, spellCount); + change = spellCount; + progressType = PROGRESS_SET; break; } case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS)); + change = GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS); + progressType = PROGRESS_SET; break; case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID) continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + change = 1; + progressType = PROGRESS_ACCUMULATE; break; case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST); + change = GetPlayer()->GetMoney(); + progressType = PROGRESS_HIGHEST; break; // std case: not exist in DBC, not triggered in code as result case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: @@ -1447,147 +1549,99 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS: break; // Not implemented yet :( } - if(IsCompletedCriteria(achievementCriteria,achievement)) - CompletedCriteriaFor(achievement); - // check again the completeness for SUMM and REQ COUNT achievements, - // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria - if (achievement->flags & ACHIEVEMENT_FLAG_SUMM) - { - if (IsCompletedAchievement(achievement)) - CompletedAchievement(achievement); - } - - if(AchievementEntryList const* achRefList = sAchievementMgr.GetAchievementByReferencedId(achievement->ID)) - { - for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr) - if(IsCompletedAchievement(*itr)) - CompletedAchievement(*itr); - } + SetCriteriaProgress(achievementCriteria, achievement, change, progressType); } } -static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 }; -static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 }; - -bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement) const +uint32 AchievementMgr::GetCriteriaProgressMaxCounter(AchievementCriteriaEntry const* achievementCriteria) { - // counter can never complete - if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER) - return false; - - if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) - { - // someone on this realm has already completed that achievement - if(sAchievementMgr.IsRealmCompleted(achievement)) - return false; - } - - CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID); - if(itr == m_criteriaProgress.end()) - return false; - - CriteriaProgress const* progress = &itr->second; - switch(achievementCriteria->requiredType) { case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - return progress->counter >= achievementCriteria->win_bg.winCount; + return achievementCriteria->win_bg.winCount; case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - return progress->counter >= achievementCriteria->kill_creature.creatureCount; + return achievementCriteria->kill_creature.creatureCount; case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: - { - // skip wrong class achievements - for(int i = 1; i < MAX_CLASSES; ++i) - if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass()) - return false; - - // skip wrong race achievements - for(int i = 1; i < MAX_RACES; ++i) - if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace()) - return false; - - // appropriate class/race or not class/race specific - return progress->counter >= achievementCriteria->reach_level.level; - } + return achievementCriteria->reach_level.level; case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: - return progress->counter >= achievementCriteria->reach_skill_level.skillLevel; + return achievementCriteria->reach_skill_level.skillLevel; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: - return progress->counter >= 1; + return 1; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: - return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount; + return achievementCriteria->complete_quest_count.totalQuestCount; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: - return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount; + return achievementCriteria->complete_quests_in_zone.questCount; case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: - return progress->counter >= achievementCriteria->healing_done.count; + return achievementCriteria->healing_done.count; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: - return progress->counter >= achievementCriteria->complete_daily_quest.questCount; + return achievementCriteria->complete_daily_quest.questCount; case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: - return progress->counter >= achievementCriteria->fall_without_dying.fallHeight; + return achievementCriteria->fall_without_dying.fallHeight; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: - return progress->counter >= 1; + return 1; case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - return progress->counter >= achievementCriteria->be_spell_target.spellCount; + return achievementCriteria->be_spell_target.spellCount; case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: - return progress->counter >= achievementCriteria->cast_spell.castCount; + return achievementCriteria->cast_spell.castCount; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - return progress->counter >= 1; + return 1; case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: - return progress->counter >= achievementCriteria->own_item.itemCount; + return achievementCriteria->own_item.itemCount; case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: - return progress->counter >= achievementCriteria->win_rated_arena.count; + return achievementCriteria->win_rated_arena.count; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: - return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75); + return achievementCriteria->learn_skill_level.skillLevel * 75; case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: - return progress->counter >= achievementCriteria->use_item.itemCount; + return achievementCriteria->use_item.itemCount; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: - return progress->counter >= achievementCriteria->loot_item.itemCount; + return achievementCriteria->loot_item.itemCount; case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: - return progress->counter >= 1; + return 1; case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: - return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots; + return achievementCriteria->buy_bank_slot.numberOfSlots; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: - return progress->counter >= achievementCriteria->gain_reputation.reputationAmount; + return achievementCriteria->gain_reputation.reputationAmount; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: - return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions; + return achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions; case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: - return progress->counter >= achievementCriteria->visit_barber.numberOfVisits; + return achievementCriteria->visit_barber.numberOfVisits; case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: - return progress->counter >= achievementCriteria->equip_epic_item.count; + return achievementCriteria->equip_epic_item.count; case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - return progress->counter >= achievementCriteria->roll_greed_on_loot.count; + return achievementCriteria->roll_greed_on_loot.count; case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: - return progress->counter >= achievementCriteria->hk_class.count; + return achievementCriteria->hk_class.count; case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: - return progress->counter >= achievementCriteria->hk_race.count; + return achievementCriteria->hk_race.count; case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: - return progress->counter >= achievementCriteria->do_emote.count; + return achievementCriteria->do_emote.count; case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: - return progress->counter >= achievementCriteria->equip_item.count; + return achievementCriteria->equip_item.count; case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: - return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper; + return achievementCriteria->quest_reward_money.goldInCopper; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: - return progress->counter >= achievementCriteria->loot_money.goldInCopper; + return achievementCriteria->loot_money.goldInCopper; case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: - return progress->counter >= achievementCriteria->use_gameobject.useCount; + return achievementCriteria->use_gameobject.useCount; case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: - return progress->counter >= achievementCriteria->special_pvp_kill.killCount; + return achievementCriteria->special_pvp_kill.killCount; case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: - return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount; + return achievementCriteria->fish_in_gameobject.lootCount; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: - return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount; + return achievementCriteria->learn_skillline_spell.spellCount; case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: - return progress->counter >= achievementCriteria->win_duel.duelCount; + return achievementCriteria->win_duel.duelCount; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: - return progress->counter >= achievementCriteria->loot_type.lootTypeCount; + return achievementCriteria->loot_type.lootTypeCount; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: - return progress->counter >= achievementCriteria->learn_skill_line.spellCount; + return achievementCriteria->learn_skill_line.spellCount; case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: - return progress->counter >= achievementCriteria->honorable_kill.killCount; + return achievementCriteria->honorable_kill.killCount; // handle all statistic-only criteria here case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: @@ -1622,9 +1676,38 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: + return 0; + } + + return 0; +} + +bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement) const +{ + // counter can never complete + if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER) + return false; + + if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + { + // someone on this realm has already completed that achievement + if(sAchievementMgr.IsRealmCompleted(achievement)) return false; } - return false; + + CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID); + if(itr == m_criteriaProgress.end()) + return false; + + CriteriaProgress const* progress = &itr->second; + + uint32 maxcounter = GetCriteriaProgressMaxCounter(achievementCriteria); + + // different counters or non-completable criteria + if (!maxcounter) + return false; + + return progress->counter >= maxcounter; } void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement) @@ -1704,13 +1787,23 @@ bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry) return false; } -void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype) +void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint32 changeValue, ProgressType ptype) { - DETAIL_FILTER_LOG(LOG_FILTER_ACHIEVEMENT_UPDATES, "AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow()); + DETAIL_FILTER_LOG(LOG_FILTER_ACHIEVEMENT_UPDATES, "AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", criteria->ID, changeValue, m_player->GetGUIDLow()); + + uint32 max_value = GetCriteriaProgressMaxCounter(criteria); + + if (!max_value) + max_value = std::numeric_limits::max(); + + // change value must be in allowed value range for SET/HIGHEST directly + if (changeValue > max_value) + changeValue = max_value; CriteriaProgress *progress = NULL; + uint32 old_value = 0; - CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID); + CriteriaProgressMap::iterator iter = m_criteriaProgress.find(criteria->ID); if(iter == m_criteriaProgress.end()) { @@ -1718,7 +1811,7 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, if(changeValue == 0) return; - progress = &m_criteriaProgress[entry->ID]; + progress = &m_criteriaProgress[criteria->ID]; progress->counter = changeValue; progress->date = time(NULL); } @@ -1726,6 +1819,8 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, { progress = &iter->second; + old_value = progress->counter; + uint32 newValue = 0; switch(ptype) { @@ -1735,7 +1830,6 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, case PROGRESS_ACCUMULATE: { // avoid overflow - uint32 max_value = std::numeric_limits::max(); newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value; break; } @@ -1753,16 +1847,64 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, progress->changed = true; - if(entry->timeLimit) + if(criteria->timeLimit) { time_t now = time(NULL); - if(time_t(progress->date + entry->timeLimit) < now) + if(time_t(progress->date + criteria->timeLimit) < now) progress->counter = 1; // also it seems illogical, the timeframe will be extended at every criteria update progress->date = now; } - SendCriteriaUpdate(entry->ID,progress); + + // update client side value + SendCriteriaUpdate(criteria->ID,progress); + + // nothing do for counter case + if (achievement->flags & ACHIEVEMENT_FLAG_COUNTER) + return; + + // update dependent achievements state at criteria complete + if (old_value < progress->counter) + { + if(IsCompletedCriteria(criteria, achievement)) + CompletedCriteriaFor(achievement); + + // check again the completeness for SUMM and REQ COUNT achievements, + // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria + if (achievement->flags & ACHIEVEMENT_FLAG_SUMM) + { + if (IsCompletedAchievement(achievement)) + CompletedAchievement(achievement); + } + + if(AchievementEntryList const* achRefList = sAchievementMgr.GetAchievementByReferencedId(achievement->ID)) + { + for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr) + if(IsCompletedAchievement(*itr)) + CompletedAchievement(*itr); + } + } + // update dependent achievements state at criteria incomplete + else if (old_value > progress->counter) + { + if (progress->counter < max_value) + { + WorldPacket data(SMSG_CRITERIA_DELETED,4); + data << uint32(criteria->ID); + m_player->SendDirectMessage(&data); + } + + if (HasAchievement(achievement->ID)) + if(!IsCompletedAchievement(achievement)) + IncompletedAchievement(achievement); + + if(AchievementEntryList const* achRefList = sAchievementMgr.GetAchievementByReferencedId(achievement->ID)) + for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr) + if (HasAchievement((*itr)->ID)) + if(!IsCompletedAchievement(*itr)) + IncompletedAchievement(*itr); + } } void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) @@ -1833,6 +1975,43 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) } } +void AchievementMgr::IncompletedAchievement(AchievementEntry const* achievement) +{ + DETAIL_LOG("AchievementMgr::IncompletedAchievement(%u)", achievement->ID); + if (achievement->flags & ACHIEVEMENT_FLAG_COUNTER) + return; + + CompletedAchievementMap::iterator itr = m_completedAchievements.find(achievement->ID); + if (itr == m_completedAchievements.end()) + return; + + WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4); + data << uint32(achievement->ID); + m_player->SendDirectMessage(&data); + + if (!itr->second.changed) // complete state saved + CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u AND achievement = %u", + GetPlayer()->GetGUIDLow(), achievement->ID); + + m_completedAchievements.erase(achievement->ID); + + // reward items and titles if any + AchievementReward const* reward = sAchievementMgr.GetAchievementReward(achievement, GetPlayer()->getGender()); + + // no rewards + if(!reward) + return; + + // titles + if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE ? 0 : 1]) + { + if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId)) + GetPlayer()->SetTitle(titleEntry, true); + } + + // items impossible remove in clear way... +} + void AchievementMgr::SendAllAchievementData() { // since we don't know the exact size of the packed GUIDs this is just an approximation diff --git a/src/game/AchievementMgr.h b/src/game/AchievementMgr.h index 8dc7d2af7..ee394e586 100644 --- a/src/game/AchievementMgr.h +++ b/src/game/AchievementMgr.h @@ -265,13 +265,23 @@ class AchievementMgr CompletedAchievementMap const& GetCompletedAchievements() const { return m_completedAchievements; } bool IsCompletedCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement) const; - private: + uint32 GetCriteriaProgressCounter(AchievementCriteriaEntry const* entry) const + { + CriteriaProgressMap::const_iterator iter = m_criteriaProgress.find(entry->ID); + return iter != m_criteriaProgress.end() ? iter->second.counter : 0; + } + + static uint32 GetCriteriaProgressMaxCounter(AchievementCriteriaEntry const* entry); + enum ProgressType { PROGRESS_SET, PROGRESS_ACCUMULATE, PROGRESS_HIGHEST }; + void SetCriteriaProgress(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint32 changeValue, ProgressType ptype = PROGRESS_SET); + + private: void SendAchievementEarned(AchievementEntry const* achievement); void SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress); - void SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype = PROGRESS_SET); void CompletedCriteriaFor(AchievementEntry const* achievement); void CompletedAchievement(AchievementEntry const* entry); + void IncompletedAchievement(AchievementEntry const* entry); bool IsCompletedAchievement(AchievementEntry const* entry); void CompleteAchievementsWithRefs(AchievementEntry const* entry); void BuildAllDataPacket(WorldPacket *data); diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index 759915638..bfd293308 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -39,6 +39,7 @@ // Supported shift-links (client generated and server side) // |color|Hachievement:achievement_id:player_guid_hex:completed_0_1:mm:dd:yy_from_2000:criteriaMask1:criteriaMask2:criteriaMask3:criteriaMask4|h[name]|h|r // - client, item icon shift click, not used in server currently +// |color|Hachievement_criteria:criteria_id|h[name]|h|r // |color|Harea:area_id|h[name]|h|r // |color|Hareatrigger:id|h[name]|h|r // |color|Hareatrigger_target:id|h[name]|h|r @@ -87,6 +88,22 @@ ChatCommand * ChatHandler::getCommandTable() { NULL, 0, false, NULL, "", NULL } }; + static ChatCommand achievementCreateriaCommandTable[] = + { + { "add", SEC_ADMINISTRATOR, true, &ChatHandler::HandleAchievementCriteriaAddCommand, "", NULL }, + { "remove", SEC_ADMINISTRATOR, true, &ChatHandler::HandleAchievementCriteriaRemoveCommand,"", NULL }, + { NULL, 0, true, NULL, "", NULL } + }; + + static ChatCommand achievementCommandTable[] = + { + { "createria", SEC_ADMINISTRATOR, true, NULL, "", achievementCreateriaCommandTable }, + { "add", SEC_ADMINISTRATOR, true, &ChatHandler::HandleAchievementAddCommand, "", NULL }, + { "remove", SEC_ADMINISTRATOR, true, &ChatHandler::HandleAchievementRemoveCommand, "", NULL }, + { "", SEC_ADMINISTRATOR, true, &ChatHandler::HandleAchievementCommand, "", NULL }, + { NULL, 0, true, NULL, "", NULL } + }; + static ChatCommand auctionCommandTable[] = { { "alliance", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAuctionAllianceCommand, "", NULL }, @@ -642,6 +659,7 @@ ChatCommand * ChatHandler::getCommandTable() static ChatCommand commandTable[] = { { "account", SEC_PLAYER, true, NULL, "", accountCommandTable }, + { "achievement", SEC_ADMINISTRATOR, true, NULL, "", achievementCommandTable }, { "auction", SEC_ADMINISTRATOR, false, NULL, "", auctionCommandTable }, { "cast", SEC_ADMINISTRATOR, false, NULL, "", castCommandTable }, { "character", SEC_GAMEMASTER, true, NULL, "", characterCommandTable}, diff --git a/src/game/Chat.h b/src/game/Chat.h index c08888cba..4bd95af36 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -131,6 +131,13 @@ class ChatHandler bool HandleAuctionHordeCommand(char* args); bool HandleAuctionCommand(char* args); + bool HandleAchievementCommand(char* args); + + bool HandleAchievementAddCommand(char* args); + bool HandleAchievementRemoveCommand(char* args); + bool HandleAchievementCriteriaAddCommand(char* args); + bool HandleAchievementCriteriaRemoveCommand(char* args); + bool HandleBanAccountCommand(char* args); bool HandleBanCharacterCommand(char* args); bool HandleBanIPCommand(char* args); @@ -595,6 +602,7 @@ class ChatHandler // Utility methods for commands bool ShowAccountListHelper(QueryResult* result, uint32* limit = NULL, bool title = true, bool error = true); void ShowAchievementListHelper(AchievementEntry const * achEntry, LocaleConstant loc, time_t const* date = NULL, Player* target = NULL); + void ShowAchievementCriteriaListHelper(AchievementCriteriaEntry const* criEntry, AchievementEntry const * achEntry, LocaleConstant loc, Player* target = NULL); void ShowFactionListHelper(FactionEntry const * factionEntry, LocaleConstant loc, FactionState const* repState = NULL, Player * target = NULL ); void ShowItemListHelper(uint32 itemId, int loc_idx, Player* target = NULL); void ShowQuestListHelper(uint32 questId, int32 loc_idx, Player* target = NULL); diff --git a/src/game/DBCStructure.h b/src/game/DBCStructure.h index 37133c75d..9577fd625 100644 --- a/src/game/DBCStructure.h +++ b/src/game/DBCStructure.h @@ -489,7 +489,7 @@ struct AchievementCriteriaEntry uint32 additionalRequirement2_value; // 8 additional requirement 1 value } raw; }; - //char* name[16]; // 9-24 + char* name[16]; // 9-24 //uint32 name_flags; // 25 uint32 completionFlag; // 26 uint32 groupFlag; // 27 diff --git a/src/game/DBCfmt.h b/src/game/DBCfmt.h index b6b46fc72..127e8a28c 100644 --- a/src/game/DBCfmt.h +++ b/src/game/DBCfmt.h @@ -20,7 +20,7 @@ #define MANGOS_DBCSFRM_H const char Achievementfmt[]="niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii"; -const char AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiixii"; +const char AchievementCriteriafmt[]="niiiiiiiissssssssssssssssxiixii"; const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxixxxxxxx"; const char AreaGroupEntryfmt[]="niiiiiii"; const char AreaTriggerEntryfmt[]="niffffffff"; diff --git a/src/game/Language.h b/src/game/Language.h index cb344a161..ddce509e9 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -877,7 +877,11 @@ enum MangosStrings LANG_CHANGE_HEX_FIELD = 1158, LANG_CHANGE_FLOAT = 1159, //log LANG_CHANGE_FLOAT_FIELD = 1160, - // Room for more level 3 1161-1199 not used + LANG_COMMAND_ACHIEVEMENT_CREATERIA = 1161, + LANG_COUNTER = 1162, + LANG_ACHIEVEMENT_NOT_EXIST = 1163, + LANG_ACHIEVEMENT_CRITERIA_NOT_EXIST = 1164, + // Room for more level 3 1163-1199 not used // Debug commands LANG_CINEMATIC_NOT_EXIST = 1200, diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 1095666d7..2e0e2b433 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -1018,6 +1018,269 @@ bool ChatHandler::HandleAccountSetPasswordCommand(char* args) return true; } + +void ChatHandler::ShowAchievementCriteriaListHelper(AchievementCriteriaEntry const* criEntry, AchievementEntry const * achEntry, LocaleConstant loc, Player* target /*= NULL*/) +{ + std::ostringstream ss; + if (m_session) + { + ss << criEntry->ID << " - |cffffffff|Hachievement_criteria:" << criEntry->ID << "|h[" << criEntry->name[loc] << " " << localeNames[loc] << "]|h|r"; + } + else + ss << criEntry->ID << " - " << criEntry->name[loc] << " " << localeNames[loc]; + + if (target) + ss << " = " << target->GetAchievementMgr().GetCriteriaProgressCounter(criEntry); + + if (achEntry->flags & ACHIEVEMENT_FLAG_COUNTER) + ss << GetMangosString(LANG_COUNTER); + else + { + ss << " [" << AchievementMgr::GetCriteriaProgressMaxCounter(criEntry) << "]"; + + if (target && target->GetAchievementMgr().IsCompletedCriteria(criEntry, achEntry)) + ss << GetMangosString(LANG_COMPLETE); + } + + SendSysMessage(ss.str().c_str()); +} + +bool ChatHandler::HandleAchievementCommand(char* args) +{ + char* nameStr = ExtractOptNotLastArg(&args); + + Player* target = NULL; + + if (nameStr) + { + if (!ExtractPlayerTarget(&nameStr, &target)) + return false; + } + else + target = getSelectedPlayer(); + + uint32 achId; + if (!ExtractUint32KeyFromLink(&args, "Hachievement", achId)) + return false; + + AchievementEntry const *achEntry = sAchievementStore.LookupEntry(achId); + if (!achEntry) + { + PSendSysMessage(LANG_ACHIEVEMENT_NOT_EXIST, achId); + SetSentErrorMessage(true); + return false; + } + + LocaleConstant loc = GetSessionDbcLocale(); + + CompletedAchievementData const* completed = target ? target->GetAchievementMgr().GetCompleteData(achId) : NULL; + + ShowAchievementListHelper(achEntry, loc, completed ? &completed->date : NULL, target); + + if (AchievementCriteriaEntryList const* criteriaList = sAchievementMgr.GetAchievementCriteriaByAchievement(achEntry->ID)) + { + SendSysMessage(LANG_COMMAND_ACHIEVEMENT_CREATERIA); + for (AchievementCriteriaEntryList::const_iterator itr = criteriaList->begin(); itr != criteriaList->end(); ++itr) + ShowAchievementCriteriaListHelper(*itr, achEntry, loc, target); + } + + return true; +} + +bool ChatHandler::HandleAchievementAddCommand(char* args) +{ + char* nameStr = ExtractOptNotLastArg(&args); + + Player* target; + if (!ExtractPlayerTarget(&nameStr, &target)) + return false; + + uint32 achId; + if (!ExtractUint32KeyFromLink(&args, "Hachievement", achId)) + return false; + + AchievementEntry const *achEntry = sAchievementStore.LookupEntry(achId); + if (!achEntry || achEntry->flags & ACHIEVEMENT_FLAG_COUNTER) + { + PSendSysMessage(LANG_ACHIEVEMENT_NOT_EXIST, achId); + SetSentErrorMessage(true); + return false; + } + + AchievementMgr& mgr = target->GetAchievementMgr(); + + if (AchievementCriteriaEntryList const* criteriaList = sAchievementMgr.GetAchievementCriteriaByAchievement(achEntry->ID)) + { + for (AchievementCriteriaEntryList::const_iterator itr = criteriaList->begin(); itr != criteriaList->end(); ++itr) + { + if (mgr.IsCompletedCriteria(*itr, achEntry)) + continue; + + uint32 maxValue = AchievementMgr::GetCriteriaProgressMaxCounter(*itr); + mgr.SetCriteriaProgress(*itr, achEntry, maxValue); + } + } + + LocaleConstant loc = GetSessionDbcLocale(); + CompletedAchievementData const* completed = target ? target->GetAchievementMgr().GetCompleteData(achId) : NULL; + ShowAchievementListHelper(achEntry, loc, completed ? &completed->date : NULL, target); + return true; +} + +bool ChatHandler::HandleAchievementRemoveCommand(char* args) +{ + char* nameStr = ExtractOptNotLastArg(&args); + + Player* target; + if (!ExtractPlayerTarget(&nameStr, &target)) + return false; + + uint32 achId; + if (!ExtractUint32KeyFromLink(&args, "Hachievement", achId)) + return false; + + AchievementEntry const *achEntry = sAchievementStore.LookupEntry(achId); + if (!achEntry) + { + PSendSysMessage(LANG_ACHIEVEMENT_NOT_EXIST, achId); + SetSentErrorMessage(true); + return false; + } + + AchievementMgr& mgr = target->GetAchievementMgr(); + + if (AchievementCriteriaEntryList const* criteriaList = sAchievementMgr.GetAchievementCriteriaByAchievement(achEntry->ID)) + for (AchievementCriteriaEntryList::const_iterator itr = criteriaList->begin(); itr != criteriaList->end(); ++itr) + mgr.SetCriteriaProgress(*itr, achEntry, 0); + + LocaleConstant loc = GetSessionDbcLocale(); + CompletedAchievementData const* completed = target ? target->GetAchievementMgr().GetCompleteData(achId) : NULL; + ShowAchievementListHelper(achEntry, loc, completed ? &completed->date : NULL, target); + return true; +} + +bool ChatHandler::HandleAchievementCriteriaAddCommand(char* args) +{ + Player* target; + uint32 criId; + + if (!ExtractUint32KeyFromLink(&args, "Hachievement_criteria", criId)) + { + // maybe player first + char* nameStr = ExtractArg(&args); + if (!ExtractPlayerTarget(&nameStr, &target)) + return false; + + if (!ExtractUint32KeyFromLink(&args, "Hachievement_criteria", criId)) + return false; + } + else + target = getSelectedPlayer(); + + AchievementCriteriaEntry const *criEntry = sAchievementCriteriaStore.LookupEntry(criId); + if (!criEntry) + { + PSendSysMessage(LANG_ACHIEVEMENT_CRITERIA_NOT_EXIST, criId); + SetSentErrorMessage(true); + return false; + } + + AchievementEntry const *achEntry = sAchievementStore.LookupEntry(criEntry->referredAchievement); + if (!achEntry) + return false; + + LocaleConstant loc = GetSessionDbcLocale(); + + uint32 maxValue = AchievementMgr::GetCriteriaProgressMaxCounter(criEntry); + + AchievementMgr& mgr = target->GetAchievementMgr(); + + // nothing do if completed + if (mgr.IsCompletedCriteria(criEntry, achEntry)) + { + ShowAchievementCriteriaListHelper(criEntry, achEntry, loc, target); + return true; + } + + uint32 progress = mgr.GetCriteriaProgressCounter(criEntry); + + uint32 val; + if (!ExtractOptUInt32(&args, val, maxValue ? maxValue : 1)) + return false; + + uint32 new_val; + + if (maxValue) + new_val = progress < maxValue && maxValue - progress > val ? progress + val : maxValue; + else + { + uint32 max_int = std::numeric_limits::max(); + new_val = progress < max_int && max_int - progress > val ? progress + val : max_int; + } + + mgr.SetCriteriaProgress(criEntry, achEntry, new_val); // value will move to allowed range into function + + ShowAchievementCriteriaListHelper(criEntry, achEntry, loc, target); + return true; +} + +bool ChatHandler::HandleAchievementCriteriaRemoveCommand(char* args) +{ + Player* target; + uint32 criId; + + if (!ExtractUint32KeyFromLink(&args, "Hachievement_criteria", criId)) + { + // maybe player first + char* nameStr = ExtractArg(&args); + if (!ExtractPlayerTarget(&nameStr, &target)) + return false; + + if (!ExtractUint32KeyFromLink(&args, "Hachievement_criteria", criId)) + return false; + } + else + target = getSelectedPlayer(); + + AchievementCriteriaEntry const *criEntry = sAchievementCriteriaStore.LookupEntry(criId); + if (!criEntry) + { + PSendSysMessage(LANG_ACHIEVEMENT_CRITERIA_NOT_EXIST, criId); + SetSentErrorMessage(true); + return false; + } + + AchievementEntry const *achEntry = sAchievementStore.LookupEntry(criEntry->referredAchievement); + if (!achEntry) + return false; + + LocaleConstant loc = GetSessionDbcLocale(); + + uint32 maxValue = AchievementMgr::GetCriteriaProgressMaxCounter(criEntry); + + AchievementMgr& mgr = target->GetAchievementMgr(); + + uint32 progress = mgr.GetCriteriaProgressCounter(criEntry); + + // nothing do if not started + if (progress == 0) + { + ShowAchievementCriteriaListHelper(criEntry, achEntry, loc, target); + return true; + } + + uint32 change; + if (!ExtractOptUInt32(&args, change, maxValue ? maxValue : 1)) + return false; + + uint32 newval = change < progress ? progress - change : 0; + + mgr.SetCriteriaProgress(criEntry, achEntry, newval); // value will move to allowed range into function + + ShowAchievementCriteriaListHelper(criEntry, achEntry, loc, target); + return true; +} + bool ChatHandler::HandleMaxSkillCommand(char* /*args*/) { Player* SelectedPlayer = getSelectedPlayer(); @@ -4176,22 +4439,22 @@ bool ChatHandler::HandleChangeWeatherCommand(char* args) return false; } - uint32 type; - if (!ExtractUInt32(&args, type)) - return false; - - //0 to 3, 0: fine, 1: rain, 2: snow, 3: sand - if (type > 3) - return false; - - float grade; - if (!ExtractFloat(&args, grade)) - return false; - - //0 to 1, sending -1 is instand good weather - if (grade < 0.0f || grade > 1.0f) - return false; - + uint32 type; + if (!ExtractUInt32(&args, type)) + return false; + + //0 to 3, 0: fine, 1: rain, 2: snow, 3: sand + if (type > 3) + return false; + + float grade; + if (!ExtractFloat(&args, grade)) + return false; + + //0 to 1, sending -1 is instand good weather + if (grade < 0.0f || grade > 1.0f) + return false; + Player *player = m_session->GetPlayer(); uint32 zoneid = player->GetZoneId(); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index dcbfc7ac3..246b7112c 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "10341" + #define REVISION_NR "10342" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index 9d09ac49d..576441e03 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -1,6 +1,6 @@ #ifndef __REVISION_SQL_H__ #define __REVISION_SQL_H__ #define REVISION_DB_CHARACTERS "required_10332_02_characters_pet_aura" - #define REVISION_DB_MANGOS "required_10331_02_mangos_command" + #define REVISION_DB_MANGOS "required_10342_02_mangos_command" #define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version" #endif // __REVISION_SQL_H__