/* * Copyright (C) 2008-2012 TrinityCore * Copyright (C) 2005-2009 MaNGOS * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "DatabaseEnv.h" #include "Guild.h" #include "GuildMgr.h" #include "GuildFinderMgr.h" #include "ScriptMgr.h" #include "Chat.h" #include "Config.h" #include "SocialMgr.h" #include "Log.h" #include "AccountMgr.h" #define MAX_GUILD_BANK_TAB_TEXT_LEN 500 #define EMBLEM_PRICE 10 * GOLD inline uint32 _GetGuildBankTabPrice(uint8 tabId) { switch (tabId) { case 0: return 100; case 1: return 250; case 2: return 500; case 3: return 1000; case 4: return 2500; case 5: return 5000; default: return 0; } } void Guild::SendCommandResult(WorldSession* session, GuildCommandType type, GuildCommandError errCode, const std::string& param) { WorldPacket data(SMSG_GUILD_COMMAND_RESULT, 8 + param.size() + 1); data << uint32(type); data << param; data << uint32(errCode); session->SendPacket(&data); sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Sent (SMSG_GUILD_COMMAND_RESULT)"); } void Guild::SendSaveEmblemResult(WorldSession* session, GuildEmblemError errCode) { WorldPacket data(MSG_SAVE_GUILD_EMBLEM, 4); data << uint32(errCode); session->SendPacket(&data); sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Sent (MSG_SAVE_GUILD_EMBLEM)"); } // LogHolder Guild::LogHolder::~LogHolder() { // Cleanup for (GuildLog::iterator itr = m_log.begin(); itr != m_log.end(); ++itr) delete (*itr); } // Adds event loaded from database to collection inline void Guild::LogHolder::LoadEvent(LogEntry* entry) { if (m_nextGUID == uint32(GUILD_EVENT_LOG_GUID_UNDEFINED)) m_nextGUID = entry->GetGUID(); m_log.push_front(entry); } // Adds new event happened in game. // If maximum number of events is reached, oldest event is removed from collection. inline void Guild::LogHolder::AddEvent(SQLTransaction& trans, LogEntry* entry) { // Check max records limit if (m_log.size() >= m_maxRecords) { LogEntry* oldEntry = m_log.front(); delete oldEntry; m_log.pop_front(); } // Add event to list m_log.push_back(entry); // Save to DB entry->SaveToDB(trans); } // Writes information about all events into packet. inline void Guild::LogHolder::WritePacket(WorldPacket& data) const { ByteBuffer buffer; data.WriteBits(m_log.size(), 23); for (GuildLog::const_iterator itr = m_log.begin(); itr != m_log.end(); ++itr) (*itr)->WritePacket(data, buffer); data.FlushBits(); data.append(buffer); } inline uint32 Guild::LogHolder::GetNextGUID() { // Next guid was not initialized. It means there are no records for this holder in DB yet. // Start from the beginning. if (m_nextGUID == uint32(GUILD_EVENT_LOG_GUID_UNDEFINED)) m_nextGUID = 0; else m_nextGUID = (m_nextGUID + 1) % m_maxRecords; return m_nextGUID; } // EventLogEntry void Guild::EventLogEntry::SaveToDB(SQLTransaction& trans) const { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_EVENTLOG); stmt->setUInt32(0, m_guildId); stmt->setUInt32(1, m_guid); CharacterDatabase.ExecuteOrAppend(trans, stmt); uint8 index = 0; stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_EVENTLOG); stmt->setUInt32( index, m_guildId); stmt->setUInt32(++index, m_guid); stmt->setUInt8 (++index, uint8(m_eventType)); stmt->setUInt32(++index, m_playerGuid1); stmt->setUInt32(++index, m_playerGuid2); stmt->setUInt8 (++index, m_newRank); stmt->setUInt64(++index, m_timestamp); CharacterDatabase.ExecuteOrAppend(trans, stmt); } void Guild::EventLogEntry::WritePacket(WorldPacket& data, ByteBuffer& content) const { ObjectGuid guid1 = MAKE_NEW_GUID(m_playerGuid1, 0, HIGHGUID_PLAYER); ObjectGuid guid2 = MAKE_NEW_GUID(m_playerGuid2, 0, HIGHGUID_PLAYER); data.WriteBit(guid1[2]); data.WriteBit(guid1[4]); data.WriteBit(guid2[7]); data.WriteBit(guid2[6]); data.WriteBit(guid1[3]); data.WriteBit(guid2[3]); data.WriteBit(guid2[5]); data.WriteBit(guid1[7]); data.WriteBit(guid1[5]); data.WriteBit(guid1[0]); data.WriteBit(guid2[4]); data.WriteBit(guid2[2]); data.WriteBit(guid2[0]); data.WriteBit(guid2[1]); data.WriteBit(guid1[1]); data.WriteBit(guid1[6]); content.WriteByteSeq(guid2[3]); content.WriteByteSeq(guid2[2]); content.WriteByteSeq(guid2[5]); // New Rank content << uint8(m_newRank); content.WriteByteSeq(guid2[4]); content.WriteByteSeq(guid1[0]); content.WriteByteSeq(guid1[4]); // Event timestamp content << uint32(::time(NULL) - m_timestamp); content.WriteByteSeq(guid1[7]); content.WriteByteSeq(guid1[3]); content.WriteByteSeq(guid2[0]); content.WriteByteSeq(guid2[6]); content.WriteByteSeq(guid2[7]); content.WriteByteSeq(guid1[5]); // Event type content << uint8(m_eventType); content.WriteByteSeq(guid2[1]); content.WriteByteSeq(guid1[2]); content.WriteByteSeq(guid1[6]); content.WriteByteSeq(guid1[1]); } // BankEventLogEntry void Guild::BankEventLogEntry::SaveToDB(SQLTransaction& trans) const { uint8 index = 0; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_EVENTLOG); stmt->setUInt32( index, m_guildId); stmt->setUInt32(++index, m_guid); stmt->setUInt8 (++index, m_bankTabId); CharacterDatabase.ExecuteOrAppend(trans, stmt); index = 0; stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_EVENTLOG); stmt->setUInt32( index, m_guildId); stmt->setUInt32(++index, m_guid); stmt->setUInt8 (++index, m_bankTabId); stmt->setUInt8 (++index, uint8(m_eventType)); stmt->setUInt32(++index, m_playerGuid); stmt->setUInt32(++index, m_itemOrMoney); stmt->setUInt16(++index, m_itemStackCount); stmt->setUInt8 (++index, m_destTabId); stmt->setUInt64(++index, m_timestamp); CharacterDatabase.ExecuteOrAppend(trans, stmt); } void Guild::BankEventLogEntry::WritePacket(WorldPacket& data, ByteBuffer& content) const { ObjectGuid logGuid = MAKE_NEW_GUID(m_playerGuid, 0, HIGHGUID_PLAYER); bool hasItem = m_eventType == GUILD_BANK_LOG_DEPOSIT_ITEM || m_eventType == GUILD_BANK_LOG_WITHDRAW_ITEM || m_eventType == GUILD_BANK_LOG_MOVE_ITEM || m_eventType == GUILD_BANK_LOG_MOVE_ITEM2; bool itemMoved = (m_eventType == GUILD_BANK_LOG_MOVE_ITEM || m_eventType == GUILD_BANK_LOG_MOVE_ITEM2); bool hasStack = (hasItem && m_itemStackCount > 1) || itemMoved; data.WriteBit(IsMoneyEvent()); data.WriteBit(logGuid[4]); data.WriteBit(logGuid[1]); data.WriteBit(hasItem); data.WriteBit(hasStack); data.WriteBit(logGuid[2]); data.WriteBit(logGuid[5]); data.WriteBit(logGuid[3]); data.WriteBit(logGuid[6]); data.WriteBit(logGuid[0]); data.WriteBit(itemMoved); data.WriteBit(logGuid[7]); content.WriteByteSeq(logGuid[6]); content.WriteByteSeq(logGuid[1]); content.WriteByteSeq(logGuid[5]); if (hasStack) content << uint32(m_itemStackCount); content << uint8(m_eventType); content.WriteByteSeq(logGuid[2]); content.WriteByteSeq(logGuid[4]); content.WriteByteSeq(logGuid[0]); content.WriteByteSeq(logGuid[7]); content.WriteByteSeq(logGuid[3]); if (hasItem) content << uint32(m_itemOrMoney); content << uint32(time(NULL) - m_timestamp); if (IsMoneyEvent()) content << uint64(m_itemOrMoney); if (itemMoved) content << uint8(m_destTabId); } // RankInfo void Guild::RankInfo::LoadFromDB(Field* fields) { m_rankId = fields[1].GetUInt8(); m_name = fields[2].GetString(); m_rights = fields[3].GetUInt32(); m_bankMoneyPerDay = fields[4].GetUInt32(); if (m_rankId == GR_GUILDMASTER) // Prevent loss of leader rights m_rights |= GR_RIGHT_ALL; } void Guild::RankInfo::SaveToDB(SQLTransaction& trans) const { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_RANK); stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, m_rankId); stmt->setString(2, m_name); stmt->setUInt32(3, m_rights); CharacterDatabase.ExecuteOrAppend(trans, stmt); } void Guild::RankInfo::SetName(const std::string& name) { if (m_name == name) return; m_name = name; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_NAME); stmt->setString(0, m_name); stmt->setUInt8 (1, m_rankId); stmt->setUInt32(2, m_guildId); CharacterDatabase.Execute(stmt); } void Guild::RankInfo::SetRights(uint32 rights) { if (m_rankId == GR_GUILDMASTER) // Prevent loss of leader rights rights = GR_RIGHT_ALL; if (m_rights == rights) return; m_rights = rights; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_RIGHTS); stmt->setUInt32(0, m_rights); stmt->setUInt8 (1, m_rankId); stmt->setUInt32(2, m_guildId); CharacterDatabase.Execute(stmt); } void Guild::RankInfo::SetBankMoneyPerDay(uint32 money) { if (m_rankId == GR_GUILDMASTER) // Prevent loss of leader rights money = uint32(GUILD_WITHDRAW_MONEY_UNLIMITED); if (m_bankMoneyPerDay == money) return; m_bankMoneyPerDay = money; PreparedStatement* stmt = NULL; stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_BANK_MONEY); stmt->setUInt32(0, money); stmt->setUInt8 (1, m_rankId); stmt->setUInt32(2, m_guildId); CharacterDatabase.Execute(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_BANK_RESET_TIME); stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, m_rankId); CharacterDatabase.Execute(stmt); } void Guild::RankInfo::SetBankTabSlotsAndRights(uint8 tabId, GuildBankRightsAndSlots rightsAndSlots, bool saveToDB) { if (m_rankId == GR_GUILDMASTER) // Prevent loss of leader rights rightsAndSlots.SetGuildMasterValues(); GuildBankRightsAndSlots& guildBR = m_bankTabRightsAndSlots[tabId]; if (guildBR.IsEqual(rightsAndSlots)) return; guildBR = rightsAndSlots; if (saveToDB) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_RIGHT); stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, tabId); stmt->setUInt8 (2, m_rankId); CharacterDatabase.Execute(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT); stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, tabId); stmt->setUInt8 (2, m_rankId); stmt->setUInt8 (3, guildBR.rights); stmt->setUInt32(4, guildBR.slots); CharacterDatabase.Execute(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_BANK_TIME0 + tabId); stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, m_rankId); CharacterDatabase.Execute(stmt); } } // BankTab bool Guild::BankTab::LoadFromDB(Field* fields) { m_name = fields[2].GetString(); m_icon = fields[3].GetString(); m_text = fields[4].GetString(); return true; } bool Guild::BankTab::LoadItemFromDB(Field* fields) { uint8 slotId = fields[13].GetUInt8(); uint32 itemGuid = fields[14].GetUInt32(); uint32 itemEntry = fields[15].GetUInt32(); if (slotId >= GUILD_BANK_MAX_SLOTS) { sLog->outError(LOG_FILTER_GUILD, "Invalid slot for item (GUID: %u, id: %u) in guild bank, skipped.", itemGuid, itemEntry); return false; } ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry); if (!proto) { sLog->outError(LOG_FILTER_GUILD, "Unknown item (GUID: %u, id: %u) in guild bank, skipped.", itemGuid, itemEntry); return false; } Item* pItem = NewItemOrBag(proto); if (!pItem->LoadFromDB(itemGuid, 0, fields, itemEntry)) { sLog->outError(LOG_FILTER_GUILD, "Item (GUID %u, id: %u) not found in item_instance, deleting from guild bank!", itemGuid, itemEntry); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM); stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, m_tabId); stmt->setUInt8 (2, slotId); CharacterDatabase.Execute(stmt); delete pItem; return false; } pItem->AddToWorld(); m_items[slotId] = pItem; return true; } // Deletes contents of the tab from the world (and from DB if necessary) void Guild::BankTab::Delete(SQLTransaction& trans, bool removeItemsFromDB) { for (uint8 slotId = 0; slotId < GUILD_BANK_MAX_SLOTS; ++slotId) if (Item* pItem = m_items[slotId]) { pItem->RemoveFromWorld(); if (removeItemsFromDB) pItem->DeleteFromDB(trans); delete pItem; pItem = NULL; } } void Guild::BankTab::SetInfo(const std::string& name, const std::string& icon) { if (m_name == name && m_icon == icon) return; m_name = name; m_icon = icon; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_INFO); stmt->setString(0, m_name); stmt->setString(1, m_icon); stmt->setUInt32(2, m_guildId); stmt->setUInt8 (3, m_tabId); CharacterDatabase.Execute(stmt); } void Guild::BankTab::SetText(const std::string& text) { if (m_text == text) return; m_text = text; utf8truncate(m_text, MAX_GUILD_BANK_TAB_TEXT_LEN); // DB and client size limitation PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_TEXT); stmt->setString(0, m_text); stmt->setUInt32(1, m_guildId); stmt->setUInt8 (2, m_tabId); CharacterDatabase.Execute(stmt); } // Sets/removes contents of specified slot. // If pItem == NULL contents are removed. bool Guild::BankTab::SetItem(SQLTransaction& trans, uint8 slotId, Item* item) { if (slotId >= GUILD_BANK_MAX_SLOTS) return false; m_items[slotId] = item; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_ITEM); stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, m_tabId); stmt->setUInt8 (2, slotId); CharacterDatabase.ExecuteOrAppend(trans, stmt); if (item) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_ITEM); stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, m_tabId); stmt->setUInt8 (2, slotId); stmt->setUInt32(3, item->GetGUIDLow()); CharacterDatabase.ExecuteOrAppend(trans, stmt); item->SetUInt64Value(ITEM_FIELD_CONTAINED, 0); item->SetUInt64Value(ITEM_FIELD_OWNER, 0); item->FSetState(ITEM_NEW); item->SaveToDB(trans); // Not in inventory and can be saved standalone } return true; } void Guild::BankTab::SendText(Guild const* guild, WorldSession* session) const { WorldPacket data(SMSG_GUILD_BANK_QUERY_TEXT_RESULT, 1 + m_text.size() + 1); data.WriteBits(m_text.length(), 14); data << uint32(m_tabId); data.WriteString(m_text); if (session) session->SendPacket(&data); else guild->BroadcastPacket(&data); } // Member void Guild::Member::SetStats(Player* player) { m_name = player->GetName(); m_level = player->getLevel(); m_class = player->getClass(); m_zoneId = player->GetZoneId(); m_accountId = player->GetSession()->GetAccountId(); } void Guild::Member::SetStats(const std::string& name, uint8 level, uint8 _class, uint32 zoneId, uint32 accountId) { m_name = name; m_level = level; m_class = _class; m_zoneId = zoneId; m_accountId = accountId; } void Guild::Member::SetPublicNote(const std::string& publicNote) { if (m_publicNote == publicNote) return; m_publicNote = publicNote; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_PNOTE); stmt->setString(0, publicNote); stmt->setUInt32(1, GUID_LOPART(m_guid)); CharacterDatabase.Execute(stmt); } void Guild::Member::SetOfficerNote(const std::string& officerNote) { if (m_officerNote == officerNote) return; m_officerNote = officerNote; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_OFFNOTE); stmt->setString(0, officerNote); stmt->setUInt32(1, GUID_LOPART(m_guid)); CharacterDatabase.Execute(stmt); } void Guild::Member::ChangeRank(uint8 newRank) { m_rankId = newRank; // Update rank information in player's field, if he is online. if (Player* player = FindPlayer()) player->SetRank(newRank); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_RANK); stmt->setUInt8 (0, newRank); stmt->setUInt32(1, GUID_LOPART(m_guid)); CharacterDatabase.Execute(stmt); } void Guild::Member::SaveToDB(SQLTransaction& trans) const { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER); stmt->setUInt32(0, m_guildId); stmt->setUInt32(1, GUID_LOPART(m_guid)); stmt->setUInt8 (2, m_rankId); stmt->setString(3, m_publicNote); stmt->setString(4, m_officerNote); CharacterDatabase.ExecuteOrAppend(trans, stmt); } // Loads member's data from database. // If member has broken fields (level, class) returns false. // In this case member has to be removed from guild. bool Guild::Member::LoadFromDB(Field* fields) { m_publicNote = fields[3].GetString(); m_officerNote = fields[4].GetString(); m_bankRemaining[GUILD_BANK_MAX_TABS].resetTime = fields[5].GetUInt32(); m_bankRemaining[GUILD_BANK_MAX_TABS].value = fields[6].GetUInt32(); for (uint8 i = 0; i < GUILD_BANK_MAX_TABS; ++i) { m_bankRemaining[i].resetTime = fields[7 + i * 2].GetUInt32(); m_bankRemaining[i].value = fields[8 + i * 2].GetUInt32(); } SetStats(fields[23].GetString(), fields[24].GetUInt8(), // characters.level fields[25].GetUInt8(), // characters.class fields[26].GetUInt16(), // characters.zone fields[27].GetUInt32()); // characters.account m_logoutTime = fields[28].GetUInt32(); // characters.logout_time if (!CheckStats()) return false; if (!m_zoneId) { sLog->outError(LOG_FILTER_GUILD, "Player (GUID: %u) has broken zone-data", GUID_LOPART(m_guid)); m_zoneId = Player::GetZoneIdFromDB(m_guid); } return true; } // Validate player fields. Returns false if corrupted fields are found. bool Guild::Member::CheckStats() const { if (m_level < 1) { sLog->outError(LOG_FILTER_GUILD, "Player (GUID: %u) has a broken data in field `characters`.`level`, deleting him from guild!", GUID_LOPART(m_guid)); return false; } if (m_class < CLASS_WARRIOR || m_class >= MAX_CLASSES) { sLog->outError(LOG_FILTER_GUILD, "Player (GUID: %u) has a broken data in field `characters`.`class`, deleting him from guild!", GUID_LOPART(m_guid)); return false; } return true; } // Decreases amount of money/slots left for today. // If (tabId == GUILD_BANK_MAX_TABS) decrease money amount. // Otherwise decrease remaining items amount for specified tab. void Guild::Member::DecreaseBankRemainingValue(SQLTransaction& trans, uint8 tabId, uint32 amount) { m_bankRemaining[tabId].value -= amount; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement( tabId == GUILD_BANK_MAX_TABS ? CHAR_UPD_GUILD_MEMBER_BANK_REM_MONEY : CHAR_UPD_GUILD_MEMBER_BANK_REM_SLOTS0 + tabId); stmt->setUInt32(0, m_bankRemaining[tabId].value); stmt->setUInt32(1, m_guildId); stmt->setUInt32(2, GUID_LOPART(m_guid)); CharacterDatabase.ExecuteOrAppend(trans, stmt); } // Get amount of money/slots left for today. // If (tabId == GUILD_BANK_MAX_TABS) return money amount. // Otherwise return remaining items amount for specified tab. // If reset time was more than 24 hours ago, renew reset time and reset amount to maximum value. uint32 Guild::Member::GetBankRemainingValue(uint8 tabId, const Guild* guild) const { // Guild master has unlimited amount. if (IsRank(GR_GUILDMASTER)) return tabId == GUILD_BANK_MAX_TABS ? GUILD_WITHDRAW_MONEY_UNLIMITED : GUILD_WITHDRAW_SLOT_UNLIMITED; // Check rights for non-money tab. if (tabId != GUILD_BANK_MAX_TABS) if ((guild->_GetRankBankTabRights(m_rankId, tabId) & GUILD_BANK_RIGHT_VIEW_TAB) != GUILD_BANK_RIGHT_VIEW_TAB) return 0; uint32 curTime = uint32(::time(NULL) / MINUTE); // minutes if (curTime > m_bankRemaining[tabId].resetTime + 24 * HOUR / MINUTE) { RemainingValue& rv = const_cast (m_bankRemaining[tabId]); rv.resetTime = curTime; rv.value = tabId == GUILD_BANK_MAX_TABS ? guild->_GetRankBankMoneyPerDay(m_rankId) : guild->_GetRankBankTabSlotsPerDay(m_rankId, tabId); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement( tabId == GUILD_BANK_MAX_TABS ? CHAR_UPD_GUILD_MEMBER_BANK_TIME_MONEY : CHAR_UPD_GUILD_MEMBER_BANK_TIME_REM_SLOTS0 + tabId); stmt->setUInt32(0, m_bankRemaining[tabId].resetTime); stmt->setUInt32(1, m_bankRemaining[tabId].value); stmt->setUInt32(2, m_guildId); stmt->setUInt32(3, GUID_LOPART(m_guid)); CharacterDatabase.Execute(stmt); } return m_bankRemaining[tabId].value; } inline void Guild::Member::ResetTabTimes() { for (uint8 tabId = 0; tabId < GUILD_BANK_MAX_TABS; ++tabId) m_bankRemaining[tabId].resetTime = 0; } inline void Guild::Member::ResetMoneyTime() { m_bankRemaining[GUILD_BANK_MAX_TABS].resetTime = 0; } // EmblemInfo void EmblemInfo::ReadPacket(WorldPacket& recv) { recv >> m_style >> m_color >> m_borderStyle >> m_borderColor >> m_backgroundColor; } void EmblemInfo::LoadFromDB(Field* fields) { m_style = fields[3].GetUInt8(); m_color = fields[4].GetUInt8(); m_borderStyle = fields[5].GetUInt8(); m_borderColor = fields[6].GetUInt8(); m_backgroundColor = fields[7].GetUInt8(); } void EmblemInfo::WritePacket(WorldPacket& data) const { data << uint32(m_style); data << uint32(m_color); data << uint32(m_borderStyle); data << uint32(m_borderColor); data << uint32(m_backgroundColor); } void EmblemInfo::SaveToDB(uint32 guildId) const { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_EMBLEM_INFO); stmt->setUInt32(0, m_style); stmt->setUInt32(1, m_color); stmt->setUInt32(2, m_borderStyle); stmt->setUInt32(3, m_borderColor); stmt->setUInt32(4, m_backgroundColor); stmt->setUInt32(5, guildId); CharacterDatabase.Execute(stmt); } // MoveItemData bool Guild::MoveItemData::CheckItem(uint32& splitedAmount) { ASSERT(m_pItem); if (splitedAmount > m_pItem->GetCount()) return false; if (splitedAmount == m_pItem->GetCount()) splitedAmount = 0; return true; } bool Guild::MoveItemData::CanStore(Item* pItem, bool swap, bool sendError) { m_vec.clear(); InventoryResult msg = CanStore(pItem, swap); if (sendError && msg != EQUIP_ERR_OK) m_pPlayer->SendEquipError(msg, pItem); return (msg == EQUIP_ERR_OK); } bool Guild::MoveItemData::CloneItem(uint32 count) { ASSERT(m_pItem); m_pClonedItem = m_pItem->CloneItem(count); if (!m_pClonedItem) { m_pPlayer->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, m_pItem); return false; } return true; } void Guild::MoveItemData::LogAction(MoveItemData* pFrom) const { ASSERT(pFrom->GetItem()); sScriptMgr->OnGuildItemMove(m_pGuild, m_pPlayer, pFrom->GetItem(), pFrom->IsBank(), pFrom->GetContainer(), pFrom->GetSlotId(), IsBank(), GetContainer(), GetSlotId()); } inline void Guild::MoveItemData::CopySlots(SlotIds& ids) const { for (ItemPosCountVec::const_iterator itr = m_vec.begin(); itr != m_vec.end(); ++itr) ids.insert(uint8(itr->pos)); } // PlayerMoveItemData bool Guild::PlayerMoveItemData::InitItem() { m_pItem = m_pPlayer->GetItemByPos(m_container, m_slotId); if (m_pItem) { // Anti-WPE protection. Do not move non-empty bags to bank. if (m_pItem->IsNotEmptyBag()) { m_pPlayer->SendEquipError(EQUIP_ERR_DESTROY_NONEMPTY_BAG, m_pItem); m_pItem = NULL; } // Bound items cannot be put into bank. else if (!m_pItem->CanBeTraded()) { m_pPlayer->SendEquipError(EQUIP_ERR_CANT_SWAP, m_pItem); m_pItem = NULL; } } return (m_pItem != NULL); } void Guild::PlayerMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* /*pOther*/, uint32 splitedAmount) { if (splitedAmount) { m_pItem->SetCount(m_pItem->GetCount() - splitedAmount); m_pItem->SetState(ITEM_CHANGED, m_pPlayer); m_pPlayer->SaveInventoryAndGoldToDB(trans); } else { m_pPlayer->MoveItemFromInventory(m_container, m_slotId, true); m_pItem->DeleteFromInventoryDB(trans); m_pItem = NULL; } } Item* Guild::PlayerMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem) { ASSERT(pItem); m_pPlayer->MoveItemToInventory(m_vec, pItem, true); m_pPlayer->SaveInventoryAndGoldToDB(trans); return pItem; } void Guild::PlayerMoveItemData::LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const { ASSERT(pFrom); // Bank -> Char m_pGuild->_LogBankEvent(trans, GUILD_BANK_LOG_WITHDRAW_ITEM, pFrom->GetContainer(), m_pPlayer->GetGUIDLow(), pFrom->GetItem()->GetEntry(), count); } inline InventoryResult Guild::PlayerMoveItemData::CanStore(Item* pItem, bool swap) { return m_pPlayer->CanStoreItem(m_container, m_slotId, m_vec, pItem, swap); } // BankMoveItemData bool Guild::BankMoveItemData::InitItem() { m_pItem = m_pGuild->_GetItem(m_container, m_slotId); return (m_pItem != NULL); } bool Guild::BankMoveItemData::HasStoreRights(MoveItemData* pOther) const { ASSERT(pOther); // Do not check rights if item is being swapped within the same bank tab if (pOther->IsBank() && pOther->GetContainer() == m_container) return true; return m_pGuild->_MemberHasTabRights(m_pPlayer->GetGUID(), m_container, GUILD_BANK_RIGHT_DEPOSIT_ITEM); } bool Guild::BankMoveItemData::HasWithdrawRights(MoveItemData* pOther) const { ASSERT(pOther); // Do not check rights if item is being swapped within the same bank tab if (pOther->IsBank() && pOther->GetContainer() == m_container) return true; return (m_pGuild->_GetMemberRemainingSlots(m_pPlayer->GetGUID(), m_container) != 0); } void Guild::BankMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount) { ASSERT(m_pItem); if (splitedAmount) { m_pItem->SetCount(m_pItem->GetCount() - splitedAmount); m_pItem->FSetState(ITEM_CHANGED); m_pItem->SaveToDB(trans); } else { m_pGuild->_RemoveItem(trans, m_container, m_slotId); m_pItem = NULL; } // Decrease amount of player's remaining items (if item is moved to different tab or to player) if (!pOther->IsBank() || pOther->GetContainer() != m_container) m_pGuild->_DecreaseMemberRemainingSlots(trans, m_pPlayer->GetGUID(), m_container); } Item* Guild::BankMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem) { if (!pItem) return NULL; BankTab* pTab = m_pGuild->GetBankTab(m_container); if (!pTab) return NULL; Item* pLastItem = pItem; for (ItemPosCountVec::const_iterator itr = m_vec.begin(); itr != m_vec.end(); ) { ItemPosCount pos(*itr); ++itr; sLog->outDebug(LOG_FILTER_GUILD, "GUILD STORAGE: StoreItem tab = %u, slot = %u, item = %u, count = %u", m_container, m_slotId, pItem->GetEntry(), pItem->GetCount()); pLastItem = _StoreItem(trans, pTab, pItem, pos, itr != m_vec.end()); } return pLastItem; } void Guild::BankMoveItemData::LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const { ASSERT(pFrom->GetItem()); if (pFrom->IsBank()) // Bank -> Bank m_pGuild->_LogBankEvent(trans, GUILD_BANK_LOG_MOVE_ITEM, pFrom->GetContainer(), m_pPlayer->GetGUIDLow(), pFrom->GetItem()->GetEntry(), count, m_container); else // Char -> Bank m_pGuild->_LogBankEvent(trans, GUILD_BANK_LOG_DEPOSIT_ITEM, m_container, m_pPlayer->GetGUIDLow(), pFrom->GetItem()->GetEntry(), count); } void Guild::BankMoveItemData::LogAction(MoveItemData* pFrom) const { MoveItemData::LogAction(pFrom); if (!pFrom->IsBank() && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE) && !AccountMgr::IsPlayerAccount(m_pPlayer->GetSession()->GetSecurity())) // TODO: move to scripts sLog->outCommand(m_pPlayer->GetSession()->GetAccountId(), "GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u)", m_pPlayer->GetName(), m_pPlayer->GetSession()->GetAccountId(), pFrom->GetItem()->GetTemplate()->Name1.c_str(), pFrom->GetItem()->GetEntry(), pFrom->GetItem()->GetCount(), m_pGuild->GetId()); } Item* Guild::BankMoveItemData::_StoreItem(SQLTransaction& trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const { uint8 slotId = uint8(pos.pos); uint32 count = pos.count; if (Item* pItemDest = pTab->GetItem(slotId)) { pItemDest->SetCount(pItemDest->GetCount() + count); pItemDest->FSetState(ITEM_CHANGED); pItemDest->SaveToDB(trans); if (!clone) { pItem->RemoveFromWorld(); pItem->DeleteFromDB(trans); delete pItem; } return pItemDest; } if (clone) pItem = pItem->CloneItem(count); else pItem->SetCount(count); if (pItem && pTab->SetItem(trans, slotId, pItem)) return pItem; return NULL; } // Tries to reserve space for source item. // If item in destination slot exists it must be the item of the same entry // and stack must have enough space to take at least one item. // Returns false if destination item specified and it cannot be used to reserve space. bool Guild::BankMoveItemData::_ReserveSpace(uint8 slotId, Item* pItem, Item* pItemDest, uint32& count) { uint32 requiredSpace = pItem->GetMaxStackCount(); if (pItemDest) { // Make sure source and destination items match and destination item has space for more stacks. if (pItemDest->GetEntry() != pItem->GetEntry() || pItemDest->GetCount() >= pItem->GetMaxStackCount()) return false; requiredSpace -= pItemDest->GetCount(); } // Let's not be greedy, reserve only required space requiredSpace = std::min(requiredSpace, count); // Reserve space ItemPosCount pos(slotId, requiredSpace); if (!pos.isContainedIn(m_vec)) { m_vec.push_back(pos); count -= requiredSpace; } return true; } void Guild::BankMoveItemData::CanStoreItemInTab(Item* pItem, uint8 skipSlotId, bool merge, uint32& count) { for (uint8 slotId = 0; (slotId < GUILD_BANK_MAX_SLOTS) && (count > 0); ++slotId) { // Skip slot already processed in CanStore (when destination slot was specified) if (slotId == skipSlotId) continue; Item* pItemDest = m_pGuild->_GetItem(m_container, slotId); if (pItemDest == pItem) pItemDest = NULL; // If merge skip empty, if not merge skip non-empty if ((pItemDest != NULL) != merge) continue; _ReserveSpace(slotId, pItem, pItemDest, count); } } InventoryResult Guild::BankMoveItemData::CanStore(Item* pItem, bool swap) { sLog->outDebug(LOG_FILTER_GUILD, "GUILD STORAGE: CanStore() tab = %u, slot = %u, item = %u, count = %u", m_container, m_slotId, pItem->GetEntry(), pItem->GetCount()); uint32 count = pItem->GetCount(); // Soulbound items cannot be moved if (pItem->IsSoulBound()) return EQUIP_ERR_DROP_BOUND_ITEM; // Make sure destination bank tab exists if (m_container >= m_pGuild->_GetPurchasedTabsSize()) return EQUIP_ERR_WRONG_BAG_TYPE; // Slot explicitely specified. Check it. if (m_slotId != NULL_SLOT) { Item* pItemDest = m_pGuild->_GetItem(m_container, m_slotId); // Ignore swapped item (this slot will be empty after move) if ((pItemDest == pItem) || swap) pItemDest = NULL; if (!_ReserveSpace(m_slotId, pItem, pItemDest, count)) return EQUIP_ERR_CANT_STACK; if (count == 0) return EQUIP_ERR_OK; } // Slot was not specified or it has not enough space for all the items in stack // Search for stacks to merge with if (pItem->GetMaxStackCount() > 1) { CanStoreItemInTab(pItem, m_slotId, true, count); if (count == 0) return EQUIP_ERR_OK; } // Search free slot for item CanStoreItemInTab(pItem, m_slotId, false, count); if (count == 0) return EQUIP_ERR_OK; return EQUIP_ERR_BANK_FULL; } // Guild Guild::Guild() : m_id(0), m_leaderGuid(0), m_createdDate(0), m_accountsNumber(0), m_bankMoney(0), m_eventLog(NULL), m_achievementMgr(this), _newsLog(this), _level(1), _experience(0), _todayExperience(0) { memset(&m_bankEventLog, 0, (GUILD_BANK_MAX_TABS + 1) * sizeof(LogHolder*)); } Guild::~Guild() { SQLTransaction temp(NULL); _DeleteBankItems(temp); // Cleanup if (m_eventLog) delete m_eventLog; for (uint8 tabId = 0; tabId <= GUILD_BANK_MAX_TABS; ++tabId) if (m_bankEventLog[tabId]) delete m_bankEventLog[tabId]; for (Members::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) delete itr->second; } // Creates new guild with default data and saves it to database. bool Guild::Create(Player* pLeader, const std::string& name) { // Check if guild with such name already exists if (sGuildMgr->GetGuildByName(name)) return false; WorldSession* pLeaderSession = pLeader->GetSession(); if (!pLeaderSession) return false; m_id = sGuildMgr->GenerateGuildId(); m_leaderGuid = pLeader->GetGUID(); m_name = name; m_info = ""; m_motd = "No message set."; m_bankMoney = 0; m_createdDate = ::time(NULL); _level = 1; _CreateLogHolders(); sLog->outDebug(LOG_FILTER_GUILD, "GUILD: creating guild [%s] for leader %s (%u)", name.c_str(), pLeader->GetName(), GUID_LOPART(m_leaderGuid)); PreparedStatement* stmt = NULL; SQLTransaction trans = CharacterDatabase.BeginTransaction(); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBERS); stmt->setUInt32(0, m_id); trans->Append(stmt); uint8 index = 0; stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD); stmt->setUInt32( index, m_id); stmt->setString(++index, name); stmt->setUInt32(++index, GUID_LOPART(m_leaderGuid)); stmt->setString(++index, m_info); stmt->setString(++index, m_motd); stmt->setUInt64(++index, uint32(m_createdDate)); stmt->setUInt32(++index, m_emblemInfo.GetStyle()); stmt->setUInt32(++index, m_emblemInfo.GetColor()); stmt->setUInt32(++index, m_emblemInfo.GetBorderStyle()); stmt->setUInt32(++index, m_emblemInfo.GetBorderColor()); stmt->setUInt32(++index, m_emblemInfo.GetBackgroundColor()); stmt->setUInt64(++index, m_bankMoney); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); // Create default ranks _CreateDefaultGuildRanks(pLeaderSession->GetSessionDbLocaleIndex()); // Add guildmaster bool ret = AddMember(m_leaderGuid, GR_GUILDMASTER); if (ret) // Call scripts on successful create sScriptMgr->OnGuildCreate(this, pLeader, name); _BroadcastEvent(GE_FOUNDER, m_leaderGuid); return ret; } // Disbands guild and deletes all related data from database void Guild::Disband() { // Call scripts before guild data removed from database sScriptMgr->OnGuildDisband(this); _BroadcastEvent(GE_DISBANDED, 0); // Remove all members while (!m_members.empty()) { Members::const_iterator itr = m_members.begin(); DeleteMember(itr->second->GetGUID(), true); } PreparedStatement* stmt = NULL; SQLTransaction trans = CharacterDatabase.BeginTransaction(); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD); stmt->setUInt32(0, m_id); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_RANKS); stmt->setUInt32(0, m_id); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_TABS); stmt->setUInt32(0, m_id); trans->Append(stmt); // Free bank tab used memory and delete items stored in them _DeleteBankItems(trans, true); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_ITEMS); stmt->setUInt32(0, m_id); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_RIGHTS); stmt->setUInt32(0, m_id); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_EVENTLOGS); stmt->setUInt32(0, m_id); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_EVENTLOGS); stmt->setUInt32(0, m_id); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); sGuildFinderMgr->DeleteGuild(m_id); sGuildMgr->RemoveGuild(m_id); } void Guild::SaveToDB() { SQLTransaction trans = CharacterDatabase.BeginTransaction(); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_EXPERIENCE); stmt->setUInt32(0, GetLevel()); stmt->setUInt64(1, GetExperience()); stmt->setUInt64(2, GetTodayExperience()); stmt->setUInt32(3, GetId()); trans->Append(stmt); m_achievementMgr.SaveToDB(trans); CharacterDatabase.CommitTransaction(trans); } void Guild::HandleRoster(WorldSession* session /*= NULL*/) { ByteBuffer memberData(100); // Guess size WorldPacket data(SMSG_GUILD_ROSTER, 100); data.WriteBits(m_motd.length(), 11); data.WriteBits(m_members.size(), 18); for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { Member* member = itr->second; Player* player = member->FindPlayer(); size_t pubNoteLength = member->GetPublicNote().length(); size_t offNoteLength = member->GetOfficerNote().length(); ObjectGuid guid = member->GetGUID(); data.WriteBit(guid[3]); data.WriteBit(guid[4]); data.WriteBit(0); // Has Authenticator data.WriteBit(0); // Can Scroll of Ressurect data.WriteBits(pubNoteLength, 8); data.WriteBits(offNoteLength, 8); data.WriteBit(guid[0]); data.WriteBits(member->GetName().length(), 7); data.WriteBit(guid[1]); data.WriteBit(guid[2]); data.WriteBit(guid[6]); data.WriteBit(guid[5]); data.WriteBit(guid[7]); uint8 flags = GUILDMEMBER_STATUS_NONE; if (player) { flags |= GUILDMEMBER_STATUS_ONLINE; if (player->isAFK()) flags |= GUILDMEMBER_STATUS_AFK; if (player->isDND()) flags |= GUILDMEMBER_STATUS_DND; } memberData << uint8(member->GetClass()); memberData << int32(0); // unk memberData.WriteByteSeq(guid[0]); memberData << uint64(0); // weekly activity memberData << uint32(member->GetRankId()); memberData << uint32(0); // player->GetAchievementMgr().GetCompletedAchievementsAmount() // for (2 professions) memberData << uint32(0) << uint32(0) << uint32(0); memberData << uint32(0) << uint32(0) << uint32(0); memberData.WriteByteSeq(guid[2]); memberData << uint8(flags); memberData << uint32(player ? player->GetZoneId() : member->GetZone()); memberData << uint64(0); // Total activity memberData.WriteByteSeq(guid[7]); memberData << uint32(member->GetRemainingWeeklyReputation());// Remaining guild week Rep if (pubNoteLength) memberData.WriteString(member->GetPublicNote()); memberData.WriteByteSeq(guid[3]); memberData << uint8(player ? player->getLevel() : member->GetLevel()); memberData << int32(0); // unk memberData.WriteByteSeq(guid[5]); memberData.WriteByteSeq(guid[4]); memberData << uint8(0); // unk memberData.WriteByteSeq(guid[1]); memberData << float(player ? 0.0f : float(::time(NULL) - member->GetLogoutTime()) / DAY); if (offNoteLength) memberData.WriteString(member->GetOfficerNote()); memberData.WriteByteSeq(guid[6]); memberData.WriteString(member->GetName()); } size_t infoLength = m_info.length(); data.WriteBits(infoLength, 12); data.FlushBits(); data.append(memberData); if (infoLength) data.WriteString(m_info); data.WriteString(m_motd); data << uint32(m_accountsNumber); data << uint32(sWorld->getIntConfig(CONFIG_GUILD_WEEKLY_REP_CAP)); data.AppendPackedTime(m_createdDate); data << uint32(0); if (session) session->SendPacket(&data); else BroadcastPacket(&data); sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Sent (SMSG_GUILD_ROSTER)"); } void Guild::HandleQuery(WorldSession* session) { WorldPacket data(SMSG_GUILD_QUERY_RESPONSE, 8 * 32 + 200); // Guess size data << uint64(GetGUID()); data << m_name; // Rank name for (uint8 i = 0; i < GUILD_RANKS_MAX_COUNT; ++i) // Always show 10 ranks { if (i < _GetRanksSize()) data << m_ranks[i].GetName(); else data << uint8(0); // Empty string } // Rank order of creation for (uint8 i = 0; i < GUILD_RANKS_MAX_COUNT; ++i) { if (i < _GetRanksSize()) data << uint32(i); else data << uint32(0); } // Rank order of "importance" (sorting by rights) for (uint8 i = 0; i < GUILD_RANKS_MAX_COUNT; ++i) { if (i < _GetRanksSize()) data << uint32(m_ranks[i].GetId()); else data << uint32(0); } m_emblemInfo.WritePacket(data); data << uint32(_GetRanksSize()); // Number of ranks used session->SendPacket(&data); sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Sent (SMSG_GUILD_QUERY_RESPONSE)"); } void Guild::HandleGuildRanks(WorldSession* session) const { // perhaps move to guild.cpp..... ByteBuffer rankData(100); WorldPacket data(SMSG_GUILD_RANK, 100); data.WriteBits(_GetRanksSize(), 18); for (uint8 i = 0; i < _GetRanksSize(); i++) { RankInfo const* rankInfo = GetRankInfo(i); if (!rankInfo) continue; data.WriteBits(rankInfo->GetName().length(), 7); rankData << uint32(i); for (uint8 j = 0; j < GUILD_BANK_MAX_TABS; ++j) { rankData << uint32(rankInfo->GetBankTabSlotsPerDay(j)); rankData << uint32(rankInfo->GetBankTabRights(j)); } rankData << uint32(rankInfo->GetBankMoneyPerDay()); rankData << uint32(rankInfo->GetRights()); if (rankInfo->GetName().length()) rankData.WriteString(rankInfo->GetName()); rankData << uint32(rankInfo->GetId()); } data.FlushBits(); data.append(rankData); session->SendPacket(&data); } void Guild::HandleSetMOTD(WorldSession* session, const std::string& motd) { if (m_motd == motd) return; // Player must have rights to set MOTD if (!_HasRankRight(session->GetPlayer(), GR_RIGHT_SETMOTD)) SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PERMISSIONS); else { m_motd = motd; sScriptMgr->OnGuildMOTDChanged(this, motd); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MOTD); stmt->setString(0, motd); stmt->setUInt32(1, m_id); CharacterDatabase.Execute(stmt); _BroadcastEvent(GE_MOTD, 0, motd.c_str()); } } void Guild::HandleSetInfo(WorldSession* session, const std::string& info) { if (m_info == info) return; // Player must have rights to set guild's info if (!_HasRankRight(session->GetPlayer(), GR_RIGHT_MODIFY_GUILD_INFO)) SendCommandResult(session, GUILD_CREATE_S, ERR_GUILD_PERMISSIONS); else { m_info = info; sScriptMgr->OnGuildInfoChanged(this, info); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_INFO); stmt->setString(0, info); stmt->setUInt32(1, m_id); CharacterDatabase.Execute(stmt); } } void Guild::HandleSetEmblem(WorldSession* session, const EmblemInfo& emblemInfo) { Player* player = session->GetPlayer(); if (!_IsLeader(player)) // "Only guild leaders can create emblems." SendSaveEmblemResult(session, ERR_GUILDEMBLEM_NOTGUILDMASTER); else if (!player->HasEnoughMoney(uint64(EMBLEM_PRICE))) // "You can't afford to do that." SendSaveEmblemResult(session, ERR_GUILDEMBLEM_NOTENOUGHMONEY); else { player->ModifyMoney(-int64(EMBLEM_PRICE)); m_emblemInfo = emblemInfo; m_emblemInfo.SaveToDB(m_id); // "Guild Emblem saved." SendSaveEmblemResult(session, ERR_GUILDEMBLEM_SUCCESS); HandleQuery(session); } } void Guild::HandleSetLeader(WorldSession* session, const std::string& name) { Player* player = session->GetPlayer(); // Only leader can assign new leader if (!_IsLeader(player)) SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PERMISSIONS); // Old leader must be a member of guild else if (Member* pOldLeader = GetMember(player->GetGUID())) { // New leader must be a member of guild if (Member* pNewLeader = GetMember(session, name)) { _SetLeaderGUID(pNewLeader); pOldLeader->ChangeRank(GR_OFFICER); _BroadcastEvent(GE_LEADER_CHANGED, 0, player->GetName(), name.c_str()); } } else SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PERMISSIONS); } void Guild::HandleSetBankTabInfo(WorldSession* session, uint8 tabId, const std::string& name, const std::string& icon) { if (BankTab* pTab = GetBankTab(tabId)) { pTab->SetInfo(name, icon); SendBankList(session, tabId, true, true); } } void Guild::HandleSetMemberNote(WorldSession* session, std::string const& note, uint64 guid, bool isPublic) { // Player must have rights to set public/officer note if (!_HasRankRight(session->GetPlayer(), isPublic ? GR_RIGHT_EPNOTE : GR_RIGHT_EOFFNOTE)) SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PERMISSIONS); else if (Member* member = GetMember(guid)) { if (isPublic) member->SetPublicNote(note); else member->SetOfficerNote(note); HandleRoster(session); } } void Guild::HandleSetRankInfo(WorldSession* session, uint32 rankId, const std::string& name, uint32 rights, uint32 moneyPerDay, GuildBankRightsAndSlotsVec rightsAndSlots) { // Only leader can modify ranks if (!_IsLeader(session->GetPlayer())) SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PERMISSIONS); else if (RankInfo* rankInfo = GetRankInfo(rankId)) { sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Changed RankName to '%s', rights to 0x%08X", name.c_str(), rights); rankInfo->SetName(name); rankInfo->SetRights(rights); _SetRankBankMoneyPerDay(rankId, moneyPerDay); uint8 tabId = 0; for (GuildBankRightsAndSlotsVec::const_iterator itr = rightsAndSlots.begin(); itr != rightsAndSlots.end(); ++itr) _SetRankBankTabRightsAndSlots(rankId, tabId++, *itr); HandleQuery(session); HandleRoster(); // Broadcast for tab rights update } } void Guild::HandleBuyBankTab(WorldSession* session, uint8 tabId) { if (tabId != _GetPurchasedTabsSize()) return; uint32 tabCost = _GetGuildBankTabPrice(tabId) * GOLD; if (!tabCost) return; Player* player = session->GetPlayer(); if (!player->HasEnoughMoney(uint64(tabCost))) // Should not happen, this is checked by client return; if (!_CreateNewBankTab()) return; player->ModifyMoney(-int64(tabCost)); _SetRankBankMoneyPerDay(player->GetRank(), uint32(GUILD_WITHDRAW_MONEY_UNLIMITED)); _SetRankBankTabRightsAndSlots(player->GetRank(), tabId, GuildBankRightsAndSlots(GUILD_BANK_RIGHT_FULL, uint32(GUILD_WITHDRAW_SLOT_UNLIMITED))); HandleRoster(); // Broadcast for tab rights update SendBankList(session, tabId, false, true); } void Guild::HandleInviteMember(WorldSession* session, const std::string& name) { Player* pInvitee = sObjectAccessor->FindPlayerByName(name.c_str()); if (!pInvitee) { SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PLAYER_NOT_FOUND_S, name); return; } Player* player = session->GetPlayer(); // Do not show invitations from ignored players if (pInvitee->GetSocial()->HasIgnore(player->GetGUIDLow())) return; if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && pInvitee->GetTeam() != player->GetTeam()) { SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_NOT_ALLIED, name); return; } // Invited player cannot be in another guild /*if (pInvitee->GetGuildId()) { SendCommandResult(session, GUILD_INVITE, ERR_ALREADY_IN_GUILD_S, name); return; }*/ // Invited player cannot be invited if (pInvitee->GetGuildIdInvited()) { SendCommandResult(session, GUILD_INVITE_S, ERR_ALREADY_INVITED_TO_GUILD_S, name); return; } // Inviting player must have rights to invite if (!_HasRankRight(player, GR_RIGHT_INVITE)) { SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PERMISSIONS); return; } sLog->outDebug(LOG_FILTER_GUILD, "Player %s invited %s to join his Guild", player->GetName(), name.c_str()); pInvitee->SetGuildIdInvited(m_id); _LogEvent(GUILD_EVENT_LOG_INVITE_PLAYER, player->GetGUIDLow(), pInvitee->GetGUIDLow()); WorldPacket data(SMSG_GUILD_INVITE, 100); data << uint32(GetLevel()); data << uint32(m_emblemInfo.GetBorderStyle()); data << uint32(m_emblemInfo.GetBorderColor()); data << uint32(m_emblemInfo.GetStyle()); data << uint32(m_emblemInfo.GetBackgroundColor()); data << uint32(m_emblemInfo.GetColor()); ObjectGuid oldGuildGuid = MAKE_NEW_GUID(pInvitee->GetGuildId(), 0, pInvitee->GetGuildId() ? uint32(HIGHGUID_GUILD) : 0); ObjectGuid newGuildGuid = GetGUID(); data.WriteBit(newGuildGuid[3]); data.WriteBit(newGuildGuid[2]); data.WriteBits(pInvitee->GetGuildName().length(), 8); data.WriteBit(newGuildGuid[1]); data.WriteBit(oldGuildGuid[6]); data.WriteBit(oldGuildGuid[4]); data.WriteBit(oldGuildGuid[1]); data.WriteBit(oldGuildGuid[5]); data.WriteBit(oldGuildGuid[7]); data.WriteBit(oldGuildGuid[2]); data.WriteBit(newGuildGuid[7]); data.WriteBit(newGuildGuid[0]); data.WriteBit(newGuildGuid[6]); data.WriteBits(m_name.length(), 8); data.WriteBit(oldGuildGuid[3]); data.WriteBit(oldGuildGuid[0]); data.WriteBit(newGuildGuid[5]); data.WriteBits(strlen(player->GetName()), 7); data.WriteBit(newGuildGuid[4]); data.FlushBits(); data.WriteByteSeq(newGuildGuid[1]); data.WriteByteSeq(oldGuildGuid[3]); data.WriteByteSeq(newGuildGuid[6]); data.WriteByteSeq(oldGuildGuid[2]); data.WriteByteSeq(oldGuildGuid[1]); data.WriteByteSeq(newGuildGuid[0]); if (!pInvitee->GetGuildName().empty()) data.WriteString(pInvitee->GetGuildName()); data.WriteByteSeq(newGuildGuid[7]); data.WriteByteSeq(newGuildGuid[2]); data.WriteString(player->GetName()); data.WriteByteSeq(oldGuildGuid[7]); data.WriteByteSeq(oldGuildGuid[6]); data.WriteByteSeq(oldGuildGuid[5]); data.WriteByteSeq(oldGuildGuid[0]); data.WriteByteSeq(newGuildGuid[4]); data.WriteString(m_name); data.WriteByteSeq(newGuildGuid[5]); data.WriteByteSeq(newGuildGuid[3]); data.WriteByteSeq(oldGuildGuid[4]); pInvitee->GetSession()->SendPacket(&data); sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Sent (SMSG_GUILD_INVITE)"); } void Guild::HandleAcceptMember(WorldSession* session) { Player* player = session->GetPlayer(); if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != sObjectMgr->GetPlayerTeamByGUID(GetLeaderGUID())) return; if (AddMember(player->GetGUID())) { _LogEvent(GUILD_EVENT_LOG_JOIN_GUILD, player->GetGUIDLow()); _BroadcastEvent(GE_JOINED, player->GetGUID(), player->GetName()); sGuildFinderMgr->RemoveMembershipRequest(player->GetGUIDLow(), GUID_LOPART(this->GetGUID())); } } void Guild::HandleLeaveMember(WorldSession* session) { Player* player = session->GetPlayer(); // If leader is leaving if (_IsLeader(player)) { if (m_members.size() > 1) // Leader cannot leave if he is not the last member SendCommandResult(session, GUILD_QUIT_S, ERR_GUILD_LEADER_LEAVE); else if (GetLevel() >= sWorld->getIntConfig(CONFIG_GUILD_UNDELETABLE_LEVEL)) SendCommandResult(session, GUILD_QUIT_S, ERR_GUILD_UNDELETABLE_DUE_TO_LEVEL); else // Guild is disbanded if leader leaves. Disband(); } else { DeleteMember(player->GetGUID(), false, false); _LogEvent(GUILD_EVENT_LOG_LEAVE_GUILD, player->GetGUIDLow()); _BroadcastEvent(GE_LEFT, player->GetGUID(), player->GetName()); SendCommandResult(session, GUILD_QUIT_S, ERR_GUILD_COMMAND_SUCCESS, m_name); } } void Guild::HandleRemoveMember(WorldSession* session, uint64 guid) { Player* player = session->GetPlayer(); Player* removedPlayer = ObjectAccessor::FindPlayer(guid); Member* member = GetMember(guid); // Player must have rights to remove members if (!_HasRankRight(player, GR_RIGHT_REMOVE)) SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PERMISSIONS); // Removed player must be a member of the guild else if (member && removedPlayer) { // Guild masters cannot be removed if (member->IsRank(GR_GUILDMASTER)) SendCommandResult(session, GUILD_QUIT_S, ERR_GUILD_LEADER_LEAVE); // Do not allow to remove player with the same rank or higher else if (member->IsRankNotLower(player->GetRank())) SendCommandResult(session, GUILD_QUIT_S, ERR_GUILD_RANK_TOO_HIGH_S, removedPlayer->GetName()); else { // After call to DeleteMember pointer to member becomes invalid DeleteMember(guid, false, true); _LogEvent(GUILD_EVENT_LOG_UNINVITE_PLAYER, player->GetGUIDLow(), GUID_LOPART(guid)); _BroadcastEvent(GE_REMOVED, 0, removedPlayer->GetName(), player->GetName()); } } else if (removedPlayer) SendCommandResult(session, GUILD_QUIT_S, ERR_GUILD_COMMAND_SUCCESS, removedPlayer->GetName()); } void Guild::HandleUpdateMemberRank(WorldSession* session, uint64 targetGuid, bool demote) { Player* player = session->GetPlayer(); // Promoted player must be a member of guild if (Member* member = GetMember(targetGuid)) { if (!_HasRankRight(player, demote ? GR_RIGHT_DEMOTE : GR_RIGHT_PROMOTE)) { SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PERMISSIONS); return; } // Player cannot promote himself if (member->IsSamePlayer(player->GetGUID())) { SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_NAME_INVALID); return; } if (demote) { // Player can demote only lower rank members if (member->IsRankNotLower(player->GetRank())) { SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_RANK_TOO_HIGH_S, member->GetName()); return; } // Lowest rank cannot be demoted if (member->GetRankId() >= _GetLowestRankId()) { SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_RANK_TOO_LOW_S, member->GetName()); return; } } else { // Allow to promote only to lower rank than member's rank // member->GetRank() + 1 is the highest rank that current player can promote to if (member->IsRankNotLower(player->GetRank() + 1)) { SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_RANK_TOO_HIGH_S, member->GetName()); return; } } uint32 newRankId = member->GetRankId() + (demote ? 1 : -1); member->ChangeRank(newRankId); _LogEvent(demote ? GUILD_EVENT_LOG_DEMOTE_PLAYER : GUILD_EVENT_LOG_PROMOTE_PLAYER, player->GetGUIDLow(), GUID_LOPART(member->GetGUID()), newRankId); _BroadcastEvent(demote ? GE_DEMOTION : GE_PROMOTION, 0, player->GetName(), member->GetName().c_str(), _GetRankName(newRankId).c_str()); } } void Guild::HandleSetMemberRank(WorldSession* session, uint64 targetGuid, uint64 setterGuid, uint32 rank) { Player* player = session->GetPlayer(); // Promoted player must be a member of guild if (Member* member = GetMember(targetGuid)) { if (!_HasRankRight(player, rank > member->GetRankId() ? GR_RIGHT_DEMOTE : GR_RIGHT_PROMOTE)) { SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PERMISSIONS); return; } // Player cannot promote himself if (member->IsSamePlayer(player->GetGUID())) { SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_NAME_INVALID); return; } SendGuildRanksUpdate(setterGuid, targetGuid, rank); } } void Guild::HandleAddNewRank(WorldSession* session, std::string const& name) //, uint32 rankId) { if (_GetRanksSize() >= GUILD_RANKS_MAX_COUNT) return; // Only leader can add new rank if (!_IsLeader(session->GetPlayer())) SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PERMISSIONS); else { _CreateRank(name, GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); HandleQuery(session); HandleRoster(); // Broadcast for tab rights update } } void Guild::HandleRemoveRank(WorldSession* session, uint32 rankId) { // Cannot remove rank if total count is minimum allowed by the client if (_GetRanksSize() <= GUILD_RANKS_MIN_COUNT) return; // Only leader can delete ranks if (!_IsLeader(session->GetPlayer())) SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PERMISSIONS); else { // Delete bank rights for rank PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_RIGHTS_FOR_RANK); stmt->setUInt32(0, m_id); stmt->setUInt8(1, rankId); CharacterDatabase.Execute(stmt); // Delete rank stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_RANK); stmt->setUInt32(0, m_id); stmt->setUInt8(1, rankId); CharacterDatabase.Execute(stmt); m_ranks.erase(m_ranks.begin() + rankId-1); HandleQuery(session); HandleRoster(); // Broadcast for tab rights update } } void Guild::HandleMemberDepositMoney(WorldSession* session, uint32 amount, bool cashFlow /*=false*/) { Player* player = session->GetPlayer(); // Call script after validation and before money transfer. sScriptMgr->OnGuildMemberDepositMoney(this, player, amount); SQLTransaction trans = CharacterDatabase.BeginTransaction(); // Add money to bank _ModifyBankMoney(trans, amount, true); // Remove money from player player->ModifyMoney(-int64(amount)); player->SaveGoldToDB(trans); // Log GM action (TODO: move to scripts) if (!AccountMgr::IsPlayerAccount(player->GetSession()->GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE)) { sLog->outCommand(player->GetSession()->GetAccountId(), "GM %s (Account: %u) deposit money (Amount: %u) to guild bank (Guild ID %u)", player->GetName(), player->GetSession()->GetAccountId(), amount, m_id); } // Log guild bank event _LogBankEvent(trans, cashFlow ? GUILD_BANK_LOG_CASH_FLOW_DEPOSIT : GUILD_BANK_LOG_DEPOSIT_MONEY, uint8(0), player->GetGUIDLow(), amount); CharacterDatabase.CommitTransaction(trans); if (!cashFlow) SendBankList(session, 0, false, false); } bool Guild::HandleMemberWithdrawMoney(WorldSession* session, uint32 amount, bool repair) { if (m_bankMoney < amount) // Not enough money in bank return false; Player* player = session->GetPlayer(); if (!_HasRankRight(player, repair ? GR_RIGHT_WITHDRAW_REPAIR : GR_RIGHT_WITHDRAW_GOLD)) return false; uint32 remainingMoney = _GetMemberRemainingMoney(player->GetGUID()); if (!remainingMoney) return false; if (remainingMoney < amount) return false; // Call script after validation and before money transfer. sScriptMgr->OnGuildMemberWitdrawMoney(this, player, amount, repair); SQLTransaction trans = CharacterDatabase.BeginTransaction(); // Update remaining money amount if (remainingMoney < uint32(GUILD_WITHDRAW_MONEY_UNLIMITED)) if (Member* member = GetMember(player->GetGUID())) member->DecreaseBankRemainingValue(trans, GUILD_BANK_MAX_TABS, amount); // Remove money from bank _ModifyBankMoney(trans, amount, false); // Add money to player (if required) if (!repair) { player->ModifyMoney(amount); player->SaveGoldToDB(trans); } // Log guild bank event _LogBankEvent(trans, repair ? GUILD_BANK_LOG_REPAIR_MONEY : GUILD_BANK_LOG_WITHDRAW_MONEY, uint8(0), player->GetGUIDLow(), amount); CharacterDatabase.CommitTransaction(trans); SendMoneyInfo(session); if (!repair) SendBankList(session, 0, false, false); return true; } void Guild::HandleMemberLogout(WorldSession* session) { Player* player = session->GetPlayer(); if (Member* member = GetMember(player->GetGUID())) { member->SetStats(player); member->UpdateLogoutTime(); } _BroadcastEvent(GE_SIGNED_OFF, player->GetGUID(), player->GetName()); SaveToDB(); } void Guild::HandleDisband(WorldSession* session) { // Only leader can disband guild if (!_IsLeader(session->GetPlayer())) Guild::SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_PERMISSIONS); else if (GetLevel() >= sWorld->getIntConfig(CONFIG_GUILD_UNDELETABLE_LEVEL)) Guild::SendCommandResult(session, GUILD_INVITE_S, ERR_GUILD_UNDELETABLE_DUE_TO_LEVEL); else { Disband(); sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Guild Successfully Disbanded"); } } void Guild::HandleGuildPartyRequest(WorldSession* session) { Player* player = session->GetPlayer(); Group* group = player->GetGroup(); // Make sure player is a member of the guild and that he is in a group. if (!IsMember(player->GetGUID()) || !group) return; WorldPacket data(SMSG_GUILD_PARTY_STATE_RESPONSE, 13); data.WriteBit(player->GetMap()->GetOwnerGuildId(player->GetTeam()) == GetId()); // Is guild group data.FlushBits(); data << float(0.f); // Guild XP multiplier data << uint32(0); // Current guild members data << uint32(0); // Needed guild members session->SendPacket(&data); sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent (SMSG_GUILD_PARTY_STATE_RESPONSE)"); } void Guild::SendEventLog(WorldSession* session) const { WorldPacket data(SMSG_GUILD_EVENT_LOG_QUERY_RESULT, 1 + m_eventLog->GetSize() * (1 + 8 + 4)); m_eventLog->WritePacket(data); session->SendPacket(&data); sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Sent (SMSG_GUILD_EVENT_LOG_QUERY_RESULT)"); } void Guild::SendBankLog(WorldSession* session, uint8 tabId) const { // GUILD_BANK_MAX_TABS send by client for money log if (tabId < _GetPurchasedTabsSize() || tabId == GUILD_BANK_MAX_TABS) { LogHolder const* log = m_bankEventLog[tabId]; WorldPacket data(SMSG_GUILD_BANK_LOG_QUERY_RESULT, log->GetSize() * (4 * 4 + 1) + 1 + 1); data.WriteBit(GetLevel() >= 5 && tabId == GUILD_BANK_MAX_TABS); // has Cash Flow perk log->WritePacket(data); data << uint32(tabId); //if (tabId == GUILD_BANK_MAX_TABS && hasCashFlow) // data << uint64(cashFlowContribution); session->SendPacket(&data); sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Sent (SMSG_GUILD_BANK_LOG_QUERY_RESULT) for tab %u", tabId); } } void Guild::SendBankList(WorldSession* session, uint8 tabId, bool withContent, bool withTabInfo) const { ByteBuffer tabData; WorldPacket data(SMSG_GUILD_BANK_LIST, 500); data.WriteBit(0); uint32 itemCount = 0; if (withContent && _MemberHasTabRights(session->GetPlayer()->GetGUID(), tabId, GUILD_BANK_RIGHT_VIEW_TAB)) if (BankTab const* tab = GetBankTab(tabId)) for (uint8 slotId = 0; slotId < GUILD_BANK_MAX_SLOTS; ++slotId) if (tab->GetItem(slotId)) ++itemCount; data.WriteBits(itemCount, 20); data.WriteBits(withTabInfo ? _GetPurchasedTabsSize() : 0, 22); if (withContent && _MemberHasTabRights(session->GetPlayer()->GetGUID(), tabId, GUILD_BANK_RIGHT_VIEW_TAB)) { if (BankTab const* tab = GetBankTab(tabId)) { for (uint8 slotId = 0; slotId < GUILD_BANK_MAX_SLOTS; ++slotId) { if (Item* tabItem = tab->GetItem(slotId)) { data.WriteBit(0); uint32 enchants = 0; for (uint32 ench = 0; ench < MAX_ENCHANTMENT_SLOT; ++ench) { if (uint32 enchantId = tabItem->GetEnchantmentId(EnchantmentSlot(ench))) { tabData << uint32(enchantId); tabData << uint32(ench); ++enchants; } } data.WriteBits(enchants, 23); tabData << uint32(0); tabData << uint32(0); tabData << uint32(0); tabData << uint32(tabItem->GetCount()); // ITEM_FIELD_STACK_COUNT tabData << uint32(slotId); tabData << uint32(0); tabData << uint32(tabItem->GetEntry()); tabData << uint32(tabItem->GetItemRandomPropertyId()); tabData << uint32(abs(tabItem->GetSpellCharges())); // Spell charges tabData << uint32(tabItem->GetItemSuffixFactor()); // SuffixFactor } } } } if (withTabInfo) { for (uint8 i = 0; i < _GetPurchasedTabsSize(); ++i) { data.WriteBits(m_bankTabs[i]->GetIcon().length(), 9); data.WriteBits(m_bankTabs[i]->GetName().length(), 7); } } data.FlushBits(); if (withTabInfo) { for (uint8 i = 0; i < _GetPurchasedTabsSize(); ++i) { data.WriteString(m_bankTabs[i]->GetIcon()); data << uint32(i); data.WriteString(m_bankTabs[i]->GetName()); } } data << uint64(m_bankMoney); if (!tabData.empty()) data.append(tabData); data << uint32(tabId); data << uint32(_GetMemberRemainingSlots(session->GetPlayer()->GetGUID(), 0)); session->SendPacket(&data); sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Sent (SMSG_GUILD_BANK_LIST)"); } void Guild::SendBankTabText(WorldSession* session, uint8 tabId) const { if (BankTab const* tab = GetBankTab(tabId)) tab->SendText(this, session); } void Guild::SendPermissions(WorldSession* session) const { uint64 guid = session->GetPlayer()->GetGUID(); uint32 rankId = session->GetPlayer()->GetRank(); WorldPacket data(SMSG_GUILD_PERMISSIONS_QUERY_RESULTS, 4 * 15 + 1); data << uint32(rankId); data << uint32(_GetPurchasedTabsSize()); data << uint32(_GetRankRights(rankId)); data << uint32(_GetMemberRemainingMoney(guid)); data.WriteBits(GUILD_BANK_MAX_TABS, 23); for (uint8 tabId = 0; tabId < GUILD_BANK_MAX_TABS; ++tabId) { data << uint32(_GetRankBankTabRights(rankId, tabId)); data << uint32(_GetMemberRemainingSlots(guid, tabId)); } session->SendPacket(&data); sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Sent (SMSG_GUILD_PERMISSIONS_QUERY_RESULTS)"); } void Guild::SendMoneyInfo(WorldSession* session) const { WorldPacket data(SMSG_GUILD_BANK_MONEY_WITHDRAWN, 4); data << uint64(_GetMemberRemainingMoney(session->GetPlayer()->GetGUID())); session->SendPacket(&data); sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Sent SMSG_GUILD_BANK_MONEY_WITHDRAWN"); } void Guild::SendLoginInfo(WorldSession* session) const { /* Login sequence: SMSG_GUILD_EVENT - GE_MOTD SMSG_GUILD_RANK SMSG_GUILD_EVENT - GE_SIGNED_ON -- learn perks SMSG_GUILD_REPUTATION_WEEKLY_CAP SMSG_GUILD_ACHIEVEMENT_DATA SMSG_GUILD_MEMBER_DAILY_RESET // bank withdrawal reset */ WorldPacket data(SMSG_GUILD_EVENT, 1 + 1 + m_motd.size() + 1); data << uint8(GE_MOTD); data << uint8(1); data << m_motd; session->SendPacket(&data); sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Sent guild MOTD (SMSG_GUILD_EVENT)"); HandleGuildRanks(session); _BroadcastEvent(GE_SIGNED_ON, session->GetPlayer()->GetGUID(), session->GetPlayer()->GetName()); // Send to self separately, player is not in world yet and is not found by _BroadcastEvent data.Initialize(SMSG_GUILD_EVENT, 1 + 1 + strlen(session->GetPlayer()->GetName()) + 8); data << uint8(GE_SIGNED_ON); data << uint8(1); data << session->GetPlayer()->GetName(); data << uint64(session->GetPlayer()->GetGUID()); session->SendPacket(&data); data.Initialize(SMSG_GUILD_MEMBER_DAILY_RESET, 0); // tells the client to request bank withdrawal limit session->SendPacket(&data); if (!sWorld->getBoolConfig(CONFIG_GUILD_LEVELING_ENABLED)) return; for (uint32 i = 0; i < sGuildPerkSpellsStore.GetNumRows(); ++i) if (GuildPerkSpellsEntry const* entry = sGuildPerkSpellsStore.LookupEntry(i)) if (entry->Level <= GetLevel()) session->GetPlayer()->learnSpell(entry->SpellId, true); SendGuildReputationWeeklyCap(session); GetAchievementMgr().SendAllAchievementData(session->GetPlayer()); } void Guild::SendGuildReputationWeeklyCap(WorldSession* session) const { if (Member const* member = GetMember(session->GetPlayer()->GetGUID())) { WorldPacket data(SMSG_GUILD_REPUTATION_WEEKLY_CAP, 4); data << uint32(member->GetRemainingWeeklyReputation()); session->SendPacket(&data); } } // Loading methods bool Guild::LoadFromDB(Field* fields) { m_id = fields[0].GetUInt32(); m_name = fields[1].GetString(); m_leaderGuid = MAKE_NEW_GUID(fields[2].GetUInt32(), 0, HIGHGUID_PLAYER); m_emblemInfo.LoadFromDB(fields); m_info = fields[8].GetString(); m_motd = fields[9].GetString(); m_createdDate = time_t(fields[10].GetUInt32()); m_bankMoney = fields[11].GetUInt64(); _level = fields[12].GetUInt32(); _experience = fields[13].GetUInt64(); _todayExperience = fields[14].GetUInt64(); uint8 purchasedTabs = uint8(fields[15].GetUInt64()); if (purchasedTabs > GUILD_BANK_MAX_TABS) purchasedTabs = GUILD_BANK_MAX_TABS; m_bankTabs.resize(purchasedTabs); for (uint8 i = 0; i < purchasedTabs; ++i) m_bankTabs[i] = new BankTab(m_id, i); _CreateLogHolders(); return true; } void Guild::LoadRankFromDB(Field* fields) { RankInfo rankInfo(m_id); rankInfo.LoadFromDB(fields); m_ranks.push_back(rankInfo); } bool Guild::LoadMemberFromDB(Field* fields) { uint32 lowguid = fields[1].GetUInt32(); Member *member = new Member(m_id, MAKE_NEW_GUID(lowguid, 0, HIGHGUID_PLAYER), fields[2].GetUInt8()); if (!member->LoadFromDB(fields)) { _DeleteMemberFromDB(lowguid); delete member; return false; } m_members[lowguid] = member; return true; } void Guild::LoadBankRightFromDB(Field* fields) { // rights slots GuildBankRightsAndSlots rightsAndSlots(fields[3].GetUInt8(), fields[4].GetUInt32()); // rankId tabId _SetRankBankTabRightsAndSlots(fields[2].GetUInt8(), fields[1].GetUInt8(), rightsAndSlots, false); } bool Guild::LoadEventLogFromDB(Field* fields) { if (m_eventLog->CanInsert()) { m_eventLog->LoadEvent(new EventLogEntry( m_id, // guild id fields[1].GetUInt32(), // guid time_t(fields[6].GetUInt32()), // timestamp GuildEventLogTypes(fields[2].GetUInt8()), // event type fields[3].GetUInt32(), // player guid 1 fields[4].GetUInt32(), // player guid 2 fields[5].GetUInt8())); // rank return true; } return false; } bool Guild::LoadBankEventLogFromDB(Field* fields) { uint8 dbTabId = fields[1].GetUInt8(); bool isMoneyTab = (dbTabId == GUILD_BANK_MONEY_LOGS_TAB); if (dbTabId < _GetPurchasedTabsSize() || isMoneyTab) { uint8 tabId = isMoneyTab ? uint8(GUILD_BANK_MAX_TABS) : dbTabId; LogHolder* pLog = m_bankEventLog[tabId]; if (pLog->CanInsert()) { uint32 guid = fields[2].GetUInt32(); GuildBankEventLogTypes eventType = GuildBankEventLogTypes(fields[3].GetUInt8()); if (BankEventLogEntry::IsMoneyEvent(eventType)) { if (!isMoneyTab) { sLog->outError(LOG_FILTER_GUILD, "GuildBankEventLog ERROR: MoneyEvent(LogGuid: %u, Guild: %u) does not belong to money tab (%u), ignoring...", guid, m_id, dbTabId); return false; } } else if (isMoneyTab) { sLog->outError(LOG_FILTER_GUILD, "GuildBankEventLog ERROR: non-money event (LogGuid: %u, Guild: %u) belongs to money tab, ignoring...", guid, m_id); return false; } pLog->LoadEvent(new BankEventLogEntry( m_id, // guild id guid, // guid time_t(fields[8].GetUInt32()), // timestamp dbTabId, // tab id eventType, // event type fields[4].GetUInt32(), // player guid fields[5].GetUInt32(), // item or money fields[6].GetUInt16(), // itam stack count fields[7].GetUInt8())); // dest tab id } } return true; } bool Guild::LoadBankTabFromDB(Field* fields) { uint8 tabId = fields[1].GetUInt8(); if (tabId >= _GetPurchasedTabsSize()) { sLog->outError(LOG_FILTER_GUILD, "Invalid tab (tabId: %u) in guild bank, skipped.", tabId); return false; } return m_bankTabs[tabId]->LoadFromDB(fields); } bool Guild::LoadBankItemFromDB(Field* fields) { uint8 tabId = fields[12].GetUInt8(); if (tabId >= _GetPurchasedTabsSize()) { sLog->outError(LOG_FILTER_GUILD, "Invalid tab for item (GUID: %u, id: #%u) in guild bank, skipped.", fields[14].GetUInt32(), fields[15].GetUInt32()); return false; } return m_bankTabs[tabId]->LoadItemFromDB(fields); } // Validates guild data loaded from database. Returns false if guild should be deleted. bool Guild::Validate() { // Validate ranks data // GUILD RANKS represent a sequence starting from 0 = GUILD_MASTER (ALL PRIVILEGES) to max 9 (lowest privileges). // The lower rank id is considered higher rank - so promotion does rank-- and demotion does rank++ // Between ranks in sequence cannot be gaps - so 0, 1, 2, 4 is impossible // Min ranks count is 5 and max is 10. bool broken_ranks = false; if (_GetRanksSize() < GUILD_RANKS_MIN_COUNT || _GetRanksSize() > GUILD_RANKS_MAX_COUNT) { sLog->outError(LOG_FILTER_GUILD, "Guild %u has invalid number of ranks, creating new...", m_id); broken_ranks = true; } else { for (uint8 rankId = 0; rankId < _GetRanksSize(); ++rankId) { RankInfo* rankInfo = GetRankInfo(rankId); if (rankInfo->GetId() != rankId) { sLog->outError(LOG_FILTER_GUILD, "Guild %u has broken rank id %u, creating default set of ranks...", m_id, rankId); broken_ranks = true; } } } if (broken_ranks) { m_ranks.clear(); _CreateDefaultGuildRanks(DEFAULT_LOCALE); } // Validate members' data for (Members::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if (itr->second->GetRankId() > _GetRanksSize()) itr->second->ChangeRank(_GetLowestRankId()); // Repair the structure of the guild. // If the guildmaster doesn't exist or isn't member of the guild // attempt to promote another member. Member* pLeader = GetMember(m_leaderGuid); if (!pLeader) { DeleteMember(m_leaderGuid); // If no more members left, disband guild if (m_members.empty()) { Disband(); return false; } } else if (!pLeader->IsRank(GR_GUILDMASTER)) _SetLeaderGUID(pLeader); // Check config if multiple guildmasters are allowed if (!ConfigMgr::GetBoolDefault("Guild.AllowMultipleGuildMaster", 0)) for (Members::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if (itr->second->GetRankId() == GR_GUILDMASTER && !itr->second->IsSamePlayer(m_leaderGuid)) itr->second->ChangeRank(GR_OFFICER); _UpdateAccountsNumber(); return true; } // Broadcasts void Guild::BroadcastToGuild(WorldSession* session, bool officerOnly, const std::string& msg, uint32 language) const { if (session && session->GetPlayer() && _HasRankRight(session->GetPlayer(), officerOnly ? GR_RIGHT_OFFCHATSPEAK : GR_RIGHT_GCHATSPEAK)) { WorldPacket data; ChatHandler::FillMessageData(&data, session, officerOnly ? CHAT_MSG_OFFICER : CHAT_MSG_GUILD, language, NULL, 0, msg.c_str(), NULL); for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if (Player* player = itr->second->FindPlayer()) if (player->GetSession() && _HasRankRight(player, officerOnly ? GR_RIGHT_OFFCHATLISTEN : GR_RIGHT_GCHATLISTEN) && !player->GetSocial()->HasIgnore(session->GetPlayer()->GetGUIDLow())) player->GetSession()->SendPacket(&data); } } void Guild::BroadcastAddonToGuild(WorldSession* session, bool officerOnly, const std::string& msg, const std::string& prefix) const { if (session && session->GetPlayer() && _HasRankRight(session->GetPlayer(), officerOnly ? GR_RIGHT_OFFCHATSPEAK : GR_RIGHT_GCHATSPEAK)) { WorldPacket data; ChatHandler::FillMessageData(&data, session, officerOnly ? CHAT_MSG_OFFICER : CHAT_MSG_GUILD, CHAT_MSG_ADDON, NULL, 0, msg.c_str(), NULL, prefix.c_str()); for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if (Player* player = itr->second->FindPlayer()) if (player->GetSession() && _HasRankRight(player, officerOnly ? GR_RIGHT_OFFCHATLISTEN : GR_RIGHT_GCHATLISTEN) && !player->GetSocial()->HasIgnore(session->GetPlayer()->GetGUIDLow()) && player->GetSession()->IsAddonRegistered(prefix)) player->GetSession()->SendPacket(&data); } } void Guild::BroadcastPacketToRank(WorldPacket* packet, uint8 rankId) const { for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if (itr->second->IsRank(rankId)) if (Player* player = itr->second->FindPlayer()) player->GetSession()->SendPacket(packet); } void Guild::BroadcastPacket(WorldPacket* packet) const { for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if (Player* player = itr->second->FindPlayer()) player->GetSession()->SendPacket(packet); } // Members handling bool Guild::AddMember(uint64 guid, uint8 rankId) { Player* player = ObjectAccessor::FindPlayer(guid); // Player cannot be in guild if (player) { if (player->GetGuildId() != 0) return false; } else if (Player::GetGuildIdFromDB(guid) != 0) return false; // Remove all player signs from another petitions // This will be prevent attempt to join many guilds and corrupt guild data integrity Player::RemovePetitionsAndSigns(guid, GUILD_CHARTER_TYPE); uint32 lowguid = GUID_LOPART(guid); // If rank was not passed, assign lowest possible rank if (rankId == GUILD_RANK_NONE) rankId = _GetLowestRankId(); Member* member = new Member(m_id, guid, rankId); if (player) member->SetStats(player); else { bool ok = false; // Player must exist PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_DATA_FOR_GUILD); stmt->setUInt32(0, lowguid); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) { Field* fields = result->Fetch(); member->SetStats( fields[0].GetString(), fields[1].GetUInt8(), fields[2].GetUInt8(), fields[3].GetUInt16(), fields[4].GetUInt32()); ok = member->CheckStats(); } if (!ok) { delete member; return false; } } m_members[lowguid] = member; SQLTransaction trans(NULL); member->SaveToDB(trans); // If player not in game data in will be loaded from guild tables, so no need to update it! if (player) { player->SetInGuild(m_id); player->SetRank(rankId); player->SetGuildLevel(GetLevel()); player->SetGuildIdInvited(0); if (sWorld->getBoolConfig(CONFIG_GUILD_LEVELING_ENABLED)) { for (uint32 i = 0; i < sGuildPerkSpellsStore.GetNumRows(); ++i) if (GuildPerkSpellsEntry const* entry = sGuildPerkSpellsStore.LookupEntry(i)) if (entry->Level <= GetLevel()) player->learnSpell(entry->SpellId, true); } } _UpdateAccountsNumber(); // Call scripts if member was succesfully added (and stored to database) sScriptMgr->OnGuildAddMember(this, player, rankId); return true; } void Guild::DeleteMember(uint64 guid, bool isDisbanding, bool isKicked) { uint32 lowguid = GUID_LOPART(guid); Player* player = ObjectAccessor::FindPlayer(guid); // Guild master can be deleted when loading guild and guid doesn't exist in characters table // or when he is removed from guild by gm command if (m_leaderGuid == guid && !isDisbanding) { Member* oldLeader = NULL; Member* newLeader = NULL; for (Guild::Members::iterator i = m_members.begin(); i != m_members.end(); ++i) { if (i->first == lowguid) oldLeader = i->second; else if (!newLeader || newLeader->GetRankId() > i->second->GetRankId()) newLeader = i->second; } if (!newLeader) { Disband(); return; } _SetLeaderGUID(newLeader); // If player not online data in data field will be loaded from guild tabs no need to update it !! if (Player* newLeaderPlayer = newLeader->FindPlayer()) newLeaderPlayer->SetRank(GR_GUILDMASTER); // If leader does not exist (at guild loading with deleted leader) do not send broadcasts if (oldLeader) { _BroadcastEvent(GE_LEADER_CHANGED, 0, oldLeader->GetName().c_str(), newLeader->GetName().c_str()); _BroadcastEvent(GE_LEFT, guid, oldLeader->GetName().c_str()); } } // Call script on remove before member is actually removed from guild (and database) sScriptMgr->OnGuildRemoveMember(this, player, isDisbanding, isKicked); if (Member* member = GetMember(guid)) delete member; m_members.erase(lowguid); // If player not online data in data field will be loaded from guild tabs no need to update it !! if (player) { player->SetInGuild(0); player->SetRank(0); player->SetGuildLevel(0); for (uint32 i = 0; i < sGuildPerkSpellsStore.GetNumRows(); ++i) if (GuildPerkSpellsEntry const* entry = sGuildPerkSpellsStore.LookupEntry(i)) if (entry->Level <= GetLevel()) player->removeSpell(entry->SpellId, false, false); } _DeleteMemberFromDB(lowguid); if (!isDisbanding) _UpdateAccountsNumber(); } bool Guild::ChangeMemberRank(uint64 guid, uint8 newRank) { if (newRank <= _GetLowestRankId()) // Validate rank (allow only existing ranks) if (Member* member = GetMember(guid)) { member->ChangeRank(newRank); return true; } return false; } bool Guild::IsMember(uint64 guid) { Members::const_iterator itr = m_members.find(GUID_LOPART(guid)); return itr != m_members.end(); } // Bank (items move) void Guild::SwapItems(Player* player, uint8 tabId, uint8 slotId, uint8 destTabId, uint8 destSlotId, uint32 splitedAmount) { if (tabId >= _GetPurchasedTabsSize() || slotId >= GUILD_BANK_MAX_SLOTS || destTabId >= _GetPurchasedTabsSize() || destSlotId >= GUILD_BANK_MAX_SLOTS) return; if (tabId == destTabId && slotId == destSlotId) return; BankMoveItemData from(this, player, tabId, slotId); BankMoveItemData to(this, player, destTabId, destSlotId); _MoveItems(&from, &to, splitedAmount); } void Guild::SwapItemsWithInventory(Player* player, bool toChar, uint8 tabId, uint8 slotId, uint8 playerBag, uint8 playerSlotId, uint32 splitedAmount) { if ((slotId >= GUILD_BANK_MAX_SLOTS && slotId != NULL_SLOT) || tabId >= _GetPurchasedTabsSize()) return; BankMoveItemData bankData(this, player, tabId, slotId); PlayerMoveItemData charData(this, player, playerBag, playerSlotId); if (toChar) _MoveItems(&bankData, &charData, splitedAmount); else _MoveItems(&charData, &bankData, splitedAmount); } // Bank tabs void Guild::SetBankTabText(uint8 tabId, const std::string& text) { if (BankTab* pTab = GetBankTab(tabId)) { pTab->SetText(text); pTab->SendText(this, NULL); } } // Private methods void Guild::_CreateLogHolders() { m_eventLog = new LogHolder(m_id, sWorld->getIntConfig(CONFIG_GUILD_EVENT_LOG_COUNT)); for (uint8 tabId = 0; tabId <= GUILD_BANK_MAX_TABS; ++tabId) m_bankEventLog[tabId] = new LogHolder(m_id, sWorld->getIntConfig(CONFIG_GUILD_BANK_EVENT_LOG_COUNT)); } bool Guild::_CreateNewBankTab() { if (_GetPurchasedTabsSize() >= GUILD_BANK_MAX_TABS) return false; uint8 tabId = _GetPurchasedTabsSize(); // Next free id m_bankTabs.push_back(new BankTab(m_id, tabId)); PreparedStatement* stmt = NULL; SQLTransaction trans = CharacterDatabase.BeginTransaction(); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_TAB); stmt->setUInt32(0, m_id); stmt->setUInt8 (1, tabId); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_TAB); stmt->setUInt32(0, m_id); stmt->setUInt8 (1, tabId); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); return true; } void Guild::_CreateDefaultGuildRanks(LocaleConstant loc) { PreparedStatement* stmt = NULL; stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_RANKS); stmt->setUInt32(0, m_id); CharacterDatabase.Execute(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_RIGHTS); stmt->setUInt32(0, m_id); CharacterDatabase.Execute(stmt); _CreateRank(sObjectMgr->GetTrinityString(LANG_GUILD_MASTER, loc), GR_RIGHT_ALL); _CreateRank(sObjectMgr->GetTrinityString(LANG_GUILD_OFFICER, loc), GR_RIGHT_ALL); _CreateRank(sObjectMgr->GetTrinityString(LANG_GUILD_VETERAN, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); _CreateRank(sObjectMgr->GetTrinityString(LANG_GUILD_MEMBER, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); _CreateRank(sObjectMgr->GetTrinityString(LANG_GUILD_INITIATE, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); } void Guild::_CreateRank(const std::string& name, uint32 rights) { uint32 newRankId = _GetRanksSize(); if (newRankId >= GUILD_RANKS_MAX_COUNT) return; // Ranks represent sequence 0, 1, 2, ... where 0 means guildmaster RankInfo info(m_id, newRankId, name, rights, 0); m_ranks.push_back(info); SQLTransaction trans = CharacterDatabase.BeginTransaction(); for (uint8 i = 0; i < _GetPurchasedTabsSize(); ++i) { // Create bank rights with default values PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT_DEFAULT); stmt->setUInt32(0, m_id); stmt->setUInt8 (1, i); stmt->setUInt8 (2, newRankId); trans->Append(stmt); } info.SaveToDB(trans); CharacterDatabase.CommitTransaction(trans); } // Updates the number of accounts that are in the guild // Player may have many characters in the guild, but with the same account void Guild::_UpdateAccountsNumber() { // We use a set to be sure each element will be unique std::set accountsIdSet; for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) accountsIdSet.insert(itr->second->GetAccountId()); m_accountsNumber = accountsIdSet.size(); } // Detects if player is the guild master. // Check both leader guid and player's rank (otherwise multiple feature with // multiple guild masters won't work) bool Guild::_IsLeader(Player* player) const { if (player->GetGUID() == m_leaderGuid) return true; if (const Member* member = GetMember(player->GetGUID())) return member->IsRank(GR_GUILDMASTER); return false; } void Guild::_DeleteBankItems(SQLTransaction& trans, bool removeItemsFromDB) { for (uint8 tabId = 0; tabId < _GetPurchasedTabsSize(); ++tabId) { m_bankTabs[tabId]->Delete(trans, removeItemsFromDB); delete m_bankTabs[tabId]; m_bankTabs[tabId] = NULL; } m_bankTabs.clear(); } bool Guild::_ModifyBankMoney(SQLTransaction& trans, uint64 amount, bool add) { if (add) m_bankMoney += amount; else { // Check if there is enough money in bank. if (m_bankMoney < amount) return false; m_bankMoney -= amount; } PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_MONEY); stmt->setUInt64(0, m_bankMoney); stmt->setUInt32(1, m_id); trans->Append(stmt); return true; } void Guild::_SetLeaderGUID(Member* pLeader) { if (!pLeader) return; m_leaderGuid = pLeader->GetGUID(); pLeader->ChangeRank(GR_GUILDMASTER); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_LEADER); stmt->setUInt32(0, GUID_LOPART(m_leaderGuid)); stmt->setUInt32(1, m_id); CharacterDatabase.Execute(stmt); } void Guild::_SetRankBankMoneyPerDay(uint32 rankId, uint32 moneyPerDay) { if (RankInfo* rankInfo = GetRankInfo(rankId)) { for (Members::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if (itr->second->IsRank(rankId)) itr->second->ResetMoneyTime(); rankInfo->SetBankMoneyPerDay(moneyPerDay); } } void Guild::_SetRankBankTabRightsAndSlots(uint32 rankId, uint8 tabId, GuildBankRightsAndSlots rightsAndSlots, bool saveToDB) { if (tabId >= _GetPurchasedTabsSize()) return; if (RankInfo* rankInfo = GetRankInfo(rankId)) { for (Members::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if (itr->second->IsRank(rankId)) itr->second->ResetTabTimes(); rankInfo->SetBankTabSlotsAndRights(tabId, rightsAndSlots, saveToDB); } } inline std::string Guild::_GetRankName(uint32 rankId) const { if (const RankInfo* rankInfo = GetRankInfo(rankId)) return rankInfo->GetName(); return ""; } inline uint32 Guild::_GetRankRights(uint32 rankId) const { if (const RankInfo* rankInfo = GetRankInfo(rankId)) return rankInfo->GetRights(); return 0; } inline uint32 Guild::_GetRankBankMoneyPerDay(uint32 rankId) const { if (const RankInfo* rankInfo = GetRankInfo(rankId)) return rankInfo->GetBankMoneyPerDay(); return 0; } inline uint32 Guild::_GetRankBankTabSlotsPerDay(uint32 rankId, uint8 tabId) const { if (tabId < _GetPurchasedTabsSize()) if (const RankInfo* rankInfo = GetRankInfo(rankId)) return rankInfo->GetBankTabSlotsPerDay(tabId); return 0; } inline uint32 Guild::_GetRankBankTabRights(uint32 rankId, uint8 tabId) const { if (const RankInfo* rankInfo = GetRankInfo(rankId)) return rankInfo->GetBankTabRights(tabId); return 0; } inline uint32 Guild::_GetMemberRemainingSlots(uint64 guid, uint8 tabId) const { if (const Member* member = GetMember(guid)) return member->GetBankRemainingValue(tabId, this); return 0; } inline uint32 Guild::_GetMemberRemainingMoney(uint64 guid) const { if (const Member* member = GetMember(guid)) return member->GetBankRemainingValue(GUILD_BANK_MAX_TABS, this); return 0; } inline void Guild::_DecreaseMemberRemainingSlots(SQLTransaction& trans, uint64 guid, uint8 tabId) { // Remaining slots must be more then 0 if (uint32 remainingSlots = _GetMemberRemainingSlots(guid, tabId)) // Ignore guild master if (remainingSlots < uint32(GUILD_WITHDRAW_SLOT_UNLIMITED)) if (Member* member = GetMember(guid)) member->DecreaseBankRemainingValue(trans, tabId, 1); } inline bool Guild::_MemberHasTabRights(uint64 guid, uint8 tabId, uint32 rights) const { if (const Member* member = GetMember(guid)) { // Leader always has full rights if (member->IsRank(GR_GUILDMASTER) || m_leaderGuid == guid) return true; return (_GetRankBankTabRights(member->GetRankId(), tabId) & rights) == rights; } return false; } // Add new event log record inline void Guild::_LogEvent(GuildEventLogTypes eventType, uint32 playerGuid1, uint32 playerGuid2, uint8 newRank) { SQLTransaction trans = CharacterDatabase.BeginTransaction(); m_eventLog->AddEvent(trans, new EventLogEntry(m_id, m_eventLog->GetNextGUID(), eventType, playerGuid1, playerGuid2, newRank)); CharacterDatabase.CommitTransaction(trans); sScriptMgr->OnGuildEvent(this, uint8(eventType), playerGuid1, playerGuid2, newRank); } // Add new bank event log record void Guild::_LogBankEvent(SQLTransaction& trans, GuildBankEventLogTypes eventType, uint8 tabId, uint32 lowguid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId) { if (tabId > GUILD_BANK_MAX_TABS) return; // not logging moves within the same tab if (eventType == GUILD_BANK_LOG_MOVE_ITEM && tabId == destTabId) return; uint8 dbTabId = tabId; if (BankEventLogEntry::IsMoneyEvent(eventType)) { tabId = GUILD_BANK_MAX_TABS; dbTabId = GUILD_BANK_MONEY_LOGS_TAB; } LogHolder* pLog = m_bankEventLog[tabId]; pLog->AddEvent(trans, new BankEventLogEntry(m_id, pLog->GetNextGUID(), eventType, dbTabId, lowguid, itemOrMoney, itemStackCount, destTabId)); sScriptMgr->OnGuildBankEvent(this, uint8(eventType), tabId, lowguid, itemOrMoney, itemStackCount, destTabId); } inline Item* Guild::_GetItem(uint8 tabId, uint8 slotId) const { if (const BankTab* tab = GetBankTab(tabId)) return tab->GetItem(slotId); return NULL; } inline void Guild::_RemoveItem(SQLTransaction& trans, uint8 tabId, uint8 slotId) { if (BankTab* pTab = GetBankTab(tabId)) pTab->SetItem(trans, slotId, NULL); } void Guild::_MoveItems(MoveItemData* pSrc, MoveItemData* pDest, uint32 splitedAmount) { // 1. Initialize source item if (!pSrc->InitItem()) return; // No source item // 2. Check source item if (!pSrc->CheckItem(splitedAmount)) return; // Source item or splited amount is invalid /* if (pItemSrc->GetCount() == 0) { sLog->outFatal(LOG_FILTER_GUILD, "Guild::SwapItems: Player %s(GUIDLow: %u) tried to move item %u from tab %u slot %u to tab %u slot %u, but item %u has a stack of zero!", player->GetName(), player->GetGUIDLow(), pItemSrc->GetEntry(), tabId, slotId, destTabId, destSlotId, pItemSrc->GetEntry()); //return; // Commented out for now, uncomment when it's verified that this causes a crash!! } // */ // 3. Check destination rights if (!pDest->HasStoreRights(pSrc)) return; // Player has no rights to store item in destination // 4. Check source withdraw rights if (!pSrc->HasWithdrawRights(pDest)) return; // Player has no rights to withdraw items from source // 5. Check split if (splitedAmount) { // 5.1. Clone source item if (!pSrc->CloneItem(splitedAmount)) return; // Item could not be cloned // 5.2. Move splited item to destination _DoItemsMove(pSrc, pDest, true, splitedAmount); } else // 6. No split { // 6.1. Try to merge items in destination (pDest->GetItem() == NULL) if (!_DoItemsMove(pSrc, pDest, false)) // Item could not be merged { // 6.2. Try to swap items // 6.2.1. Initialize destination item if (!pDest->InitItem()) return; // 6.2.2. Check rights to store item in source (opposite direction) if (!pSrc->HasStoreRights(pDest)) return; // Player has no rights to store item in source (opposite direction) if (!pDest->HasWithdrawRights(pSrc)) return; // Player has no rights to withdraw item from destination (opposite direction) // 6.2.3. Swap items (pDest->GetItem() != NULL) _DoItemsMove(pSrc, pDest, true); } } // 7. Send changes _SendBankContentUpdate(pSrc, pDest); } bool Guild::_DoItemsMove(MoveItemData* pSrc, MoveItemData* pDest, bool sendError, uint32 splitedAmount) { Item* pDestItem = pDest->GetItem(); bool swap = (pDestItem != NULL); Item* pSrcItem = pSrc->GetItem(splitedAmount); // 1. Can store source item in destination if (!pDest->CanStore(pSrcItem, swap, sendError)) return false; // 2. Can store destination item in source if (swap) if (!pSrc->CanStore(pDestItem, true, true)) return false; // GM LOG (TODO: move to scripts) pDest->LogAction(pSrc); if (swap) pSrc->LogAction(pDest); SQLTransaction trans = CharacterDatabase.BeginTransaction(); // 3. Log bank events pDest->LogBankEvent(trans, pSrc, pSrcItem->GetCount()); if (swap) pSrc->LogBankEvent(trans, pDest, pDestItem->GetCount()); // 4. Remove item from source pSrc->RemoveItem(trans, pDest, splitedAmount); // 5. Remove item from destination if (swap) pDest->RemoveItem(trans, pSrc); // 6. Store item in destination pDest->StoreItem(trans, pSrcItem); // 7. Store item in source if (swap) pSrc->StoreItem(trans, pDestItem); CharacterDatabase.CommitTransaction(trans); return true; } void Guild::_SendBankContentUpdate(MoveItemData* pSrc, MoveItemData* pDest) const { ASSERT(pSrc->IsBank() || pDest->IsBank()); uint8 tabId = 0; SlotIds slots; if (pSrc->IsBank()) // B -> { tabId = pSrc->GetContainer(); slots.insert(pSrc->GetSlotId()); if (pDest->IsBank()) // B -> B { // Same tab - add destination slots to collection if (pDest->GetContainer() == pSrc->GetContainer()) pDest->CopySlots(slots); else // Different tabs - send second message { SlotIds destSlots; pDest->CopySlots(destSlots); _SendBankContentUpdate(pDest->GetContainer(), destSlots); } } } else if (pDest->IsBank()) // C -> B { tabId = pDest->GetContainer(); pDest->CopySlots(slots); } _SendBankContentUpdate(tabId, slots); } void Guild::_SendBankContentUpdate(uint8 tabId, SlotIds slots) const { if (BankTab const* tab = GetBankTab(tabId)) { ByteBuffer tabData; WorldPacket data(SMSG_GUILD_BANK_LIST, 1200); data.WriteBit(0); data.WriteBits(slots.size(), 20); // Item count data.WriteBits(0, 22); // Tab count for (SlotIds::const_iterator itr = slots.begin(); itr != slots.end(); ++itr) { data.WriteBit(0); Item const* tabItem = tab->GetItem(*itr); uint32 enchantCount = 0; if (tabItem) { for (uint32 enchSlot = 0; enchSlot < MAX_ENCHANTMENT_SLOT; ++enchSlot) { if (uint32 enchantId = tabItem->GetEnchantmentId(EnchantmentSlot(enchSlot))) { tabData << uint32(enchantId); tabData << uint32(enchSlot); ++enchantCount; } } } data.WriteBits(enchantCount, 23); // enchantment count tabData << uint32(0); tabData << uint32(0); tabData << uint32(0); tabData << uint32(tabItem ? tabItem->GetCount() : 0); // ITEM_FIELD_STACK_COUNT tabData << uint32(*itr); tabData << uint32(0); tabData << uint32(tabItem ? tabItem->GetEntry() : 0); tabData << uint32(tabItem ? tabItem->GetItemRandomPropertyId() : 0); tabData << uint32(tabItem ? abs(tabItem->GetSpellCharges()) : 0); // Spell charges tabData << uint32(tabItem ? tabItem->GetItemSuffixFactor() : 0); // SuffixFactor } data.FlushBits(); data << uint64(m_bankMoney); if (!tabData.empty()) data.append(tabData); data << uint32(tabId); size_t rempos = data.wpos(); data << uint32(0); // Item withdraw amount, will be filled later for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if (_MemberHasTabRights(itr->second->GetGUID(), tabId, GUILD_BANK_RIGHT_VIEW_TAB)) if (Player* player = itr->second->FindPlayer()) { data.put(rempos, uint32(_GetMemberRemainingSlots(player->GetGUID(), tabId))); player->GetSession()->SendPacket(&data); } sLog->outDebug(LOG_FILTER_GUILD, "WORLD: Sent (SMSG_GUILD_BANK_LIST)"); } } void Guild::_BroadcastEvent(GuildEvents guildEvent, uint64 guid, const char* param1, const char* param2, const char* param3) const { uint8 count = !param3 ? (!param2 ? (!param1 ? 0 : 1) : 2) : 3; WorldPacket data(SMSG_GUILD_EVENT, 1 + 1 + count + (guid ? 8 : 0)); data << uint8(guildEvent); data << uint8(count); if (param3) data << param1 << param2 << param3; else if (param2) data << param1 << param2; else if (param1) data << param1; if (guid) data << uint64(guid); BroadcastPacket(&data); sLog->outDebug(LOG_FILTER_GUILD, "SMSG_GUILD_EVENT [Broadcast] Event: %u", guildEvent); } void Guild::SendGuildRanksUpdate(uint64 setterGuid, uint64 targetGuid, uint32 rank) { ObjectGuid tarGuid = targetGuid; ObjectGuid setGuid = setterGuid; Member* member = GetMember(targetGuid); ASSERT(member); WorldPacket data(SMSG_GUILD_RANKS_UPDATE, 100); data.WriteBit(setGuid[7]); data.WriteBit(setGuid[2]); data.WriteBit(tarGuid[2]); data.WriteBit(setGuid[1]); data.WriteBit(tarGuid[1]); data.WriteBit(tarGuid[7]); data.WriteBit(tarGuid[0]); data.WriteBit(tarGuid[5]); data.WriteBit(tarGuid[4]); data.WriteBit(rank < member->GetRankId()); // 1 == higher, 0 = lower? data.WriteBit(setGuid[5]); data.WriteBit(setGuid[0]); data.WriteBit(tarGuid[6]); data.WriteBit(setGuid[3]); data.WriteBit(setGuid[6]); data.WriteBit(tarGuid[3]); data.WriteBit(setGuid[4]); data.FlushBits(); data << uint32(rank); data.WriteByteSeq(setGuid[3]); data.WriteByteSeq(tarGuid[7]); data.WriteByteSeq(setGuid[6]); data.WriteByteSeq(setGuid[2]); data.WriteByteSeq(tarGuid[5]); data.WriteByteSeq(tarGuid[0]); data.WriteByteSeq(setGuid[7]); data.WriteByteSeq(setGuid[5]); data.WriteByteSeq(tarGuid[2]); data.WriteByteSeq(tarGuid[1]); data.WriteByteSeq(setGuid[0]); data.WriteByteSeq(setGuid[4]); data.WriteByteSeq(setGuid[1]); data.WriteByteSeq(tarGuid[3]); data.WriteByteSeq(tarGuid[6]); data.WriteByteSeq(tarGuid[4]); BroadcastPacket(&data); member->ChangeRank(rank); sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_GUILD_RANKS_UPDATE"); } void Guild::GiveXP(uint32 xp, Player* source) { if (!sWorld->getBoolConfig(CONFIG_GUILD_LEVELING_ENABLED)) return; /// @TODO: Award reputation and count activity for player if (GetLevel() >= sWorld->getIntConfig(CONFIG_GUILD_MAX_LEVEL)) xp = 0; // SMSG_GUILD_XP_GAIN is always sent, even for no gains if (GetLevel() >= GUILD_EXPERIENCE_UNCAPPED_LEVEL) xp = std::min(xp, sWorld->getIntConfig(CONFIG_GUILD_DAILY_XP_CAP) - uint32(_todayExperience)); WorldPacket data(SMSG_GUILD_XP_GAIN, 8); data << uint64(xp); source->GetSession()->SendPacket(&data); _experience += xp; _todayExperience += xp; if (!xp) return; uint32 oldLevel = GetLevel(); // Ding, mon! while (GetExperience() >= sGuildMgr->GetXPForGuildLevel(GetLevel()) && GetLevel() < sWorld->getIntConfig(CONFIG_GUILD_MAX_LEVEL)) { _experience -= sGuildMgr->GetXPForGuildLevel(GetLevel()); ++_level; } // Find all guild perks to learn std::vector perksToLearn; for (uint32 i = 0; i < sGuildPerkSpellsStore.GetNumRows(); ++i) if (GuildPerkSpellsEntry const* entry = sGuildPerkSpellsStore.LookupEntry(i)) if (entry->Level > oldLevel && entry->Level <= GetLevel()) perksToLearn.push_back(entry->SpellId); // Notify all online players that guild level changed and learn perks for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { if (Player* player = itr->second->FindPlayer()) { player->SetGuildLevel(GetLevel()); for (size_t i = 0; i < perksToLearn.size(); ++i) player->learnSpell(perksToLearn[i], true); } } GetNewsLog().AddNewEvent(GUILD_NEWS_LEVEL_UP, time(NULL), 0, 0, _level); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_GUILD_LEVEL, GetLevel(), 0, NULL, source); } void Guild::SendGuildXP(WorldSession* session) const { //Member const* member = GetMember(session->GetGuidLow()); WorldPacket data(SMSG_GUILD_XP, 40); data << uint64(/*member ? member->GetTotalActivity() :*/ 0); data << uint64(sGuildMgr->GetXPForGuildLevel(GetLevel()) - GetExperience()); // XP missing for next level data << uint64(GetTodayExperience()); data << uint64(/*member ? member->GetWeeklyActivity() :*/ 0); data << uint64(GetExperience()); session->SendPacket(&data); } void Guild::ResetDailyExperience() { _todayExperience = 0; for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if (Player* player = itr->second->FindPlayer()) SendGuildXP(player->GetSession()); } void Guild::GuildNewsLog::AddNewEvent(GuildNews eventType, time_t date, uint64 playerGuid, uint32 flags, uint32 data) { uint32 id = _newsLog.size(); GuildNewsEntry& log = _newsLog[id]; log.EventType = eventType; log.PlayerGuid = playerGuid; log.Data = data; log.Flags = flags; log.Date = date; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SAVE_GUILD_NEWS); stmt->setUInt32(0, GetGuild()->GetId()); stmt->setUInt32(1, id); stmt->setUInt32(2, log.EventType); stmt->setUInt64(3, log.PlayerGuid); stmt->setUInt32(4, log.Data); stmt->setUInt32(5, log.Flags); stmt->setUInt32(6, uint32(log.Date)); CharacterDatabase.Execute(stmt); WorldPacket packet; BuildNewsData(id, log, packet); GetGuild()->BroadcastPacket(&packet); } void Guild::GuildNewsLog::LoadFromDB(PreparedQueryResult result) { if (!result) return; do { Field* fields = result->Fetch(); uint32 id = fields[0].GetInt32(); GuildNewsEntry& log = _newsLog[id]; log.EventType = GuildNews(fields[1].GetInt32()); log.PlayerGuid = fields[2].GetInt64(); log.Data = fields[3].GetInt32(); log.Flags = fields[4].GetInt32(); log.Date = time_t(fields[5].GetInt32()); } while (result->NextRow()); } void Guild::GuildNewsLog::BuildNewsData(uint32 id, GuildNewsEntry& guildNew, WorldPacket& data) { data.Initialize(SMSG_GUILD_NEWS_UPDATE, 7 + 32); data.WriteBits(1, 21); // size, we are only sending 1 news here data.WriteBits(0, 26); // Not yet implemented used for guild achievements ObjectGuid guid = guildNew.PlayerGuid; data.WriteBit(guid[7]); data.WriteBit(guid[0]); data.WriteBit(guid[6]); data.WriteBit(guid[5]); data.WriteBit(guid[4]); data.WriteBit(guid[3]); data.WriteBit(guid[1]); data.WriteBit(guid[2]); data.FlushBits(); data.WriteByteSeq(guid[5]); data << uint32(guildNew.Flags); // 1 sticky data << uint32(guildNew.Data); data << uint32(0); // always 0 data.WriteByteSeq(guid[7]); data.WriteByteSeq(guid[6]); data.WriteByteSeq(guid[2]); data.WriteByteSeq(guid[3]); data.WriteByteSeq(guid[0]); data.WriteByteSeq(guid[4]); data.WriteByteSeq(guid[1]); data << uint32(id); data << uint32(guildNew.EventType); data.AppendPackedTime(guildNew.Date); } void Guild::GuildNewsLog::BuildNewsData(WorldPacket& data) { data.Initialize(SMSG_GUILD_NEWS_UPDATE, (21 + _newsLog.size() * (26 + 8)) / 8 + (8 + 6 * 4) * _newsLog.size()); data.WriteBits(_newsLog.size(), 21); for (GuildNewsLogMap::const_iterator it = _newsLog.begin(); it != _newsLog.end(); it++) { data.WriteBits(0, 26); // Not yet implemented used for guild achievements ObjectGuid guid = it->second.PlayerGuid; data.WriteBit(guid[7]); data.WriteBit(guid[0]); data.WriteBit(guid[6]); data.WriteBit(guid[5]); data.WriteBit(guid[4]); data.WriteBit(guid[3]); data.WriteBit(guid[1]); data.WriteBit(guid[2]); } data.FlushBits(); for (GuildNewsLogMap::const_iterator it = _newsLog.begin(); it != _newsLog.end(); it++) { ObjectGuid guid = it->second.PlayerGuid; data.WriteByteSeq(guid[5]); data << uint32(it->second.Flags); // 1 sticky data << uint32(it->second.Data); data << uint32(0); data.WriteByteSeq(guid[7]); data.WriteByteSeq(guid[6]); data.WriteByteSeq(guid[2]); data.WriteByteSeq(guid[3]); data.WriteByteSeq(guid[0]); data.WriteByteSeq(guid[4]); data.WriteByteSeq(guid[1]); data << uint32(it->first); data << uint32(it->second.EventType); data.AppendPackedTime(it->second.Date); } }