diff --git a/sql/characters.sql b/sql/characters.sql index 796482cd2..ad7c0282b 100644 --- a/sql/characters.sql +++ b/sql/characters.sql @@ -21,7 +21,7 @@ DROP TABLE IF EXISTS `character_db_version`; CREATE TABLE `character_db_version` ( - `required_10568_01_characters_character_tutorial` bit(1) default NULL + `required_10655_01_characters_character_queststatus_monthly` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB'; -- @@ -725,6 +725,27 @@ LOCK TABLES `character_queststatus_daily` WRITE; /*!40000 ALTER TABLE `character_queststatus_daily` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `character_queststatus_monthly` +-- + +DROP TABLE IF EXISTS `character_queststatus_monthly`; +CREATE TABLE `character_queststatus_monthly` ( + `guid` int(11) unsigned NOT NULL default '0' COMMENT 'Global Unique Identifier', + `quest` int(11) unsigned NOT NULL default '0' COMMENT 'Quest Identifier', + PRIMARY KEY (`guid`,`quest`), + KEY `idx_guid` (`guid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Player System'; + +-- +-- Dumping data for table `character_queststatus_monthly` +-- + +LOCK TABLES `character_queststatus_monthly` WRITE; +/*!40000 ALTER TABLE `character_queststatus_monthly` DISABLE KEYS */; +/*!40000 ALTER TABLE `character_queststatus_monthly` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `character_queststatus_weekly` -- @@ -1624,6 +1645,7 @@ CREATE TABLE `saved_variables` ( `NextArenaPointDistributionTime` bigint(40) UNSIGNED NOT NULL DEFAULT '0', `NextDailyQuestResetTime` bigint(40) unsigned NOT NULL default '0', `NextWeeklyQuestResetTime` bigint(40) unsigned NOT NULL default '0', + `NextMonthlyQuestResetTime` bigint(40) unsigned NOT NULL default '0', `cleaning_flags` int(11) unsigned NOT NULL default '0' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Variable Saves'; diff --git a/sql/updates/10655_01_characters_character_queststatus_monthly.sql b/sql/updates/10655_01_characters_character_queststatus_monthly.sql new file mode 100644 index 000000000..f709c7d38 --- /dev/null +++ b/sql/updates/10655_01_characters_character_queststatus_monthly.sql @@ -0,0 +1,12 @@ +ALTER TABLE character_db_version CHANGE COLUMN required_10568_01_characters_character_tutorial required_10655_01_characters_character_queststatus_monthly bit; + +DROP TABLE IF EXISTS character_queststatus_monthly; +CREATE TABLE character_queststatus_monthly ( + guid int(11) unsigned NOT NULL default '0' COMMENT 'Global Unique Identifier', + quest int(11) unsigned NOT NULL default '0' COMMENT 'Quest Identifier', + PRIMARY KEY (guid,quest), + KEY idx_guid (guid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Player System'; + +ALTER TABLE saved_variables + ADD COLUMN NextMonthlyQuestResetTime bigint(40) unsigned NOT NULL default '0' AFTER NextWeeklyQuestResetTime; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 5704e0d2e..17f5ab22f 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -110,6 +110,7 @@ pkgdata_DATA = \ 10628_01_mangos_mangos_string.sql \ 10629_01_mangos_mangos_string.sql \ 10654_01_mangos_game_event_creature_quest.sql \ + 10655_01_characters_character_queststatus_monthly.sql \ README ## Additional files to include when running 'make dist' @@ -200,4 +201,5 @@ EXTRA_DIST = \ 10628_01_mangos_mangos_string.sql \ 10629_01_mangos_mangos_string.sql \ 10654_01_mangos_game_event_creature_quest.sql \ + 10655_01_characters_character_queststatus_monthly.sql \ README diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index 08bf61e02..59678ac12 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -79,6 +79,7 @@ bool LoginQueryHolder::Initialize() res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS, "SELECT quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4 FROM character_queststatus WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS,"SELECT quest FROM character_queststatus_daily WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS,"SELECT quest FROM character_queststatus_weekly WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMONTHLYQUESTSTATUS,"SELECT quest FROM character_queststatus_monthly WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADINVENTORY, "SELECT data,text,bag,slot,item,item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag,slot", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT spec,button,action,type FROM character_action WHERE guid = '%u' ORDER BY button", GUID_LOPART(m_guid)); diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 1ea8b01d4..3ed99bdce 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -3685,6 +3685,15 @@ void ObjectMgr::LoadQuests() } } + if (qinfo->QuestFlags & QUEST_MANGOS_FLAGS_MONTHLY) + { + if (!(qinfo->QuestFlags & QUEST_MANGOS_FLAGS_REPEATABLE)) + { + sLog.outErrorDb("Monthly quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId()); + qinfo->QuestFlags |= QUEST_MANGOS_FLAGS_REPEATABLE; + } + } + if (qinfo->QuestFlags & QUEST_FLAGS_AUTO_REWARDED) { // at auto-reward can be rewarded only RewChoiceItemId[0] diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 33584ed4f..ef980af11 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -13275,7 +13275,8 @@ bool Player::CanSeeStartQuest(Quest const *pQuest) const if (SatisfyQuestClass(pQuest, false) && SatisfyQuestRace(pQuest, false) && SatisfyQuestSkill(pQuest, false) && SatisfyQuestExclusiveGroup(pQuest, false) && SatisfyQuestReputation(pQuest, false) && SatisfyQuestPreviousQuest(pQuest, false) && SatisfyQuestNextChain(pQuest, false) && - SatisfyQuestPrevChain(pQuest, false) && SatisfyQuestDay(pQuest, false) && SatisfyQuestWeek(pQuest, false)) + SatisfyQuestPrevChain(pQuest, false) && SatisfyQuestDay(pQuest, false) && SatisfyQuestWeek(pQuest, false) && + SatisfyQuestMonth(pQuest, false)) { return getLevel() + sWorld.getConfig(CONFIG_UINT32_QUEST_HIGH_LEVEL_HIDE_DIFF) >= pQuest->GetMinLevel(); } @@ -13290,7 +13291,7 @@ bool Player::CanTakeQuest(Quest const *pQuest, bool msg) const SatisfyQuestSkill(pQuest, msg) && SatisfyQuestReputation(pQuest, msg) && SatisfyQuestPreviousQuest(pQuest, msg) && SatisfyQuestTimed(pQuest, msg) && SatisfyQuestNextChain(pQuest, msg) && SatisfyQuestPrevChain(pQuest, msg) && - SatisfyQuestDay(pQuest, msg) && SatisfyQuestWeek(pQuest, msg); + SatisfyQuestDay(pQuest, msg) && SatisfyQuestWeek(pQuest, msg) && SatisfyQuestMonth(pQuest, msg); } bool Player::CanAddQuest(Quest const *pQuest, bool msg) const @@ -13409,7 +13410,7 @@ bool Player::CanRewardQuest(Quest const *pQuest, bool msg) const return false; // daily quest can't be rewarded (25 daily quest already completed) - if (!SatisfyQuestDay(pQuest, true) || !SatisfyQuestWeek(pQuest, true)) + if (!SatisfyQuestDay(pQuest, true) || !SatisfyQuestWeek(pQuest, true) || !SatisfyQuestMonth(pQuest, true)) return false; // rewarded and not repeatable quest (only cheating case, then ignore without message) @@ -13695,6 +13696,9 @@ void Player::RewardQuest(Quest const *pQuest, uint32 reward, Object* questGiver, if (pQuest->IsWeekly()) SetWeeklyQuestStatus(quest_id); + if (pQuest->IsMonthly()) + SetMonthlyQuestStatus(quest_id); + if (!pQuest->IsRepeatable()) SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE); else @@ -14133,6 +14137,15 @@ bool Player::SatisfyQuestWeek(Quest const* qInfo, bool msg) const return m_weeklyquests.find(qInfo->GetQuestId()) == m_weeklyquests.end(); } +bool Player::SatisfyQuestMonth(Quest const* qInfo, bool msg) const +{ + if (!qInfo->IsMonthly() || m_monthlyquests.empty()) + return true; + + // if not found in cooldown list + return m_monthlyquests.find(qInfo->GetQuestId()) == m_monthlyquests.end(); +} + bool Player::CanGiveQuestSourceItem( Quest const *pQuest, ItemPosCountVec* dest ) const { uint32 srcitem = pQuest->GetSrcItemId(); @@ -15458,6 +15471,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) _LoadQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS)); _LoadDailyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS)); _LoadWeeklyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS)); + _LoadMonthlyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADMONTHLYQUESTSTATUS)); _LoadTalents(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTALENTS)); @@ -16291,6 +16305,36 @@ void Player::_LoadWeeklyQuestStatus(QueryResult *result) m_WeeklyQuestChanged = false; } +void Player::_LoadMonthlyQuestStatus(QueryResult *result) +{ + m_monthlyquests.clear(); + + //QueryResult *result = CharacterDatabase.PQuery("SELECT quest FROM character_queststatus_weekly WHERE guid = '%u'", GetGUIDLow()); + + if (result) + { + do + { + Field *fields = result->Fetch(); + + uint32 quest_id = fields[0].GetUInt32(); + + Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id); + if (!pQuest) + continue; + + m_monthlyquests.insert(quest_id); + + DEBUG_LOG("Monthly quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow()); + } + while( result->NextRow() ); + + delete result; + } + + m_MonthlyQuestChanged = false; +} + void Player::_LoadSpells(QueryResult *result) { //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,active,disabled FROM character_spell WHERE guid = '%u'",GetGUIDLow()); @@ -16911,6 +16955,7 @@ void Player::SaveToDB() _SaveQuestStatus(); _SaveDailyQuestStatus(); _SaveWeeklyQuestStatus(); + _SaveMonthlyQuestStatus(); _SaveSpells(); _SaveSpellCooldowns(); _SaveActions(); @@ -17239,6 +17284,24 @@ void Player::_SaveWeeklyQuestStatus() m_WeeklyQuestChanged = false; } +void Player::_SaveMonthlyQuestStatus() +{ + if (!m_MonthlyQuestChanged || m_monthlyquests.empty()) + return; + + // we don't need transactions here. + CharacterDatabase.PExecute("DELETE FROM character_queststatus_monthly WHERE guid = '%u'", GetGUIDLow()); + + for (QuestSet::const_iterator iter = m_monthlyquests.begin(); iter != m_monthlyquests.end(); ++iter) + { + uint32 quest_id = *iter; + + CharacterDatabase.PExecute("INSERT INTO character_queststatus_monthly (guid, quest) VALUES ('%u', '%u')", GetGUIDLow(), quest_id); + } + + m_MonthlyQuestChanged = false; +} + void Player::_SaveSkills() { // we don't need transactions here. @@ -19941,6 +20004,12 @@ void Player::SetWeeklyQuestStatus( uint32 quest_id ) m_WeeklyQuestChanged = true; } +void Player::SetMonthlyQuestStatus(uint32 quest_id) +{ + m_monthlyquests.insert(quest_id); + m_MonthlyQuestChanged = true; +} + void Player::ResetDailyQuestStatus() { for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx) @@ -19960,6 +20029,16 @@ void Player::ResetWeeklyQuestStatus() m_WeeklyQuestChanged = false; } +void Player::ResetMonthlyQuestStatus() +{ + if (m_monthlyquests.empty()) + return; + + m_monthlyquests.clear(); + // DB data deleted in caller + m_MonthlyQuestChanged = false; +} + BattleGround* Player::GetBattleGround() const { if(GetBattleGroundId()==0) diff --git a/src/game/Player.h b/src/game/Player.h index 7d705b6db..5bc7ae612 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -914,6 +914,7 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOADMAILEDITEMS, PLAYER_LOGIN_QUERY_LOADTALENTS, PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS, + PLAYER_LOGIN_QUERY_LOADMONTHLYQUESTSTATUS, MAX_PLAYER_LOGIN_QUERY }; @@ -1435,6 +1436,7 @@ class MANGOS_DLL_SPEC Player : public Unit bool SatisfyQuestPrevChain( Quest const* qInfo, bool msg ) const; bool SatisfyQuestDay( Quest const* qInfo, bool msg ) const; bool SatisfyQuestWeek( Quest const* qInfo, bool msg ) const; + bool SatisfyQuestMonth(Quest const* qInfo, bool msg) const; bool CanGiveQuestSourceItem( Quest const *pQuest, ItemPosCountVec* dest = NULL) const; void GiveQuestSourceItem( Quest const *pQuest ); bool TakeQuestSourceItem( uint32 quest_id, bool msg ); @@ -1444,8 +1446,10 @@ class MANGOS_DLL_SPEC Player : public Unit void SetDailyQuestStatus( uint32 quest_id ); void SetWeeklyQuestStatus( uint32 quest_id ); + void SetMonthlyQuestStatus(uint32 quest_id); void ResetDailyQuestStatus(); void ResetWeeklyQuestStatus(); + void ResetMonthlyQuestStatus(); uint16 FindQuestSlot( uint32 quest_id ) const; uint32 GetQuestSlotQuestId(uint16 slot) const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_ID_OFFSET); } @@ -2425,6 +2429,7 @@ class MANGOS_DLL_SPEC Player : public Unit typedef std::set QuestSet; QuestSet m_timedquests; QuestSet m_weeklyquests; + QuestSet m_monthlyquests; uint64 m_divider; uint32 m_ingametime; @@ -2442,6 +2447,7 @@ class MANGOS_DLL_SPEC Player : public Unit void _LoadQuestStatus(QueryResult *result); void _LoadDailyQuestStatus(QueryResult *result); void _LoadWeeklyQuestStatus(QueryResult *result); + void _LoadMonthlyQuestStatus(QueryResult *result); void _LoadGroup(QueryResult *result); void _LoadSkills(QueryResult *result); void _LoadSpells(QueryResult *result); @@ -2466,6 +2472,7 @@ class MANGOS_DLL_SPEC Player : public Unit void _SaveQuestStatus(); void _SaveDailyQuestStatus(); void _SaveWeeklyQuestStatus(); + void _SaveMonthlyQuestStatus(); void _SaveSkills(); void _SaveSpells(); void _SaveEquipmentSets(); @@ -2565,6 +2572,7 @@ class MANGOS_DLL_SPEC Player : public Unit bool m_DailyQuestChanged; bool m_WeeklyQuestChanged; + bool m_MonthlyQuestChanged; uint32 m_drunkTimer; uint16 m_drunk; diff --git a/src/game/QuestDef.h b/src/game/QuestDef.h index f00b7f736..6d2a4b3f4 100644 --- a/src/game/QuestDef.h +++ b/src/game/QuestDef.h @@ -160,15 +160,17 @@ enum __QuestFlags QUEST_FLAGS_AUTO_ACCEPT = 0x00080000, // quests in starting areas // Mangos flags for set SpecialFlags in DB if required but used only at server - QUEST_MANGOS_FLAGS_REPEATABLE = 0x01000000, // Set by 1 in SpecialFlags from DB - QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT = 0x02000000, // Set by 2 in SpecialFlags from DB (if required area explore, spell SPELL_EFFECT_QUEST_COMPLETE casting, table `*_script` command SCRIPT_COMMAND_QUEST_EXPLORED use, set from script DLL) - QUEST_MANGOS_FLAGS_DB_ALLOWED = 0xFFFFFF | QUEST_MANGOS_FLAGS_REPEATABLE | QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT, + QUEST_MANGOS_FLAGS_REPEATABLE = 0x001000000, // Set by 1 in SpecialFlags from DB + QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT = 0x002000000, // Set by 2 in SpecialFlags from DB (if required area explore, spell SPELL_EFFECT_QUEST_COMPLETE casting, table `*_script` command SCRIPT_COMMAND_QUEST_EXPLORED use, set from script DLL) + QUEST_MANGOS_FLAGS_MONTHLY = 0x004000000, // 4 in SpecialFlags. Quest reset for player at beginning of month. + QUEST_MANGOS_FLAGS_DB_ALLOWED = 0xFFFFFF | + QUEST_MANGOS_FLAGS_REPEATABLE | QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT | QUEST_MANGOS_FLAGS_MONTHLY, // Mangos flags for internal use only - QUEST_MANGOS_FLAGS_DELIVER = 0x04000000, // Internal flag computed only - QUEST_MANGOS_FLAGS_SPEAKTO = 0x08000000, // Internal flag computed only - QUEST_MANGOS_FLAGS_KILL_OR_CAST = 0x10000000, // Internal flag computed only - QUEST_MANGOS_FLAGS_TIMED = 0x20000000, // Internal flag computed only + QUEST_MANGOS_FLAGS_DELIVER = 0x008000000, // Internal flag computed only + QUEST_MANGOS_FLAGS_SPEAKTO = 0x010000000, // Internal flag computed only + QUEST_MANGOS_FLAGS_KILL_OR_CAST = 0x020000000, // Internal flag computed only + QUEST_MANGOS_FLAGS_TIMED = 0x040000000, // Internal flag computed only }; struct QuestLocale @@ -257,6 +259,7 @@ class Quest uint32 GetFlags() const { return QuestFlags; } bool IsDaily() const { return QuestFlags & QUEST_FLAGS_DAILY; } bool IsWeekly() const { return QuestFlags & QUEST_FLAGS_WEEKLY; } + bool IsMonthly() const { return QuestFlags & QUEST_MANGOS_FLAGS_MONTHLY; } bool IsDailyOrWeekly() const { return QuestFlags & (QUEST_FLAGS_DAILY | QUEST_FLAGS_WEEKLY); } bool IsAutoAccept() const { return QuestFlags & QUEST_FLAGS_AUTO_ACCEPT; } bool IsAllowedInRaid() const; diff --git a/src/game/World.cpp b/src/game/World.cpp index 4fb965c6f..1df858939 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -1311,6 +1311,9 @@ void World::SetInitialWorldSettings() sLog.outString("Calculate next weekly quest reset time..." ); InitWeeklyQuestResetTime(); + sLog.outString("Calculate next monthly quest reset time..." ); + SetMonthlyQuestResetTime(); + sLog.outString("Starting objects Pooling system..." ); sPoolMgr.Initialize(); @@ -1395,6 +1398,10 @@ void World::Update(uint32 diff) if (m_gameTime > m_NextWeeklyQuestReset) ResetWeeklyQuests(); + /// Handle monthly quests reset time + if (m_gameTime > m_NextMonthlyQuestReset) + ResetMonthlyQuests(); + ///