From 2251d1bfae88b8f7d18d4111929f4bb03f4a4131 Mon Sep 17 00:00:00 2001 From: Spp Date: Tue, 27 Nov 2012 10:23:56 +0100 Subject: Core/Misc: Set mode 0644 for files --- src/server/shared/Database/Implementation/LoginDatabase.cpp | 0 src/server/shared/Database/Implementation/LoginDatabase.h | 0 src/server/shared/Database/Implementation/WorldDatabase.cpp | 0 src/server/shared/Database/Implementation/WorldDatabase.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/server/shared/Database/Implementation/LoginDatabase.cpp mode change 100755 => 100644 src/server/shared/Database/Implementation/LoginDatabase.h mode change 100755 => 100644 src/server/shared/Database/Implementation/WorldDatabase.cpp mode change 100755 => 100644 src/server/shared/Database/Implementation/WorldDatabase.h (limited to 'src/server/shared/Database/Implementation') diff --git a/src/server/shared/Database/Implementation/LoginDatabase.cpp b/src/server/shared/Database/Implementation/LoginDatabase.cpp old mode 100755 new mode 100644 diff --git a/src/server/shared/Database/Implementation/LoginDatabase.h b/src/server/shared/Database/Implementation/LoginDatabase.h old mode 100755 new mode 100644 diff --git a/src/server/shared/Database/Implementation/WorldDatabase.cpp b/src/server/shared/Database/Implementation/WorldDatabase.cpp old mode 100755 new mode 100644 diff --git a/src/server/shared/Database/Implementation/WorldDatabase.h b/src/server/shared/Database/Implementation/WorldDatabase.h old mode 100755 new mode 100644 -- cgit v1.2.3 From d6aee0862147e41764d35f982fc387d780c42c5b Mon Sep 17 00:00:00 2001 From: Spp Date: Fri, 30 Nov 2012 13:59:01 +0100 Subject: Core/Misc: Include character level in log when a character is being deleted --- src/server/game/Handlers/CharacterHandler.cpp | 14 ++++++++------ .../shared/Database/Implementation/CharacterDatabase.cpp | 2 +- .../shared/Database/Implementation/CharacterDatabase.h | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) (limited to 'src/server/shared/Database/Implementation') diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 51cf1486213..005e7679a4e 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -683,6 +683,7 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) return; uint32 accountId = 0; + uint8 level = 0; std::string name; // is guild leader @@ -703,14 +704,15 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) return; } - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_NAME_BY_GUID); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_DATA_BY_GUID); stmt->setUInt32(0, GUID_LOPART(guid)); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) { Field* fields = result->Fetch(); - accountId = fields[0].GetUInt32(); - name = fields[1].GetString(); + accountId = fields[0].GetUInt32(); + name = fields[1].GetString(); + level = fields[2].GetUInt8(); } // prevent deleting other players' characters using cheating tools @@ -718,7 +720,7 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) return; std::string IP_str = GetRemoteAddress(); - sLog->outInfo(LOG_FILTER_CHARACTER, "Account: %d (IP: %s) Delete Character:[%s] (GUID: %u)", GetAccountId(), IP_str.c_str(), name.c_str(), GUID_LOPART(guid)); + sLog->outInfo(LOG_FILTER_CHARACTER, "Account: %d, IP: %s deleted character: %s, GUID: %u, Level: %u", accountId, IP_str.c_str(), name.c_str(), GUID_LOPART(guid), level); sScriptMgr->OnPlayerDelete(guid); sWorld->DeleteCharaceterNameData(GUID_LOPART(guid)); @@ -726,11 +728,11 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) { std::string dump; if (PlayerDumpWriter().GetDump(GUID_LOPART(guid), dump)) - sLog->outCharDump(dump.c_str(), GetAccountId(), GUID_LOPART(guid), name.c_str()); + sLog->outCharDump(dump.c_str(), accountId, GUID_LOPART(guid), name.c_str()); } sCalendarMgr->RemoveAllPlayerEventsAndInvites(guid); - Player::DeleteFromDB(guid, GetAccountId()); + Player::DeleteFromDB(guid, accountId); WorldPacket data(SMSG_CHAR_DELETE, 1); data << uint8(CHAR_DELETE_SUCCESS); diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index 5210ee6f39d..9c5db00ad49 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -140,7 +140,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PREPARE_STATEMENT(CHAR_SEL_CHARACTER_GIFT_BY_ITEM, "SELECT entry, flags FROM character_gifts WHERE item_guid = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_SEL_ACCOUNT_BY_NAME, "SELECT account FROM characters WHERE name = ?", CONNECTION_SYNCH) PREPARE_STATEMENT(CHAR_SEL_ACCOUNT_BY_GUID, "SELECT account FROM characters WHERE guid = ?", CONNECTION_SYNCH); - PREPARE_STATEMENT(CHAR_SEL_ACCOUNT_NAME_BY_GUID, "SELECT account, name FROM characters WHERE guid = ?", CONNECTION_SYNCH); + PREPARE_STATEMENT(CHAR_SEL_CHARACTER_DATA_BY_GUID, "SELECT account, name, level FROM characters WHERE guid = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES, "DELETE FROM account_instance_times WHERE accountId = ?", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES, "INSERT INTO account_instance_times (accountId, instanceId, releaseTime) VALUES (?, ?, ?)", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_SEL_CHARACTER_NAME_CLASS, "SELECT name, class FROM characters WHERE guid = ?", CONNECTION_SYNCH); diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h index 7e4fe6a0b0e..5ce0639e59b 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.h +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h @@ -157,7 +157,7 @@ enum CharacterDatabaseStatements CHAR_SEL_CHARACTER_COUNT, CHAR_UPD_NAME, CHAR_DEL_DECLINED_NAME, - CHAR_SEL_ACCOUNT_NAME_BY_GUID, + CHAR_SEL_CHARACTER_DATA_BY_GUID, CHAR_INS_GUILD, CHAR_DEL_GUILD, -- cgit v1.2.3 From b2928e59fa8334e91dfa1a5951fade5b574b5842 Mon Sep 17 00:00:00 2001 From: Vincent-Michael Date: Tue, 4 Dec 2012 00:02:06 +0100 Subject: Core/Quest: Add support for monthly quests Author: svetilo12 Converted and updated by: Subv --- ..._00_character_character_queststatus_monthly.sql | 8 ++ src/server/game/Entities/Player/Player.cpp | 144 +++++++++++++++------ src/server/game/Entities/Player/Player.h | 72 ++++++----- src/server/game/Globals/ObjectMgr.cpp | 9 ++ src/server/game/Handlers/CharacterHandler.cpp | 72 ++++++----- src/server/game/Quests/QuestDef.h | 6 +- src/server/game/World/World.cpp | 68 ++++++++++ src/server/game/World/World.h | 10 +- .../Database/Implementation/CharacterDatabase.cpp | 4 + .../Database/Implementation/CharacterDatabase.h | 4 + 10 files changed, 289 insertions(+), 108 deletions(-) create mode 100644 sql/updates/characters/2012_12_03_00_character_character_queststatus_monthly.sql (limited to 'src/server/shared/Database/Implementation') diff --git a/sql/updates/characters/2012_12_03_00_character_character_queststatus_monthly.sql b/sql/updates/characters/2012_12_03_00_character_character_queststatus_monthly.sql new file mode 100644 index 00000000000..256bb1f7ad6 --- /dev/null +++ b/sql/updates/characters/2012_12_03_00_character_character_queststatus_monthly.sql @@ -0,0 +1,8 @@ +DROP TABLE IF EXISTS `character_queststatus_monthly`; + +CREATE TABLE IF NOT EXISTS `character_queststatus_monthly` ( + `guid` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Global Unique Identifier', + `quest` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Quest Identifier', + PRIMARY KEY (`guid`,`quest`), + KEY `idx_guid` (`guid`) +) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='Player System'; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 3a562ca09b1..6c3434df14f 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -14705,11 +14705,11 @@ Quest const* Player::GetNextQuest(uint64 guid, Quest const* quest) bool Player::CanSeeStartQuest(Quest const* quest) { - if (SatisfyQuestClass(quest, false) && SatisfyQuestRace(quest, false) && SatisfyQuestSkill(quest, false) && - SatisfyQuestExclusiveGroup(quest, false) && SatisfyQuestReputation(quest, false) && + if (!DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this) && SatisfyQuestClass(quest, false) && SatisfyQuestRace(quest, false) && + SatisfyQuestSkill(quest, false) && SatisfyQuestExclusiveGroup(quest, false) && SatisfyQuestReputation(quest, false) && SatisfyQuestPreviousQuest(quest, false) && SatisfyQuestNextChain(quest, false) && SatisfyQuestPrevChain(quest, false) && SatisfyQuestDay(quest, false) && SatisfyQuestWeek(quest, false) && - SatisfyQuestSeasonal(quest, false) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this)) + SatisfyQuestMonth(quest, false) && SatisfyQuestSeasonal(quest, false)) { return getLevel() + sWorld->getIntConfig(CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF) >= quest->GetMinLevel(); } @@ -14726,7 +14726,7 @@ bool Player::CanTakeQuest(Quest const* quest, bool msg) && SatisfyQuestPreviousQuest(quest, msg) && SatisfyQuestTimed(quest, msg) && SatisfyQuestNextChain(quest, msg) && SatisfyQuestPrevChain(quest, msg) && SatisfyQuestDay(quest, msg) && SatisfyQuestWeek(quest, msg) - && SatisfyQuestSeasonal(quest,msg) + && SatisfyQuestMonth(quest, msg) && SatisfyQuestSeasonal(quest, msg) && SatisfyQuestConditions(quest, msg); } @@ -14850,7 +14850,7 @@ bool Player::CanRewardQuest(Quest const* quest, bool msg) return false; // daily quest can't be rewarded (25 daily quest already completed) - if (!SatisfyQuestDay(quest, true) || !SatisfyQuestWeek(quest, true) || !SatisfyQuestSeasonal(quest,true)) + if (!SatisfyQuestDay(quest, true) || !SatisfyQuestWeek(quest, true) || !SatisfyQuestMonth(quest, true) || !SatisfyQuestSeasonal(quest, true)) return false; // rewarded and not repeatable quest (only cheating case, then ignore without message) @@ -15148,6 +15148,8 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, } else if (quest->IsWeekly()) SetWeeklyQuestStatus(quest_id); + else if (quest->IsMonthly()) + SetMonthlyQuestStatus(quest_id); else if (quest->IsSeasonal()) SetSeasonalQuestStatus(quest_id); @@ -15601,6 +15603,15 @@ bool Player::SatisfyQuestSeasonal(Quest const* qInfo, bool /*msg*/) return m_seasonalquests[eventId].find(qInfo->GetQuestId()) == m_seasonalquests[eventId].end(); } +bool Player::SatisfyQuestMonth(Quest const* qInfo, bool /*msg*/) +{ + 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::GiveQuestSourceItem(Quest const* quest) { uint32 srcitem = quest->GetSrcItemId(); @@ -16664,8 +16675,7 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) //"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, " // 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 //"health, power1, power2, power3, power4, power5, power6, power7, instance_id, speccount, activespec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars, grantableLevels FROM characters WHERE guid = '%u'", guid); - PreparedQueryResult result = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADFROM); - + PreparedQueryResult result = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM); if (!result) { sLog->outError(LOG_FILTER_PLAYER, "Player (GUID: %u) not found in table `characters`, can't load. ", guid); @@ -16684,7 +16694,7 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) return false; } - if (holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADBANNED)) + if (holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BANNED)) { sLog->outError(LOG_FILTER_PLAYER, "Player (GUID: %u) is banned, can't load.", guid); return false; @@ -16699,12 +16709,9 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) (AccountMgr::IsPlayerAccount(GetSession()->GetSecurity()) && sObjectMgr->IsReservedName(m_name))) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); - stmt->setUInt16(0, uint16(AT_LOGIN_RENAME)); stmt->setUInt32(1, guid); - CharacterDatabase.Execute(stmt); - return false; } @@ -16736,7 +16743,7 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); // load achievements before anything else to prevent multiple gains for the same achievement/criteria on every loading (as loading does call UpdateAchievementCriteria) - m_achievementMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS)); + m_achievementMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS)); uint32 money = fields[8].GetUInt32(); if (money > MAX_MONEY_AMOUNT) @@ -16777,7 +16784,7 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) setFactionForRace(getRace()); // load home bind and check in same time class/race pair, it used later for restore broken positions - if (!_LoadHomeBind(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND))) + if (!_LoadHomeBind(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_HOME_BIND))) return false; InitPrimaryProfessions(); // to max set before any spell loaded @@ -16801,9 +16808,9 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) #define RelocateToHomebind(){ mapId = m_homebindMapId; instanceId = 0; Relocate(m_homebindX, m_homebindY, m_homebindZ); } - _LoadGroup(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADGROUP)); + _LoadGroup(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GROUP)); - _LoadArenaTeamInfo(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADARENAINFO)); + _LoadArenaTeamInfo(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ARENA_INFO)); SetArenaPoints(fields[39].GetUInt32()); @@ -16830,9 +16837,9 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) SetUInt16Value(PLAYER_FIELD_KILLS, 0, fields[44].GetUInt16()); SetUInt16Value(PLAYER_FIELD_KILLS, 1, fields[45].GetUInt16()); - _LoadBoundInstances(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES)); - _LoadInstanceTimeRestrictions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADINSTANCELOCKTIMES)); - _LoadBGData(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADBGDATA)); + _LoadBoundInstances(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BOUND_INSTANCES)); + _LoadInstanceTimeRestrictions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES)); + _LoadBGData(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BG_DATA)); GetSession()->SetPlayer(this); MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); @@ -17141,7 +17148,7 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) } // load skills after InitStatsForLevel because it triggering aura apply also - _LoadSkills(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSKILLS)); + _LoadSkills(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SKILLS)); UpdateSkillsForLevel(); //update skills after load, to make sure they are correctly update at player load // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods() @@ -17159,42 +17166,43 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) sLog->outError(LOG_FILTER_PLAYER, "Player %s(GUID: %u) has SpecCount = %u and ActiveSpec = %u.", GetName().c_str(), GetGUIDLow(), m_specsCount, m_activeSpec); } - _LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADTALENTS)); - _LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSPELLS)); + _LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS)); + _LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS)); - _LoadGlyphs(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADGLYPHS)); - _LoadAuras(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADAURAS), time_diff); + _LoadGlyphs(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GLYPHS)); + _LoadAuras(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURAS), time_diff); _LoadGlyphAuras(); // add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura) if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) m_deathState = DEAD; // after spell load, learn rewarded spell if need also - _LoadQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS)); - _LoadQuestStatusRewarded(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUSREW)); - _LoadDailyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS)); - _LoadWeeklyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS)); - _LoadSeasonalQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSEASONALQUESTSTATUS)); - _LoadRandomBGStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADRANDOMBG)); + _LoadQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS)); + _LoadQuestStatusRewarded(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS_REW)); + _LoadDailyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DAILY_QUEST_STATUS)); + _LoadWeeklyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_WEEKLY_QUEST_STATUS)); + _LoadSeasonalQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SEASONAL_QUEST_STATUS)); + _LoadMonthlyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS)); + _LoadRandomBGStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_RANDOM_BG)); // after spell and quest load InitTalentForLevel(); learnDefaultSpells(); // must be before inventory (some items required reputation check) - m_reputationMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADREPUTATION)); + m_reputationMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION)); - _LoadInventory(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADINVENTORY), time_diff); + _LoadInventory(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY), time_diff); // update items with duration and realtime UpdateItemDuration(time_diff, true); - _LoadActions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADACTIONS)); + _LoadActions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACTIONS)); // unread mails and next delivery time, actual mails not loaded - _LoadMailInit(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADMAILCOUNT), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADMAILDATE)); + _LoadMailInit(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE)); - m_social = sSocialMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSOCIALLIST), GetGUIDLow()); + m_social = sSocialMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SOCIAL_LIST), GetGUIDLow()); // check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES // note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded @@ -17207,7 +17215,7 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) // has to be called after last Relocate() in Player::LoadFromDB SetFallInformation(0, GetPositionZ()); - _LoadSpellCooldowns(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS)); + _LoadSpellCooldowns(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS)); // Spell code allow apply any auras to dead character in load time in aura/spell/item loading // Do now before stats re-calculation cleanup for ghost state unexpected auras @@ -17288,11 +17296,11 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) if (m_grantableLevels > 0) SetByteValue(PLAYER_FIELD_BYTES, 1, 0x01); - _LoadDeclinedNames(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES)); + _LoadDeclinedNames(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES)); m_achievementMgr->CheckAllAchievementCriteria(); - _LoadEquipmentSets(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS)); + _LoadEquipmentSets(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS)); return true; } @@ -18102,6 +18110,29 @@ void Player::_LoadSeasonalQuestStatus(PreparedQueryResult result) m_SeasonalQuestChanged = false; } +void Player::_LoadMonthlyQuestStatus(PreparedQueryResult result) +{ + m_monthlyquests.clear(); + + if (result) + { + do + { + Field* fields = result->Fetch(); + uint32 quest_id = fields[0].GetUInt32(); + Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id); + if (!quest) + continue; + + m_monthlyquests.insert(quest_id); + sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Monthly quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow()); + } + while (result->NextRow()); + } + + m_MonthlyQuestChanged = false; +} + void Player::_LoadSpells(PreparedQueryResult result) { //QueryResult* result = CharacterDatabase.PQuery("SELECT spell, active, disabled FROM character_spell WHERE guid = '%u'", GetGUIDLow()); @@ -18842,6 +18873,7 @@ void Player::SaveToDB(bool create /*=false*/) _SaveDailyQuestStatus(trans); _SaveWeeklyQuestStatus(trans); _SaveSeasonalQuestStatus(trans); + _SaveMonthlyQuestStatus(trans); _SaveTalents(trans); _SaveSpells(trans); _SaveSpellCooldowns(trans); @@ -19344,6 +19376,28 @@ void Player::_SaveSeasonalQuestStatus(SQLTransaction& trans) m_SeasonalQuestChanged = false; } +void Player::_SaveMonthlyQuestStatus(SQLTransaction& trans) +{ + if (!m_MonthlyQuestChanged || m_monthlyquests.empty()) + return; + + // we don't need transactions here. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_MONTHLY_CHAR); + stmt->setUInt32(0, GetGUIDLow()); + trans->Append(stmt); + + for (QuestSet::const_iterator iter = m_monthlyquests.begin(); iter != m_monthlyquests.end(); ++iter) + { + uint32 quest_id = *iter; + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_MONTHLYQUESTSTATUS); + stmt->setUInt32(0, GetGUIDLow()); + stmt->setUInt32(1, quest_id); + trans->Append(stmt); + } + + m_MonthlyQuestChanged = false; +} + void Player::_SaveSkills(SQLTransaction& trans) { PreparedStatement* stmt = NULL; @@ -22526,6 +22580,12 @@ void Player::SetSeasonalQuestStatus(uint32 quest_id) m_SeasonalQuestChanged = 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) @@ -22558,6 +22618,16 @@ void Player::ResetSeasonalQuestStatus(uint16 event_id) m_SeasonalQuestChanged = 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/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 4cd90f27875..c4b7deca4dd 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -763,38 +763,39 @@ enum PlayedTimeIndex // used at player loading query list preparing, and later result selection enum PlayerLoginQueryIndex { - PLAYER_LOGIN_QUERY_LOADFROM = 0, - PLAYER_LOGIN_QUERY_LOADGROUP = 1, - PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES = 2, - PLAYER_LOGIN_QUERY_LOADAURAS = 3, - PLAYER_LOGIN_QUERY_LOADSPELLS = 4, - PLAYER_LOGIN_QUERY_LOADQUESTSTATUS = 5, - PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS = 6, - PLAYER_LOGIN_QUERY_LOADREPUTATION = 7, - PLAYER_LOGIN_QUERY_LOADINVENTORY = 8, - PLAYER_LOGIN_QUERY_LOADACTIONS = 9, - PLAYER_LOGIN_QUERY_LOADMAILCOUNT = 10, - PLAYER_LOGIN_QUERY_LOADMAILDATE = 11, - PLAYER_LOGIN_QUERY_LOADSOCIALLIST = 12, - PLAYER_LOGIN_QUERY_LOADHOMEBIND = 13, - PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS = 14, - PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES = 15, - PLAYER_LOGIN_QUERY_LOADGUILD = 16, - PLAYER_LOGIN_QUERY_LOADARENAINFO = 17, - PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS = 18, - PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS = 19, - PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS = 20, - PLAYER_LOGIN_QUERY_LOADBGDATA = 21, - PLAYER_LOGIN_QUERY_LOADGLYPHS = 22, - PLAYER_LOGIN_QUERY_LOADTALENTS = 23, - PLAYER_LOGIN_QUERY_LOADACCOUNTDATA = 24, - PLAYER_LOGIN_QUERY_LOADSKILLS = 25, - PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS = 26, - PLAYER_LOGIN_QUERY_LOADRANDOMBG = 27, - PLAYER_LOGIN_QUERY_LOADBANNED = 28, - PLAYER_LOGIN_QUERY_LOADQUESTSTATUSREW = 29, - PLAYER_LOGIN_QUERY_LOADINSTANCELOCKTIMES = 30, - PLAYER_LOGIN_QUERY_LOADSEASONALQUESTSTATUS = 31, + PLAYER_LOGIN_QUERY_LOAD_FROM = 0, + PLAYER_LOGIN_QUERY_LOAD_GROUP = 1, + PLAYER_LOGIN_QUERY_LOAD_BOUND_INSTANCES = 2, + PLAYER_LOGIN_QUERY_LOAD_AURAS = 3, + PLAYER_LOGIN_QUERY_LOAD_SPELLS = 4, + PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS = 5, + PLAYER_LOGIN_QUERY_LOAD_DAILY_QUEST_STATUS = 6, + PLAYER_LOGIN_QUERY_LOAD_REPUTATION = 7, + PLAYER_LOGIN_QUERY_LOAD_INVENTORY = 8, + PLAYER_LOGIN_QUERY_LOAD_ACTIONS = 9, + PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT = 10, + PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE = 11, + PLAYER_LOGIN_QUERY_LOAD_SOCIAL_LIST = 12, + PLAYER_LOGIN_QUERY_LOAD_HOME_BIND = 13, + PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS = 14, + PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES = 15, + PLAYER_LOGIN_QUERY_LOAD_GUILD = 16, + PLAYER_LOGIN_QUERY_LOAD_ARENA_INFO = 17, + PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS = 18, + PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS = 19, + PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS = 20, + PLAYER_LOGIN_QUERY_LOAD_BG_DATA = 21, + PLAYER_LOGIN_QUERY_LOAD_GLYPHS = 22, + PLAYER_LOGIN_QUERY_LOAD_TALENTS = 23, + PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_DATA = 24, + PLAYER_LOGIN_QUERY_LOAD_SKILLS = 25, + PLAYER_LOGIN_QUERY_LOAD_WEEKLY_QUEST_STATUS = 26, + PLAYER_LOGIN_QUERY_LOAD_RANDOM_BG = 27, + PLAYER_LOGIN_QUERY_LOAD_BANNED = 28, + PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS_REW = 29, + PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES = 30, + PLAYER_LOGIN_QUERY_LOAD_SEASONAL_QUEST_STATUS = 31, + PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS = 32, MAX_PLAYER_LOGIN_QUERY }; @@ -1397,6 +1398,7 @@ class Player : public Unit, public GridObject bool SatisfyQuestPrevChain(Quest const* qInfo, bool msg); bool SatisfyQuestDay(Quest const* qInfo, bool msg); bool SatisfyQuestWeek(Quest const* qInfo, bool msg); + bool SatisfyQuestMonth(Quest const* qInfo, bool msg); bool SatisfyQuestSeasonal(Quest const* qInfo, bool msg); bool GiveQuestSourceItem(Quest const* quest); bool TakeQuestSourceItem(uint32 questId, bool msg); @@ -1408,9 +1410,11 @@ class Player : public Unit, public GridObject void SetDailyQuestStatus(uint32 quest_id); void SetWeeklyQuestStatus(uint32 quest_id); + void SetMonthlyQuestStatus(uint32 quest_id); void SetSeasonalQuestStatus(uint32 quest_id); void ResetDailyQuestStatus(); void ResetWeeklyQuestStatus(); + void ResetMonthlyQuestStatus(); void ResetSeasonalQuestStatus(uint16 event_id); uint16 FindQuestSlot(uint32 quest_id) const; @@ -2552,6 +2556,7 @@ class Player : public Unit, public GridObject typedef UNORDERED_MAP SeasonalEventQuestMap; QuestSet m_timedquests; QuestSet m_weeklyquests; + QuestSet m_monthlyquests; SeasonalEventQuestMap m_seasonalquests; uint64 m_divider; @@ -2573,6 +2578,7 @@ class Player : public Unit, public GridObject void _LoadQuestStatusRewarded(PreparedQueryResult result); void _LoadDailyQuestStatus(PreparedQueryResult result); void _LoadWeeklyQuestStatus(PreparedQueryResult result); + void _LoadMonthlyQuestStatus(PreparedQueryResult result); void _LoadSeasonalQuestStatus(PreparedQueryResult result); void _LoadRandomBGStatus(PreparedQueryResult result); void _LoadGroup(PreparedQueryResult result); @@ -2599,6 +2605,7 @@ class Player : public Unit, public GridObject void _SaveQuestStatus(SQLTransaction& trans); void _SaveDailyQuestStatus(SQLTransaction& trans); void _SaveWeeklyQuestStatus(SQLTransaction& trans); + void _SaveMonthlyQuestStatus(SQLTransaction& trans); void _SaveSeasonalQuestStatus(SQLTransaction& trans); void _SaveSkills(SQLTransaction& trans); void _SaveSpells(SQLTransaction& trans); @@ -2708,6 +2715,7 @@ class Player : public Unit, public GridObject bool m_DailyQuestChanged; bool m_WeeklyQuestChanged; + bool m_MonthlyQuestChanged; bool m_SeasonalQuestChanged; time_t m_lastDailyQuestTime; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 64ce078bfef..b91207c9413 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -3730,6 +3730,15 @@ void ObjectMgr::LoadQuests() } } + if (qinfo->Flags & QUEST_TRINITY_FLAGS_MONTHLY) + { + if (!(qinfo->Flags & QUEST_TRINITY_FLAGS_REPEATABLE)) + { + sLog->outError(LOG_FILTER_SQL, "Monthly quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId()); + qinfo->Flags |= QUEST_TRINITY_FLAGS_REPEATABLE; + } + } + if (qinfo->Flags & QUEST_FLAGS_AUTO_REWARDED) { // at auto-reward can be rewarded only RewardChoiceItemId[0] diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 005e7679a4e..0e9ad902d8f 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -71,135 +71,139 @@ bool LoginQueryHolder::Initialize() stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADFROM, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_FROM, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GROUP_MEMBER); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADGROUP, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_GROUP, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_INSTANCE); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_BOUND_INSTANCES, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_AURAS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADAURAS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_AURAS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SPELL); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADSPELLS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SPELLS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_QUESTSTATUS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_DAILYQUESTSTATUS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_DAILY_QUEST_STATUS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_WEEKLYQUESTSTATUS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_WEEKLY_QUEST_STATUS, stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MONTHLYQUESTSTATUS); + stmt->setUInt32(0, lowGuid); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SEASONALQUESTSTATUS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADSEASONALQUESTSTATUS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SEASONAL_QUEST_STATUS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_REPUTATION); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_REPUTATION, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_INVENTORY); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADINVENTORY, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_INVENTORY, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACTIONS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ACTIONS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILCOUNT); stmt->setUInt32(0, lowGuid); stmt->setUInt64(1, uint64(time(NULL))); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADMAILCOUNT, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILDATE); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADMAILDATE, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SOCIALLIST); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SOCIAL_LIST, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_HOMEBIND); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADHOMEBIND, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_HOME_BIND, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS, stmt); if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED)) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_DECLINEDNAMES); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES, stmt); } stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUILD_MEMBER); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADGUILD, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_GUILD, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ARENAINFO); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADARENAINFO, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ARENA_INFO, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACHIEVEMENTS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_CRITERIAPROGRESS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_EQUIPMENTSETS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_BGDATA); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADBGDATA, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_BG_DATA, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GLYPHS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADGLYPHS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_GLYPHS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_TALENTS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_TALENTS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYER_ACCOUNT_DATA); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_DATA, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SKILLS); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SKILLS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_RANDOMBG); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADRANDOMBG, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_RANDOM_BG, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_BANNED); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADBANNED, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_BANNED, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_QUESTSTATUSREW); stmt->setUInt32(0, lowGuid); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADQUESTSTATUSREW, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS_REW, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_INSTANCELOCKTIMES); stmt->setUInt32(0, m_accountId); - res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADINSTANCELOCKTIMES, stmt); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES, stmt); return res; } @@ -803,7 +807,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) SendPacket(&data); // load player specific part before send times - LoadAccountData(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA), PER_CHARACTER_CACHE_MASK); + LoadAccountData(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_DATA), PER_CHARACTER_CACHE_MASK); SendAccountDataTimes(PER_CHARACTER_CACHE_MASK); data.Initialize(SMSG_FEATURE_SYSTEM_STATUS, 2); // added in 2.2.0 @@ -850,7 +854,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) } //QueryResult* result = CharacterDatabase.PQuery("SELECT guildid, rank FROM guild_member WHERE guid = '%u'", pCurrChar->GetGUIDLow()); - if (PreparedQueryResult resultGuild = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADGUILD)) + if (PreparedQueryResult resultGuild = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GUILD)) { Field* fields = resultGuild->Fetch(); pCurrChar->SetInGuild(fields[0].GetUInt32()); diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h index 58734751cea..ffb0cf02a0a 100644 --- a/src/server/game/Quests/QuestDef.h +++ b/src/server/game/Quests/QuestDef.h @@ -120,7 +120,7 @@ enum __QuestGiverStatus DIALOG_STATUS_REWARD = 10 // yellow dot on minimap }; -enum __QuestFlags +enum QuestFlags { // Flags used at server and sent to client QUEST_FLAGS_NONE = 0x00000000, @@ -150,8 +150,9 @@ enum __QuestFlags QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT = 0x00200000, // Set by 2 in SpecialFlags from DB (if reequired area explore, spell SPELL_EFFECT_QUEST_COMPLETE casting, table `*_script` command SCRIPT_COMMAND_QUEST_EXPLORED use, set from script) QUEST_TRINITY_FLAGS_AUTO_ACCEPT = 0x00400000, // Set by 4 in SpecialFlags in DB if the quest is to be auto-accepted. QUEST_TRINITY_FLAGS_DF_QUEST = 0x00800000, // Set by 8 in SpecialFlags in DB if the quest is used by Dungeon Finder. + QUEST_TRINITY_FLAGS_MONTHLY = 0x01000000, // Set by 16 in SpecialFlags in DB if the quest is reset at the begining of the month - QUEST_TRINITY_FLAGS_DB_ALLOWED = 0xFFFFF | QUEST_TRINITY_FLAGS_REPEATABLE | QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT | QUEST_TRINITY_FLAGS_AUTO_ACCEPT | QUEST_TRINITY_FLAGS_DF_QUEST, + QUEST_TRINITY_FLAGS_DB_ALLOWED = 0xFFFFF | QUEST_TRINITY_FLAGS_REPEATABLE | QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT | QUEST_TRINITY_FLAGS_AUTO_ACCEPT | QUEST_TRINITY_FLAGS_DF_QUEST | QUEST_TRINITY_FLAGS_MONTHLY, // Trinity flags for internal use only QUEST_TRINITY_FLAGS_DELIVER = 0x04000000, // Internal flag computed only @@ -251,6 +252,7 @@ class Quest uint32 GetFlags() const { return Flags; } bool IsDaily() const { return Flags & QUEST_FLAGS_DAILY; } bool IsWeekly() const { return Flags & QUEST_FLAGS_WEEKLY; } + bool IsMonthly() const { return Flags & QUEST_TRINITY_FLAGS_MONTHLY; } bool IsSeasonal() const { return (ZoneOrSort == -QUEST_SORT_SEASONAL || ZoneOrSort == -QUEST_SORT_SPECIAL || ZoneOrSort == -QUEST_SORT_LUNAR_FESTIVAL || ZoneOrSort == -QUEST_SORT_MIDSUMMER || ZoneOrSort == -QUEST_SORT_BREWFEST || ZoneOrSort == -QUEST_SORT_LOVE_IS_IN_THE_AIR || ZoneOrSort == -QUEST_SORT_NOBLEGARDEN) && !IsRepeatable(); } bool IsDailyOrWeekly() const { return Flags & (QUEST_FLAGS_DAILY | QUEST_FLAGS_WEEKLY); } bool IsRaidQuest() const { return Type == QUEST_TYPE_RAID || Type == QUEST_TYPE_RAID_10 || Type == QUEST_TYPE_RAID_25; } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 5a776126d33..7f5c6b3fead 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1772,6 +1772,9 @@ void World::SetInitialWorldSettings() sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Calculate next weekly quest reset time..."); InitWeeklyQuestResetTime(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Calculate next monthly quest reset time..."); + InitMonthlyQuestResetTime(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Calculate random battleground reset time..."); InitRandomBGResetTime(); @@ -1927,9 +1930,14 @@ void World::Update(uint32 diff) m_NextDailyQuestReset += DAY; } + /// Handle weekly quests reset time if (m_gameTime > m_NextWeeklyQuestReset) ResetWeeklyQuests(); + /// Handle monthly quests reset time + if (m_gameTime > m_NextMonthlyQuestReset) + ResetMonthlyQuests(); + if (m_gameTime > m_NextRandomBGReset) ResetRandomBG(); @@ -2719,6 +2727,13 @@ void World::InitDailyQuestResetTime() m_NextDailyQuestReset = (curTime >= curDayResetTime) ? curDayResetTime + DAY : curDayResetTime; } +void World::InitMonthlyQuestResetTime() +{ + time_t wstime = uint64(sWorld->getWorldState(WS_MONTHLY_QUEST_RESET_TIME)); + time_t curtime = time(NULL); + m_NextMonthlyQuestReset = wstime < curtime ? curtime : time_t(wstime); +} + void World::InitRandomBGResetTime() { time_t bgtime = uint64(sWorld->getWorldState(WS_BG_DAILY_RESET_TIME)); @@ -2809,6 +2824,8 @@ void World::SetPlayerSecurityLimit(AccountTypes _sec) void World::ResetWeeklyQuests() { + sLog->outInfo(LOG_FILTER_GENERAL, "Weekly quests reset for all characters."); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_WEEKLY); CharacterDatabase.Execute(stmt); @@ -2823,6 +2840,57 @@ void World::ResetWeeklyQuests() sPoolMgr->ChangeWeeklyQuests(); } +void World::ResetMonthlyQuests() +{ + sLog->outInfo(LOG_FILTER_GENERAL, "Monthly quests reset for all characters."); + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_MONTHLY); + CharacterDatabase.Execute(stmt); + + for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) + if (itr->second->GetPlayer()) + itr->second->GetPlayer()->ResetMonthlyQuestStatus(); + + time_t mostRecentQuestTime = 0; + + // generate time + time_t curTime = time(NULL); + tm localTm = *localtime(&curTime); + + int month = localTm.tm_mon; + int year = localTm.tm_year; + + ++month; + + // month 11 is december, next is january (0) + if (month > 11) + { + month = 0; + year += 1; + } + + // reset time for next month + localTm.tm_year = year; + localTm.tm_mon = month; + localTm.tm_mday = 1; // don't know if we really need config option for day / hour + localTm.tm_hour = 0; + localTm.tm_min = 0; + localTm.tm_sec = 0; + + time_t nextMonthResetTime = mktime(&localTm); + + // last reset time before current moment + time_t resetTime = (curTime < nextMonthResetTime) ? nextMonthResetTime - MONTH : nextMonthResetTime; + + // need reset (if we have quest time before last reset time (not processed by some reason) + if (mostRecentQuestTime && mostRecentQuestTime <= resetTime) + m_NextMonthlyQuestReset = mostRecentQuestTime; + else // plan next reset time + m_NextMonthlyQuestReset = (curTime >= nextMonthResetTime) ? nextMonthResetTime + MONTH : nextMonthResetTime; + + sWorld->setWorldState(WS_MONTHLY_QUEST_RESET_TIME, uint64(m_NextMonthlyQuestReset)); +} + void World::ResetEventSeasonalQuests(uint16 event_id) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 40ef0d7a8e5..1ac9b153dfd 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -469,9 +469,10 @@ enum RealmZone enum WorldStates { - WS_WEEKLY_QUEST_RESET_TIME = 20002, // Next weekly reset time - WS_BG_DAILY_RESET_TIME = 20003, // Next daily BG reset time - WS_GUILD_DAILY_RESET_TIME = 20006, // Next guild cap reset time + WS_WEEKLY_QUEST_RESET_TIME = 20002, // Next weekly reset time + WS_BG_DAILY_RESET_TIME = 20003, // Next daily BG reset time + WS_MONTHLY_QUEST_RESET_TIME = 20004, // Next monthly reset time + WS_GUILD_DAILY_RESET_TIME = 20006, // Next guild cap reset time }; /// Storage class for commands issued for delayed execution @@ -742,10 +743,12 @@ class World void InitDailyQuestResetTime(); void InitWeeklyQuestResetTime(); + void InitMonthlyQuestResetTime(); void InitRandomBGResetTime(); void InitGuildResetTime(); void ResetDailyQuests(); void ResetWeeklyQuests(); + void ResetMonthlyQuests(); void ResetRandomBG(); void ResetGuildCap(); private: @@ -807,6 +810,7 @@ class World // next daily quests and random bg reset time time_t m_NextDailyQuestReset; time_t m_NextWeeklyQuestReset; + time_t m_NextMonthlyQuestReset; time_t m_NextRandomBGReset; time_t m_NextGuildReset; diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index 9c5db00ad49..33f0cab5170 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -54,9 +54,11 @@ void CharacterDatabaseConnection::DoPrepareStatements() PREPARE_STATEMENT(CHAR_SEL_CHAR_POSITION, "SELECT position_x, position_y, position_z, orientation, map, taxi_path FROM characters WHERE guid = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_DAILY, "DELETE FROM character_queststatus_daily", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_WEEKLY, "DELETE FROM character_queststatus_weekly", CONNECTION_ASYNC); + PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_MONTHLY, "DELETE FROM character_queststatus_monthly", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_SEASONAL, "DELETE FROM character_queststatus_seasonal WHERE event = ?", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_DAILY_CHAR, "DELETE FROM character_queststatus_daily WHERE guid = ?", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR, "DELETE FROM character_queststatus_weekly WHERE guid = ?", CONNECTION_ASYNC); + PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_MONTHLY_CHAR, "DELETE FROM character_queststatus_monthly WHERE guid = ?", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR, "DELETE FROM character_queststatus_seasonal WHERE guid = ?", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_DEL_BATTLEGROUND_RANDOM, "DELETE FROM character_battleground_random", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_INS_BATTLEGROUND_RANDOM, "INSERT INTO character_battleground_random (guid) VALUES (?)", CONNECTION_ASYNC); @@ -76,9 +78,11 @@ void CharacterDatabaseConnection::DoPrepareStatements() "itemcount1, itemcount2, itemcount3, itemcount4, playercount FROM character_queststatus WHERE guid = ? AND status <> 0", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_SEL_CHARACTER_DAILYQUESTSTATUS, "SELECT quest, time FROM character_queststatus_daily WHERE guid = ?", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_SEL_CHARACTER_WEEKLYQUESTSTATUS, "SELECT quest FROM character_queststatus_weekly WHERE guid = ?", CONNECTION_ASYNC) + PREPARE_STATEMENT(CHAR_SEL_CHARACTER_MONTHLYQUESTSTATUS, "SELECT quest FROM character_queststatus_monthly WHERE guid = ?", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_SEL_CHARACTER_SEASONALQUESTSTATUS, "SELECT quest, event FROM character_queststatus_seasonal WHERE guid = ?", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_INS_CHARACTER_DAILYQUESTSTATUS, "INSERT INTO character_queststatus_daily (guid, quest, time) VALUES (?, ?, ?)", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_INS_CHARACTER_WEEKLYQUESTSTATUS, "INSERT INTO character_queststatus_weekly (guid, quest) VALUES (?, ?)", CONNECTION_ASYNC) + PREPARE_STATEMENT(CHAR_INS_CHARACTER_MONTHLYQUESTSTATUS, "INSERT INTO character_queststatus_monthly (guid, quest) VALUES (?, ?)", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_INS_CHARACTER_SEASONALQUESTSTATUS, "INSERT INTO character_queststatus_seasonal (guid, quest, event) VALUES (?, ?, ?)", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_SEL_CHARACTER_REPUTATION, "SELECT faction, standing, flags FROM character_reputation WHERE guid = ?", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_SEL_CHARACTER_INVENTORY, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, bag, slot, " diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h index 5ce0639e59b..181161978df 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.h +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h @@ -74,9 +74,11 @@ enum CharacterDatabaseStatements CHAR_SEL_CHAR_POSITION, CHAR_DEL_QUEST_STATUS_DAILY, CHAR_DEL_QUEST_STATUS_WEEKLY, + CHAR_DEL_QUEST_STATUS_MONTHLY, CHAR_DEL_QUEST_STATUS_SEASONAL, CHAR_DEL_QUEST_STATUS_DAILY_CHAR, CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR, + CHAR_DEL_QUEST_STATUS_MONTHLY_CHAR, CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR, CHAR_DEL_BATTLEGROUND_RANDOM, CHAR_INS_BATTLEGROUND_RANDOM, @@ -89,9 +91,11 @@ enum CharacterDatabaseStatements CHAR_SEL_CHARACTER_QUESTSTATUS, CHAR_SEL_CHARACTER_DAILYQUESTSTATUS, CHAR_SEL_CHARACTER_WEEKLYQUESTSTATUS, + CHAR_SEL_CHARACTER_MONTHLYQUESTSTATUS, CHAR_SEL_CHARACTER_SEASONALQUESTSTATUS, CHAR_INS_CHARACTER_DAILYQUESTSTATUS, CHAR_INS_CHARACTER_WEEKLYQUESTSTATUS, + CHAR_INS_CHARACTER_MONTHLYQUESTSTATUS, CHAR_INS_CHARACTER_SEASONALQUESTSTATUS, CHAR_SEL_CHARACTER_REPUTATION, CHAR_SEL_CHARACTER_INVENTORY, -- cgit v1.2.3 From 04f08d26a72ab29b11fc2aaef65bc54078cc2086 Mon Sep 17 00:00:00 2001 From: MrSmite Date: Fri, 14 Dec 2012 00:46:36 -0500 Subject: Implements saving of loot (items / money) contained inside lootable inventory items. * Unlooted items / money persist across player sessions * Loot inside items is tied to the item rather than the player so if trading partially looted items becomes possible, this implementation will still work * New tables added: characters_database.sql (first time users) characters_create_item_loot.sql (existing users) Implementation Can be tested with: Watertight Trunk [21113] Bulging Sack of Gems [25422] Fat Sack of Coins [11937] Closes #2048 --- sql/base/characters_database.sql | 35 ++++ .../characters/characters_create_item_loot.sql | 52 ++++++ src/server/game/Entities/Item/Item.cpp | 196 +++++++++++++++++++++ src/server/game/Entities/Item/Item.h | 9 + src/server/game/Entities/Player/Player.cpp | 21 ++- src/server/game/Handlers/LootHandler.cpp | 16 +- src/server/game/Loot/LootMgr.cpp | 41 +++++ src/server/game/Loot/LootMgr.h | 16 +- .../Database/Implementation/CharacterDatabase.cpp | 9 + .../Database/Implementation/CharacterDatabase.h | 8 + 10 files changed, 398 insertions(+), 5 deletions(-) create mode 100644 sql/updates/characters/characters_create_item_loot.sql (limited to 'src/server/shared/Database/Implementation') diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index d8cdd409bf7..9e16db1f381 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -1954,6 +1954,41 @@ LOCK TABLES `item_instance` WRITE; /*!40000 ALTER TABLE `item_instance` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `item_loot_items` +-- + +DROP TABLE IF EXISTS `item_loot_items`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `item_loot_items` ( + `container_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'guid of container (item_instance.guid)', + `item_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'loot item entry (item_instance.itemEntry)', + `item_count` int(10) NOT NULL DEFAULT '0' COMMENT 'stack size', + `follow_rules` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'follow loot rules', + `ffa` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'free-for-all', + `blocked` tinyint(1) NOT NULL DEFAULT '0', + `counted` tinyint(1) NOT NULL DEFAULT '0', + `under_threshold` tinyint(1) NOT NULL DEFAULT '0', + `needs_quest` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'quest drop', + `rnd_prop` int(10) NOT NULL DEFAULT '0' COMMENT 'random enchantment added when originally rolled', + `rnd_suffix` int(10) NOT NULL DEFAULT '0' COMMENT 'random suffix added when originally rolled' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `item_loot_money` +-- + +DROP TABLE IF EXISTS `item_loot_money`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `item_loot_money` ( + `container_id` int(10) NOT NULL DEFAULT '0' COMMENT 'guid of container (item_instance.guid)', + `money` int(10) NOT NULL DEFAULT '0' COMMENT 'money loot (in copper)' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + -- -- Table structure for table `item_refund_instance` -- diff --git a/sql/updates/characters/characters_create_item_loot.sql b/sql/updates/characters/characters_create_item_loot.sql new file mode 100644 index 00000000000..3c1529e8bdf --- /dev/null +++ b/sql/updates/characters/characters_create_item_loot.sql @@ -0,0 +1,52 @@ +/* +SQLyog Ultimate v9.63 +MySQL - 5.5.21 : Database - characters +********************************************************************* +*/ + +/*!40101 SET NAMES utf8 */; + +/*!40101 SET SQL_MODE=''*/; + +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `item_loot_items` +-- + +DROP TABLE IF EXISTS `item_loot_items`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `item_loot_items` ( + `container_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'guid of container (item_instance.guid)', + `item_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'loot item entry (item_instance.itemEntry)', + `item_count` int(10) NOT NULL DEFAULT '0' COMMENT 'stack size', + `follow_rules` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'follow loot rules', + `ffa` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'free-for-all', + `blocked` tinyint(1) NOT NULL DEFAULT '0', + `counted` tinyint(1) NOT NULL DEFAULT '0', + `under_threshold` tinyint(1) NOT NULL DEFAULT '0', + `needs_quest` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'quest drop', + `rnd_prop` int(10) NOT NULL DEFAULT '0' COMMENT 'random enchantment added when originally rolled', + `rnd_suffix` int(10) NOT NULL DEFAULT '0' COMMENT 'random suffix added when originally rolled' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `item_loot_money` +-- + +DROP TABLE IF EXISTS `item_loot_money`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `item_loot_money` ( + `container_id` int(10) NOT NULL DEFAULT '0' COMMENT 'guid of container (item_instance.guid)', + `money` int(10) NOT NULL DEFAULT '0' COMMENT 'money loot (in copper)' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index bdaf11ad9b8..5303fb8dc38 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -378,6 +378,10 @@ void Item::SaveToDB(SQLTransaction& trans) if (!isInTransaction) CharacterDatabase.CommitTransaction(trans); + // Delete the items if this is a container + if (!loot.isLooted()) + ItemContainerDeleteLootMoneyAndLootItemsFromDB(); + delete this; return; } @@ -483,6 +487,10 @@ void Item::DeleteFromDB(SQLTransaction& trans, uint32 itemGuid) void Item::DeleteFromDB(SQLTransaction& trans) { DeleteFromDB(trans, GetGUIDLow()); + + // Delete the items if this is a container + if (!loot.isLooted()) + ItemContainerDeleteLootMoneyAndLootItemsFromDB(); } /*static*/ @@ -1198,3 +1206,191 @@ bool Item::CheckSoulboundTradeExpire() return false; } + +void Item::ItemContainerSaveLootToDB() +{ + // Saves the money and item loot associated with an openable item to the DB + + if (loot.isLooted()) // no money and no loot + return; + + uint32 container_id = GetGUIDLow(); + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + + loot.containerID = container_id; // Save this for when a LootItem is removed + + // Save money + if (loot.gold > 0) + { + PreparedStatement* stmt_money = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY); + stmt_money->setUInt32(0, container_id); + trans->Append(stmt_money); + + stmt_money = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_MONEY); + stmt_money->setUInt32(0, container_id); + stmt_money->setUInt32(1, loot.gold); + trans->Append(stmt_money); + } + + // Save items + if (!loot.isLooted()) + { + + PreparedStatement* stmt_items = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEMS); + stmt_items->setUInt32(0, container_id); + trans->Append(stmt_items); + + // Now insert the items + for (LootItemList::const_iterator _li = loot.items.begin(); _li != loot.items.end(); _li++) + { + // When an item is looted, it doesn't get removed from the items collection + // but we don't want to resave it. + if (!_li->canSave) + continue; + + stmt_items = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_ITEMS); + + // container_id, item_id, item_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix + stmt_items->setUInt32(0, container_id); + stmt_items->setUInt32(1, _li->itemid); + stmt_items->setUInt32(2, _li->count); + stmt_items->setBool(3, _li->follow_loot_rules); + stmt_items->setBool(4, _li->freeforall); + stmt_items->setBool(5, _li->is_blocked); + stmt_items->setBool(6, _li->is_counted); + stmt_items->setBool(7, _li->is_underthreshold); + stmt_items->setBool(8, _li->needs_quest); + stmt_items->setUInt32(9, _li->randomPropertyId); + stmt_items->setUInt32(10, _li->randomSuffix); + trans->Append(stmt_items); + } + } + + CharacterDatabase.CommitTransaction(trans); +} + +bool Item::ItemContainerLoadLootFromDB() +{ + // Loads the money and item loot associated with an openable item from the DB + + // Default. If there are no records for this item then it will be rolled for in Player::SendLoot() + m_lootGenerated = false; + + uint32 container_id = GetGUIDLow(); + + // Save this for later use + loot.containerID = container_id; + + // First, see if there was any money loot. This gets added directly to the container. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_MONEY); + stmt->setUInt32(0, container_id); + PreparedQueryResult money_result = CharacterDatabase.Query(stmt); + + if (money_result) + { + Field* fields = money_result->Fetch(); + loot.gold = fields[0].GetUInt32(); + } + + // Next, load any items that were saved + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_ITEMS); + stmt->setUInt32(0, container_id); + PreparedQueryResult item_result = CharacterDatabase.Query(stmt); + + if (item_result) + { + // Get a LootTemplate for the container item. This is where + // the saved loot was originally rolled from, we will copy conditions from it + LootTemplate const* lt = LootTemplates_Item.GetLootFor(GetEntry()); + + if (lt) + { + do + { + // Create an empty LootItem + LootItem loot_item = LootItem(); + + // Fill in the rest of the LootItem from the DB + Field* fields = item_result->Fetch(); + + // item_id, itm_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix + loot_item.itemid = fields[0].GetUInt32(); + loot_item.count = fields[1].GetUInt32(); + loot_item.follow_loot_rules = fields[2].GetBool(); + loot_item.freeforall = fields[3].GetBool(); + loot_item.is_blocked = fields[4].GetBool(); + loot_item.is_counted = fields[5].GetBool(); + loot_item.canSave = true; + loot_item.is_underthreshold = fields[6].GetBool(); + loot_item.needs_quest = fields[7].GetBool(); + loot_item.randomPropertyId = fields[8].GetUInt32(); + loot_item.randomSuffix = fields[9].GetUInt32(); + + // Copy the extra loot conditions from the item in the loot template + lt->CopyConditions(&loot_item); + + // If container item is in a bag, add that player as an allowed looter + if (GetBagSlot()) + loot_item.allowedGUIDs.insert(GetOwner()->GetGUIDLow()); + + // Finally add the LootItem to the container + loot.items.push_back(loot_item); + + // Increment unlooted count + loot.unlootedCount++; + + } while (item_result->NextRow()); + } + } + + // Mark the item if it has loot so it won't be generated again on open + m_lootGenerated = !loot.isLooted(); + + return m_lootGenerated; +} + +void Item::ItemContainerDeleteLootItemsFromDB() +{ + // Deletes items associated with an openable item from the DB + + uint32 containerId = GetGUIDLow(); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEMS); + stmt->setUInt32(0, containerId); + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + trans->Append(stmt); + CharacterDatabase.CommitTransaction(trans); +} + +void Item::ItemContainerDeleteLootItemFromDB(uint32 itemID) +{ + // Deletes a single item associated with an openable item from the DB + + uint32 containerId = GetGUIDLow(); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEM); + stmt->setUInt32(0, containerId); + stmt->setUInt32(1, itemID); + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + trans->Append(stmt); + CharacterDatabase.CommitTransaction(trans); +} + +void Item::ItemContainerDeleteLootMoneyFromDB() +{ + // Deletes the money loot associated with an openable item from the DB + + uint32 containerId = GetGUIDLow(); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY); + stmt->setUInt32(0, containerId); + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + trans->Append(stmt); + CharacterDatabase.CommitTransaction(trans); +} + +void Item::ItemContainerDeleteLootMoneyAndLootItemsFromDB() +{ + // Deletes money and items associated with an openable item from the DB + + ItemContainerDeleteLootMoneyFromDB(); + ItemContainerDeleteLootItemsFromDB(); +} + diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index a5f9ed5c008..2e1956250f3 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -231,6 +231,15 @@ class Item : public Object static void DeleteFromDB(SQLTransaction& trans, uint32 itemGuid); virtual void DeleteFromDB(SQLTransaction& trans); static void DeleteFromInventoryDB(SQLTransaction& trans, uint32 itemGuid); + + // Lootable items and their contents + void ItemContainerSaveLootToDB(); + bool ItemContainerLoadLootFromDB(); + void ItemContainerDeleteLootItemsFromDB(); + void ItemContainerDeleteLootItemFromDB(uint32 itemID); + void ItemContainerDeleteLootMoneyFromDB(); + void ItemContainerDeleteLootMoneyAndLootItemsFromDB(); + void DeleteFromInventoryDB(SQLTransaction& trans); void SaveRefundDataToDB(); void DeleteRefundDataFromDB(SQLTransaction* trans); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index cf573ec8c5a..34d132f6dce 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8903,7 +8903,9 @@ void Player::SendLoot(uint64 guid, LootType loot_type) loot = &item->loot; - if (!item->m_lootGenerated) + // If item doesn't already have loot, attempt to load it. If that + // fails then this is first time opening, generate loot + if (!item->m_lootGenerated && !item->ItemContainerLoadLootFromDB()) { item->m_lootGenerated = true; loot->clear(); @@ -8922,6 +8924,12 @@ void Player::SendLoot(uint64 guid, LootType loot_type) default: loot->generateMoneyLoot(item->GetTemplate()->MinMoneyLoot, item->GetTemplate()->MaxMoneyLoot); loot->FillLoot(item->GetEntry(), LootTemplates_Item, this, true, loot->gold != 0); + + // Force save the loot and money items that were just rolled + // Also saves the container item ID in Loot struct (not to DB) + if (loot->gold > 0 || loot->unlootedCount > 0) + item->ItemContainerSaveLootToDB(); + break; } } @@ -12675,6 +12683,12 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) else if (Bag* pBag = GetBagByPos(bag)) pBag->RemoveItem(slot, update); + // Delete rolled money / loot from db. + // MUST be done before RemoveFromWorld() or GetTemplate() fails + if (ItemTemplate const* pTmp = pItem->GetTemplate()) + if (pTmp->Flags & ITEM_PROTO_FLAG_OPENABLE) + pItem->ItemContainerDeleteLootMoneyAndLootItemsFromDB(); + if (IsInWorld() && update) { pItem->RemoveFromWorld(); @@ -24055,6 +24069,11 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot) UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type, item->count); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid, item->count); + + // LootItem is being removed (looted) from the container, delete it from the DB. + if (loot->containerID > 0) + loot->DeleteLootItemFromContainerItemDB(item->itemid); + } else SendEquipError(msg, NULL, NULL, item->itemid); diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index 752eace536b..b15636e75d2 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -93,6 +93,10 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket& recvData) } player->StoreLootItem(lootSlot, loot); + + // If player is removing the last LootItem, delete the empty container. + if (loot->isLooted() && IS_ITEM_GUID(lguid)) + player->GetSession()->DoLootRelease(lguid); } void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recvData*/) @@ -200,6 +204,14 @@ void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recvData*/) } loot->gold = 0; + + // Delete the money loot record from the DB + if (loot->containerID > 0) + loot->DeleteLootMoneyFromContainerItemDB(); + + // Delete container if empty + if (loot->isLooted() && IS_ITEM_GUID(guid)) + player->GetSession()->DoLootRelease(guid); } } @@ -381,8 +393,8 @@ void WorldSession::DoLootRelease(uint64 lguid) player->DestroyItemCount(pItem, count, true); } else - // FIXME: item must not be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or cheating possible. - player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true); + if (pItem->loot.isLooted()) // Only delete item if no loot or money (unlooted loot is saved to db) + player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true); return; // item can be looted only single player } else diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index ef3d2b9fbd6..098fcc657b3 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -334,6 +334,7 @@ LootItem::LootItem(LootStoreItem const& li) is_blocked = 0; is_underthreshold = 0; is_counted = 0; + canSave = true; } // Basic checks for player/item compatibility - if false no chance to see the item in the loot @@ -654,6 +655,33 @@ void Loot::generateMoneyLoot(uint32 minAmount, uint32 maxAmount) } } +void Loot::DeleteLootItemFromContainerItemDB(uint32 itemID) +{ + // Deletes a single item associated with an openable item from the DB + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEM); + stmt->setUInt32(0, containerID); + stmt->setUInt32(1, itemID); + CharacterDatabase.Execute(stmt); + + // Mark the item looted to prevent resaving + for (LootItemList::iterator _itr = items.begin(); _itr != items.end(); _itr++) + { + if (!_itr->itemid == itemID) + continue; + + _itr->canSave = true; + break; + } +} + +void Loot::DeleteLootMoneyFromContainerItemDB() +{ + // Deletes money loot associated with an openable item from the DB + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY); + stmt->setUInt32(0, containerID); + CharacterDatabase.Execute(stmt); +} + LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem* *qitem, QuestItem* *ffaitem, QuestItem* *conditem) { LootItem* item = NULL; @@ -1239,6 +1267,19 @@ void LootTemplate::CopyConditions(ConditionList conditions) i->CopyConditions(conditions); } +void LootTemplate::CopyConditions(LootItem* li) const +{ + // Copies the conditions list from a template item to a LootItem + for (LootStoreItemList::const_iterator _iter = Entries.begin(); _iter != Entries.end(); ++_iter) + { + if (!_iter->itemid == li->itemid) + continue; + + li->conditions = _iter->conditions; + break; + } +} + // Rolls for every item in the template and adds the rolled items the the loot void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId) const { diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index 45fc5c7983c..cfa5d370e3b 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -141,14 +141,17 @@ struct LootItem bool is_counted : 1; bool needs_quest : 1; // quest drop bool follow_loot_rules : 1; + bool canSave; // Constructor, copies most fields from LootStoreItem, generates random count and random suffixes/properties // Should be called for non-reference LootStoreItem entries only (mincountOrRef > 0) explicit LootItem(LootStoreItem const& li); + // Empty constructor for creating an empty LootItem to be filled in with DB data + LootItem() : canSave(true){}; + // Basic checks for player/item compatibility - if false no chance to see the item in the loot bool AllowedForPlayer(Player const* player) const; - void AddAllowedLooter(Player const* player); const AllowedLooterSet & GetAllowedLooters() const { return allowedGUIDs; } }; @@ -223,6 +226,7 @@ class LootTemplate // Rolls for every item in the template and adds the rolled items the the loot void Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId = 0) const; void CopyConditions(ConditionList conditions); + void CopyConditions(LootItem* li) const; // True if template includes at least 1 quest drop entry bool HasQuestDrop(LootTemplateMap const& store, uint8 groupId = 0) const; @@ -286,10 +290,18 @@ struct Loot uint8 unlootedCount; uint64 roundRobinPlayer; // GUID of the player having the Round-Robin ownership for the loot. If 0, round robin owner has released. LootType loot_type; // required for achievement system + + // GUIDLow of container that holds this loot (item_instance.entry) + // Only set for inventory items that can be right-click looted + uint32 containerID; - Loot(uint32 _gold = 0) : gold(_gold), unlootedCount(0), loot_type(LOOT_CORPSE) {} + Loot(uint32 _gold = 0) : gold(_gold), unlootedCount(0), loot_type(LOOT_CORPSE), containerID(0) {} ~Loot() { clear(); } + // For deleting items at loot removal since there is no backward interface to the Item() + void DeleteLootItemFromContainerItemDB(uint32 itemID); + void DeleteLootMoneyFromContainerItemDB(); + // if loot becomes invalid this reference is used to inform the listener void addLootValidatorRef(LootValidatorRef* pLootValidatorRef) { diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index 33f0cab5170..3ef94f3aafd 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -540,6 +540,15 @@ void CharacterDatabaseConnection::DoPrepareStatements() PREPARE_STATEMENT(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC, "DELETE FROM character_action WHERE spec<>? AND guid = ?", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH); + // Items that hold loot or money + PREPARE_STATEMENT(CHAR_SEL_ITEMCONTAINER_ITEMS, "SELECT item_id, item_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix FROM item_loot_items WHERE container_id = ?", CONNECTION_SYNCH); + PREPARE_STATEMENT(CHAR_DEL_ITEMCONTAINER_ITEMS, "DELETE FROM item_loot_items WHERE container_id = ?", CONNECTION_ASYNC); + PREPARE_STATEMENT(CHAR_DEL_ITEMCONTAINER_ITEM, "DELETE FROM item_loot_items WHERE container_id = ? AND item_id = ?", CONNECTION_ASYNC); + PREPARE_STATEMENT(CHAR_INS_ITEMCONTAINER_ITEMS, "INSERT INTO item_loot_items (container_id, item_id, item_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix) VALUES (?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC); + PREPARE_STATEMENT(CHAR_SEL_ITEMCONTAINER_MONEY, "SELECT money FROM item_loot_money WHERE container_id = ?", CONNECTION_SYNCH); + PREPARE_STATEMENT(CHAR_DEL_ITEMCONTAINER_MONEY, "DELETE FROM item_loot_money WHERE container_id = ?", CONNECTION_ASYNC); + PREPARE_STATEMENT(CHAR_INS_ITEMCONTAINER_MONEY," INSERT INTO item_loot_money (container_id, money) VALUES (?,?)", CONNECTION_ASYNC); + // Calendar PREPARE_STATEMENT(CHAR_REP_CALENDAR_EVENT, "REPLACE INTO calendar_events (id, creator, title, description, type, dungeon, eventtime, flags, time2) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_DEL_CALENDAR_EVENT, "DELETE FROM calendar_events WHERE id = ?", CONNECTION_ASYNC); diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h index 181161978df..d2e69fb0d17 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.h +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h @@ -505,6 +505,14 @@ enum CharacterDatabaseStatements CHAR_REP_CALENDAR_INVITE, CHAR_DEL_CALENDAR_INVITE, + CHAR_SEL_ITEMCONTAINER_ITEMS, + CHAR_DEL_ITEMCONTAINER_ITEMS, + CHAR_DEL_ITEMCONTAINER_ITEM, + CHAR_INS_ITEMCONTAINER_ITEMS, + CHAR_SEL_ITEMCONTAINER_MONEY, + CHAR_DEL_ITEMCONTAINER_MONEY, + CHAR_INS_ITEMCONTAINER_MONEY, + MAX_CHARACTERDATABASE_STATEMENTS }; -- cgit v1.2.3