From 373f607a44d907d4e8aeef57dba5413ed7e76ff4 Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Fri, 6 Aug 2010 02:09:04 +0400 Subject: [PATCH] [10323] Implement achivement view commands. This is fist part for achievement related command set. Edition commands will added in some later commits when ready. Two command added: * .lookup achievment $partname - show fit achievements (id, shiftlink, complete date for selected player). shiftlink included similar data as generated by client for achievement shift-link (complete state, complete date, marked completed criteria). * .character achievements [$playername] - show completed achievements for selected player --- sql/mangos.sql | 5 +- sql/updates/10323_01_mangos_mangos_string.sql | 6 + sql/updates/10323_02_mangos_command.sql | 6 + sql/updates/Makefile.am | 4 + src/game/AchievementMgr.cpp | 2 +- src/game/AchievementMgr.h | 13 +- src/game/Chat.cpp | 4 +- src/game/Chat.h | 4 + src/game/DBCStructure.h | 2 +- src/game/DBCfmt.h | 2 +- src/game/Language.h | 3 +- src/game/Level2.cpp | 130 ++++++++++++++++++ src/shared/revision_nr.h | 2 +- src/shared/revision_sql.h | 2 +- 14 files changed, 174 insertions(+), 11 deletions(-) create mode 100644 sql/updates/10323_01_mangos_mangos_string.sql create mode 100644 sql/updates/10323_02_mangos_command.sql diff --git a/sql/mangos.sql b/sql/mangos.sql index abb1f74af..9cb942355 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_10314_02_mangos_command` bit(1) default NULL + `required_10323_02_mangos_command` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -520,6 +520,7 @@ INSERT INTO `command` VALUES ('cast dist',3,'Syntax: .cast dist #spellid [#dist [triggered]]\r\n You will cast spell to pint at distance #dist. If \'trigered\' or part provided then spell casted with triggered flag. Not all spells can be casted as area spells.'), ('cast self',3,'Syntax: .cast self #spellid [triggered]\r\nCast #spellid by target at target itself. If \'trigered\' or part provided then spell casted with triggered flag.'), ('cast target',3,'Syntax: .cast target #spellid [triggered]\r\n Selected target will cast #spellid to his victim. If \'trigered\' or part provided then spell casted with triggered flag.'), +('character achievements',2,'Syntax: .character achievements [$player_name]\r\n\r\nShow completed achievments for selected player or player find by $player_name.'), ('character customize',2,'Syntax: .character customize [$name]\r\n\r\nMark selected in game or by $name in command character for customize at next login.'), ('character deleted delete', 4, 'Syntax: .character deleted delete #guid|$name\r\n\r\nCompletely deletes the selected characters.\r\nIf $name is supplied, only characters with that string in their name will be deleted, if #guid is supplied, only the character with that GUID will be deleted.'), ('character deleted list', 3, 'Syntax: .character deleted list [#guid|$name]\r\n\r\nShows a list with all deleted characters.\r\nIf $name is supplied, only characters with that string in their name will be selected, if #guid is supplied, only the character with that GUID will be selected.'), @@ -620,6 +621,7 @@ INSERT INTO `command` VALUES ('lookup account email',2,'Syntax: .lookup account email $emailpart [#limit] \r\n\r\n Searchs accounts, which email including $emailpart with optional parametr #limit of results. If #limit not provided expected 100.'), ('lookup account ip',2,'Syntax: lookup account ip $ippart [#limit] \r\n\r\n Searchs accounts, which last used ip inluding $ippart (textual) with optional parametr #$limit of results. If #limit not provided expected 100.'), ('lookup account name',2,'Syntax: .lookup account name $accountpart [#limit] \r\n\r\n Searchs accounts, which username including $accountpart with optional parametr #limit of results. If #limit not provided expected 100.'), +('lookup achievement',2,'Syntax: .lookup $name\r\nLooks up a achievement by $namepart, and returns all matches with their quest ID\'s. Achievement shift-links generated with information about achievment state for selected player. Also for completed achievments in list show complete date.'), ('lookup area',1,'Syntax: .lookup area $namepart\r\n\r\nLooks up an area by $namepart, and returns all matches with their area ID\'s.'), ('lookup creature',3,'Syntax: .lookup creature $namepart\r\n\r\nLooks up a creature by $namepart, and returns all matches with their creature ID\'s.'), ('lookup event',2,'Syntax: .lookup event $name\r\nAttempts to find the ID of the event with the provided $name.'), @@ -3288,6 +3290,7 @@ INSERT INTO `mangos_string` VALUES (369,'Required quest (normal difficulty):',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (370,'Required heroic keys:',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (371,'Required quest (heroic difficulty):',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(372,'No achievement!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (400,'|cffff0000[System Message]:|rScripts reloaded',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (401,'You change security level of account %s to %i.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (402,'%s changed your security level to %i.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), diff --git a/sql/updates/10323_01_mangos_mangos_string.sql b/sql/updates/10323_01_mangos_mangos_string.sql new file mode 100644 index 000000000..07ca0c507 --- /dev/null +++ b/sql/updates/10323_01_mangos_mangos_string.sql @@ -0,0 +1,6 @@ +ALTER TABLE db_version CHANGE COLUMN required_10314_02_mangos_command required_10323_01_mangos_mangos_string bit; + +DELETE FROM mangos_string WHERE entry IN (372); + +INSERT INTO mangos_string VALUES +(372,'No achievement!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); diff --git a/sql/updates/10323_02_mangos_command.sql b/sql/updates/10323_02_mangos_command.sql new file mode 100644 index 000000000..6ef101402 --- /dev/null +++ b/sql/updates/10323_02_mangos_command.sql @@ -0,0 +1,6 @@ +ALTER TABLE db_version CHANGE COLUMN required_10323_01_mangos_mangos_string required_10323_02_mangos_command bit; + +DELETE FROM command WHERE name IN ('lookup achievement','character achievements'); +INSERT INTO command (name, security, help) VALUES +('character achievements',2,'Syntax: .character achievements [$player_name]\r\n\r\nShow completed achievments for selected player or player find by $player_name.'), +('lookup achievement',2,'Syntax: .lookup $name\r\nLooks up a achievement by $namepart, and returns all matches with their quest ID\'s. Achievement shift-links generated with information about achievment state for selected player. Also for completed achievments in list show complete date.'); diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index b9de9f472..e7c45db00 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -68,6 +68,8 @@ pkgdata_DATA = \ 10312_02_characters_pet_aura.sql \ 10314_01_mangos_mangos_string.sql \ 10314_02_mangos_command.sql \ + 10323_01_mangos_mangos_string.sql \ + 10323_02_mangos_command.sql \ README ## Additional files to include when running 'make dist' @@ -116,4 +118,6 @@ EXTRA_DIST = \ 10312_02_characters_pet_aura.sql \ 10314_01_mangos_mangos_string.sql \ 10314_02_mangos_command.sql \ + 10323_01_mangos_mangos_string.sql \ + 10323_02_mangos_command.sql \ README diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index c59444784..c03f7e446 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -1470,7 +1470,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui 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) +bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement) const { // counter can never complete if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER) diff --git a/src/game/AchievementMgr.h b/src/game/AchievementMgr.h index 32c5c71e2..8dc7d2af7 100644 --- a/src/game/AchievementMgr.h +++ b/src/game/AchievementMgr.h @@ -253,9 +253,17 @@ class AchievementMgr void SendAllAchievementData(); void SendRespondInspectAchievements(Player* player); - Player* GetPlayer() { return m_player;} + Player* GetPlayer() const { return m_player;} - bool HasAchievement(uint32 achievement_id) const { return m_completedAchievements.find(achievement_id) != m_completedAchievements.end(); } + CompletedAchievementData const* GetCompleteData(uint32 achievement_id) const + { + CompletedAchievementMap::const_iterator itr = m_completedAchievements.find(achievement_id); + return itr != m_completedAchievements.end() ? &itr->second : NULL; + } + + bool HasAchievement(uint32 achievement_id) const { return GetCompleteData(achievement_id) != NULL; } + CompletedAchievementMap const& GetCompletedAchievements() const { return m_completedAchievements; } + bool IsCompletedCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement) const; private: enum ProgressType { PROGRESS_SET, PROGRESS_ACCUMULATE, PROGRESS_HIGHEST }; @@ -264,7 +272,6 @@ class AchievementMgr void SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype = PROGRESS_SET); void CompletedCriteriaFor(AchievementEntry const* achievement); void CompletedAchievement(AchievementEntry const* entry); - bool IsCompletedCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement); 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 5923969b4..c5f8c5b9a 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -37,7 +37,7 @@ #include "GameEventMgr.h" // Supported shift-links (client generated and server side) -// |color|Hachievement:achievement_id:player_guid:0:0:0:0:0:0:0:0|h[name]|h|r +// |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|Harea:area_id|h[name]|h|r // |color|Hareatrigger:id|h[name]|h|r @@ -141,6 +141,7 @@ ChatCommand * ChatHandler::getCommandTable() static ChatCommand characterCommandTable[] = { + { "achievements", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterAchievementsCommand,"",NULL }, { "customize", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterCustomizeCommand, "", NULL }, { "deleted", SEC_GAMEMASTER, true, NULL, "", characterDeletedCommandTable}, { "erase", SEC_CONSOLE, true, &ChatHandler::HandleCharacterEraseCommand, "", NULL }, @@ -317,6 +318,7 @@ ChatCommand * ChatHandler::getCommandTable() static ChatCommand lookupCommandTable[] = { { "account", SEC_GAMEMASTER, true, NULL, "", lookupAccountCommandTable }, + { "achievement", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupAchievementCommand, "", NULL }, { "area", SEC_MODERATOR, true, &ChatHandler::HandleLookupAreaCommand, "", NULL }, { "creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupCreatureCommand, "", NULL }, { "event", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupEventCommand, "", NULL }, diff --git a/src/game/Chat.h b/src/game/Chat.h index 7541d1dd4..4725c62c7 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -21,6 +21,7 @@ #include "SharedDefines.h" +struct AchievementEntry; struct AreaTrigger; struct FactionEntry; struct FactionState; @@ -144,6 +145,7 @@ class ChatHandler bool HandleCastSelfCommand(char* args); bool HandleCastTargetCommand(char* args); + bool HandleCharacterAchievementsCommand(char* args); bool HandleCharacterCustomizeCommand(char* args); bool HandleCharacterDeletedDeleteCommand(char* args); bool HandleCharacterDeletedListCommand(char* args); @@ -255,6 +257,7 @@ class ChatHandler bool HandleLookupAccountEmailCommand(char* args); bool HandleLookupAccountIpCommand(char* args); bool HandleLookupAccountNameCommand(char* args); + bool HandleLookupAchievementCommand(char* args); bool HandleLookupAreaCommand(char* args); bool HandleLookupCreatureCommand(char* args); bool HandleLookupEventCommand(char* args); @@ -575,6 +578,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 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 55c21f778..37133c75d 100644 --- a/src/game/DBCStructure.h +++ b/src/game/DBCStructure.h @@ -497,7 +497,7 @@ struct AchievementCriteriaEntry // for timed spells it is spell id for // timed kills it is creature id uint32 timeLimit; // 29 time limit in seconds - //uint32 showOrder; // 30 show order + uint32 showOrder; // 30 show order, also used in achievement shift-links as index in state bitmask }; struct AreaTableEntry diff --git a/src/game/DBCfmt.h b/src/game/DBCfmt.h index 27d82d990..b6b46fc72 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[]="niiiiiiiixxxxxxxxxxxxxxxxxiixix"; +const char AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiixii"; 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 9e45a5fa0..27cfc4d3d 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -360,7 +360,8 @@ enum MangosStrings LANG_TRIGGER_REQ_QUEST_NORMAL = 369, LANG_TRIGGER_REQ_KEYS_HEROIC = 370, LANG_TRIGGER_REQ_QUEST_HEROIC = 371, - // Room for more level 2 372-399 not used + LANG_COMMAND_ACHIEVEMENT_NOTFOUND = 372, + // Room for more level 2 373-399 not used // level 3 chat LANG_SCRIPTS_RELOADED = 400, diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index 997f152e3..40fc01aeb 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -1170,6 +1170,136 @@ bool ChatHandler::HandleGUIDCommand(char* /*args*/) return true; } +void ChatHandler::ShowAchievementListHelper(AchievementEntry const * achEntry, LocaleConstant loc, time_t const* date /*= NULL*/, Player* target /*= NULL */ ) +{ + std::string name = achEntry->name[loc]; + + ObjectGuid guid = target ? target->GetObjectGuid() : ObjectGuid(); + + // |color|Hachievement:achievement_id:player_guid_hex:completed_0_1:mm:dd:yy_from_2000:criteriaMask:0:0:0|h[name]|h|r + std::ostringstream ss; + if (m_session) + { + ss << achEntry->ID << " - |cffffffff|Hachievement:" << achEntry->ID << ":" << std::hex << guid.GetRawValue() << std::dec; + if (date) + { + // complete date + tm* aTm = localtime(date); + ss << ":1:" << aTm->tm_mon+1 << ":" << aTm->tm_mday << ":" << (aTm->tm_year+1900-2000) << ":"; + + // complete criteria mask (all bits set) + ss << uint32(-1) << ":" << uint32(-1) << ":" << uint32(-1) << ":" << uint32(-1) << ":"; + } + else + { + // complete date + ss << ":0:0:0:-1:"; + + // complete criteria mask + if (target) + { + uint32 criteriaMask[4] = {0, 0, 0, 0}; + + if (AchievementMgr const* mgr = target ? &target->GetAchievementMgr() : NULL) + if (AchievementCriteriaEntryList const* criteriaList = sAchievementMgr.GetAchievementCriteriaByAchievement(achEntry->ID)) + for (AchievementCriteriaEntryList::const_iterator itr = criteriaList->begin(); itr != criteriaList->end(); ++itr) + if (mgr->IsCompletedCriteria(*itr, achEntry)) + criteriaMask[((*itr)->showOrder - 1) / 32] |= (1 << (((*itr)->showOrder - 1) % 32)); + + for (int i = 0; i < 4; ++i) + ss << criteriaMask[i] << ":"; + } + else + ss << "0:0:0:0:"; + } + + ss << "|h[" << name << " " << localeNames[loc] << "]|h|r"; + } + else + ss << achEntry->ID << " - " << name << " " << localeNames[loc]; + + if (target && date) + ss << " [" << TimeToTimestampStr(*date) << "]"; + + SendSysMessage(ss.str().c_str()); +} + +bool ChatHandler::HandleLookupAchievementCommand(char* args) +{ + if (!*args) + return false; + + // Can be NULL at console call + Player *target = getSelectedPlayer(); + + std::string namepart = args; + std::wstring wnamepart; + + if (!Utf8toWStr(namepart, wnamepart)) + return false; + + // converting string that we try to find to lower case + wstrToLower(wnamepart); + + uint32 counter = 0; // Counter for figure out that we found smth. + + for (uint32 id = 0; id < sAchievementStore.GetNumRows(); ++id) + { + AchievementEntry const *achEntry = sAchievementStore.LookupEntry(id); + if (!achEntry) + continue; + + int loc = GetSessionDbcLocale(); + std::string name = achEntry->name[loc]; + if (name.empty()) + continue; + + if (!Utf8FitTo(name, wnamepart)) + { + loc = 0; + for(; loc < MAX_LOCALE; ++loc) + { + if (loc == GetSessionDbcLocale()) + continue; + + name = achEntry->name[loc]; + if (name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + break; + } + } + + if (loc < MAX_LOCALE) + { + CompletedAchievementData const* completed = target ? target->GetAchievementMgr().GetCompleteData(id) : NULL; + ShowAchievementListHelper(achEntry, LocaleConstant(loc), completed ? &completed->date : NULL, target); + counter++; + } + } + + if (counter == 0) // if counter == 0 then we found nth + SendSysMessage(LANG_COMMAND_ACHIEVEMENT_NOTFOUND); + return true; +} + +bool ChatHandler::HandleCharacterAchievementsCommand(char* args) +{ + Player* target; + if (!extractPlayerTarget(args, &target)) + return false; + + LocaleConstant loc = GetSessionDbcLocale(); + + CompletedAchievementMap const& complitedList = target->GetAchievementMgr().GetCompletedAchievements(); + for(CompletedAchievementMap::const_iterator itr = complitedList.begin(); itr != complitedList.end(); ++itr) + { + AchievementEntry const *achEntry = sAchievementStore.LookupEntry(itr->first); + ShowAchievementListHelper(achEntry, loc, &itr->second.date, target); + } + return true; +} void ChatHandler::ShowFactionListHelper( FactionEntry const * factionEntry, LocaleConstant loc, FactionState const* repState /*= NULL*/, Player * target /*= NULL */ ) { diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 772b8c7ff..c08be58e5 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 "10322" + #define REVISION_NR "10323" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index 150cbf749..3ae2bf108 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_10312_02_characters_pet_aura" - #define REVISION_DB_MANGOS "required_10314_02_mangos_command" + #define REVISION_DB_MANGOS "required_10323_02_mangos_command" #define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version" #endif // __REVISION_SQL_H__