Core/Guild: Broadcast GuildMemberUpdateNote to all members
[trinitycore] / src / server / game / Guilds / Guild.cpp
1 /*
2 * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
3 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "Guild.h"
20 #include "AccountMgr.h"
21 #include "Bag.h"
22 #include "CalendarMgr.h"
23 #include "CalendarPackets.h"
24 #include "Chat.h"
25 #include "ChatPackets.h"
26 #include "Config.h"
27 #include "DatabaseEnv.h"
28 #include "DB2Stores.h"
29 #include "GuildFinderMgr.h"
30 #include "GuildMgr.h"
31 #include "GuildPackets.h"
32 #include "Language.h"
33 #include "Log.h"
34 #include "Map.h"
35 #include "ObjectAccessor.h"
36 #include "ObjectMgr.h"
37 #include "Player.h"
38 #include "ScriptMgr.h"
39 #include "SocialMgr.h"
40 #include "World.h"
41 #include "WorldSession.h"
42
43 size_t const MAX_GUILD_BANK_TAB_TEXT_LEN = 500;
44
45 uint32 const EMBLEM_PRICE = 10 * GOLD;
46
47 inline uint64 GetGuildBankTabPrice(uint8 tabId)
48 {
49 // these prices are in gold units, not copper
50 static uint64 const tabPrices[GUILD_BANK_MAX_TABS] = { 100, 250, 500, 1000, 2500, 5000, 0, 0 };
51 ASSERT(tabId < GUILD_BANK_MAX_TABS);
52
53 return tabPrices[tabId];
54 }
55
56 void Guild::SendCommandResult(WorldSession* session, GuildCommandType type, GuildCommandError errCode, std::string const& param)
57 {
58 WorldPackets::Guild::GuildCommandResult resultPacket;
59 resultPacket.Command = type;
60 resultPacket.Result = errCode;
61 resultPacket.Name = param;
62 session->SendPacket(resultPacket.Write());
63
64 TC_LOG_DEBUG("guild", "SMSG_GUILD_COMMAND_RESULT [%s]: Type: %u, code: %u, param: %s"
65 , session->GetPlayerInfo().c_str(), type, errCode, param.c_str());
66 }
67
68 void Guild::SendSaveEmblemResult(WorldSession* session, GuildEmblemError errCode)
69 {
70 WorldPackets::Guild::PlayerSaveGuildEmblem saveResponse;
71 saveResponse.Error = int32(errCode);
72 session->SendPacket(saveResponse.Write());
73
74 TC_LOG_DEBUG("guild", "Sent SMSG_SAVE_GUILD_EMBLEM [%s] Code: %u", session->GetPlayerInfo().c_str(), errCode);
75 }
76
77 // LogHolder
78 Guild::LogHolder::~LogHolder()
79 {
80 // Cleanup
81 for (auto itr = m_log.begin(); itr != m_log.end(); ++itr)
82 delete (*itr);
83 }
84
85 // Adds event loaded from database to collection
86 inline void Guild::LogHolder::LoadEvent(LogEntry* entry)
87 {
88 if (m_nextGUID == uint32(GUILD_EVENT_LOG_GUID_UNDEFINED))
89 m_nextGUID = entry->GetGUID();
90 m_log.push_front(entry);
91 }
92
93 // Adds new event happened in game.
94 // If maximum number of events is reached, oldest event is removed from collection.
95 inline void Guild::LogHolder::AddEvent(SQLTransaction& trans, LogEntry* entry)
96 {
97 // Check max records limit
98 if (m_log.size() >= m_maxRecords)
99 {
100 LogEntry* oldEntry = m_log.front();
101 delete oldEntry;
102 m_log.pop_front();
103 }
104 // Add event to list
105 m_log.push_back(entry);
106 // Save to DB
107 entry->SaveToDB(trans);
108 }
109
110 inline uint32 Guild::LogHolder::GetNextGUID()
111 {
112 // Next guid was not initialized. It means there are no records for this holder in DB yet.
113 // Start from the beginning.
114 if (m_nextGUID == uint32(GUILD_EVENT_LOG_GUID_UNDEFINED))
115 m_nextGUID = 0;
116 else
117 m_nextGUID = (m_nextGUID + 1) % m_maxRecords;
118 return m_nextGUID;
119 }
120
121 // EventLogEntry
122 void Guild::EventLogEntry::SaveToDB(SQLTransaction& trans) const
123 {
124 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_EVENTLOG);
125 stmt->setUInt64(0, m_guildId);
126 stmt->setUInt32(1, m_guid);
127 trans->Append(stmt);
128
129 uint8 index = 0;
130 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_EVENTLOG);
131 stmt->setUInt64( index, m_guildId);
132 stmt->setUInt32(++index, m_guid);
133 stmt->setUInt8 (++index, uint8(m_eventType));
134 stmt->setUInt64(++index, m_playerGuid1);
135 stmt->setUInt64(++index, m_playerGuid2);
136 stmt->setUInt8 (++index, m_newRank);
137 stmt->setUInt64(++index, m_timestamp);
138 trans->Append(stmt);
139 }
140
141 void Guild::EventLogEntry::WritePacket(WorldPackets::Guild::GuildEventLogQueryResults& packet) const
142 {
143 ObjectGuid playerGUID = ObjectGuid::Create<HighGuid::Player>(m_playerGuid1);
144 ObjectGuid otherGUID = ObjectGuid::Create<HighGuid::Player>(m_playerGuid2);
145
146 WorldPackets::Guild::GuildEventEntry eventEntry;
147 eventEntry.PlayerGUID = playerGUID;
148 eventEntry.OtherGUID = otherGUID;
149 eventEntry.TransactionType = uint8(m_eventType);
150 eventEntry.TransactionDate = uint32(::time(nullptr) - m_timestamp);
151 eventEntry.RankID = uint8(m_newRank);
152 packet.Entry.push_back(eventEntry);
153 }
154
155 // BankEventLogEntry
156 void Guild::BankEventLogEntry::SaveToDB(SQLTransaction& trans) const
157 {
158 uint8 index = 0;
159
160 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_EVENTLOG);
161 stmt->setUInt64( index, m_guildId);
162 stmt->setUInt32(++index, m_guid);
163 stmt->setUInt8 (++index, m_bankTabId);
164 trans->Append(stmt);
165
166 index = 0;
167 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_EVENTLOG);
168 stmt->setUInt64( index, m_guildId);
169 stmt->setUInt32(++index, m_guid);
170 stmt->setUInt8 (++index, m_bankTabId);
171 stmt->setUInt8 (++index, uint8(m_eventType));
172 stmt->setUInt64(++index, m_playerGuid);
173 stmt->setUInt64(++index, m_itemOrMoney);
174 stmt->setUInt16(++index, m_itemStackCount);
175 stmt->setUInt8 (++index, m_destTabId);
176 stmt->setUInt64(++index, m_timestamp);
177 trans->Append(stmt);
178 }
179
180 void Guild::BankEventLogEntry::WritePacket(WorldPackets::Guild:: GuildBankLogQueryResults& packet) const
181 {
182 ObjectGuid logGuid = ObjectGuid::Create<HighGuid::Player>(m_playerGuid);
183
184 bool hasItem = m_eventType == GUILD_BANK_LOG_DEPOSIT_ITEM || m_eventType == GUILD_BANK_LOG_WITHDRAW_ITEM ||
185 m_eventType == GUILD_BANK_LOG_MOVE_ITEM || m_eventType == GUILD_BANK_LOG_MOVE_ITEM2;
186
187 bool itemMoved = (m_eventType == GUILD_BANK_LOG_MOVE_ITEM || m_eventType == GUILD_BANK_LOG_MOVE_ITEM2);
188
189 bool hasStack = (hasItem && m_itemStackCount > 1) || itemMoved;
190
191 WorldPackets::Guild::GuildBankLogEntry bankLogEntry;
192 bankLogEntry.PlayerGUID = logGuid;
193 bankLogEntry.TimeOffset = int32(time(nullptr) - m_timestamp);
194 bankLogEntry.EntryType = int8(m_eventType);
195
196 if (hasStack)
197 bankLogEntry.Count = int32(m_itemStackCount);
198
199 if (IsMoneyEvent())
200 bankLogEntry.Money = uint64(m_itemOrMoney);
201
202 if (hasItem)
203 bankLogEntry.ItemID = int32(m_itemOrMoney);
204
205 if (itemMoved)
206 bankLogEntry.OtherTab = int8(m_destTabId);
207
208 packet.Entry.push_back(bankLogEntry);
209 }
210
211 void Guild::NewsLogEntry::SaveToDB(SQLTransaction& trans) const
212 {
213 uint8 index = 0;
214 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_NEWS);
215 stmt->setUInt64( index, m_guildId);
216 stmt->setUInt32(++index, GetGUID());
217 stmt->setUInt8 (++index, GetType());
218 stmt->setUInt64(++index, GetPlayerGuid().GetCounter());
219 stmt->setUInt32(++index, GetFlags());
220 stmt->setUInt32(++index, GetValue());
221 stmt->setUInt64(++index, GetTimestamp());
222 CharacterDatabase.ExecuteOrAppend(trans, stmt);
223 }
224
225 void Guild::NewsLogEntry::WritePacket(WorldPackets::Guild::GuildNews& newsPacket) const
226 {
227 WorldPackets::Guild::GuildNewsEvent newsEvent;
228 newsEvent.Id = int32(GetGUID());
229 newsEvent.MemberGuid = GetPlayerGuid();
230 newsEvent.CompletedDate = uint32(GetTimestamp());
231 newsEvent.Flags = int32(GetFlags());
232 newsEvent.Type = int32(GetType());
233
234 //for (uint8 i = 0; i < 2; i++)
235 // newsEvent.Data[i] =
236
237 //newsEvent.MemberList.push_back(MemberGuid);
238
239 if (GetType() == GUILD_NEWS_ITEM_LOOTED || GetType() == GUILD_NEWS_ITEM_CRAFTED || GetType() == GUILD_NEWS_ITEM_PURCHASED)
240 {
241 WorldPackets::Item::ItemInstance itemInstance;
242 itemInstance.ItemID = GetValue();
243 newsEvent.Item = itemInstance;
244 }
245
246 newsPacket.NewsEvents.push_back(newsEvent);
247 }
248
249 // RankInfo
250 void Guild::RankInfo::LoadFromDB(Field* fields)
251 {
252 m_rankId = fields[1].GetUInt8();
253 m_name = fields[2].GetString();
254 m_rights = fields[3].GetUInt32();
255 m_bankMoneyPerDay = fields[4].GetUInt32();
256 if (m_rankId == GR_GUILDMASTER) // Prevent loss of leader rights
257 m_rights |= GR_RIGHT_ALL;
258 }
259
260 void Guild::RankInfo::SaveToDB(SQLTransaction& trans) const
261 {
262 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_RANK);
263 stmt->setUInt64(0, m_guildId);
264 stmt->setUInt8 (1, m_rankId);
265 stmt->setString(2, m_name);
266 stmt->setUInt32(3, m_rights);
267 stmt->setUInt32(4, m_bankMoneyPerDay);
268 CharacterDatabase.ExecuteOrAppend(trans, stmt);
269 }
270
271 void Guild::RankInfo::CreateMissingTabsIfNeeded(uint8 tabs, SQLTransaction& trans, bool logOnCreate /* = false */)
272 {
273 for (uint8 i = 0; i < tabs; ++i)
274 {
275 GuildBankRightsAndSlots& rightsAndSlots = m_bankTabRightsAndSlots[i];
276 if (rightsAndSlots.GetTabId() == i)
277 continue;
278
279 rightsAndSlots.SetTabId(i);
280 if (m_rankId == GR_GUILDMASTER)
281 rightsAndSlots.SetGuildMasterValues();
282
283 if (logOnCreate)
284 TC_LOG_ERROR("guild", "Guild " UI64FMTD " has broken Tab %u for rank %u. Created default tab.", m_guildId, i, m_rankId);
285
286 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT);
287 stmt->setUInt64(0, m_guildId);
288 stmt->setUInt8(1, i);
289 stmt->setUInt8(2, m_rankId);
290 stmt->setInt8(3, rightsAndSlots.GetRights());
291 stmt->setInt32(4, rightsAndSlots.GetSlots());
292 trans->Append(stmt);
293 }
294 }
295
296 void Guild::RankInfo::SetName(std::string const& name)
297 {
298 if (m_name == name)
299 return;
300
301 m_name = name;
302
303 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_NAME);
304 stmt->setString(0, m_name);
305 stmt->setUInt8 (1, m_rankId);
306 stmt->setUInt64(2, m_guildId);
307 CharacterDatabase.Execute(stmt);
308 }
309
310 void Guild::RankInfo::SetRights(uint32 rights)
311 {
312 if (m_rankId == GR_GUILDMASTER) // Prevent loss of leader rights
313 rights = GR_RIGHT_ALL;
314
315 if (m_rights == rights)
316 return;
317
318 m_rights = rights;
319
320 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_RIGHTS);
321 stmt->setUInt32(0, m_rights);
322 stmt->setUInt8 (1, m_rankId);
323 stmt->setUInt64(2, m_guildId);
324 CharacterDatabase.Execute(stmt);
325 }
326
327 void Guild::RankInfo::SetBankMoneyPerDay(uint32 money)
328 {
329 if (m_bankMoneyPerDay == money)
330 return;
331
332 m_bankMoneyPerDay = money;
333
334 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_BANK_MONEY);
335 stmt->setUInt32(0, money);
336 stmt->setUInt8 (1, m_rankId);
337 stmt->setUInt64(2, m_guildId);
338 CharacterDatabase.Execute(stmt);
339 }
340
341 void Guild::RankInfo::SetBankTabSlotsAndRights(GuildBankRightsAndSlots rightsAndSlots, bool saveToDB)
342 {
343 if (m_rankId == GR_GUILDMASTER) // Prevent loss of leader rights
344 rightsAndSlots.SetGuildMasterValues();
345
346 GuildBankRightsAndSlots& guildBR = m_bankTabRightsAndSlots[rightsAndSlots.GetTabId()];
347 guildBR = rightsAndSlots;
348
349 if (saveToDB)
350 {
351 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT);
352 stmt->setUInt64(0, m_guildId);
353 stmt->setUInt8 (1, guildBR.GetTabId());
354 stmt->setUInt8 (2, m_rankId);
355 stmt->setInt8 (3, guildBR.GetRights());
356 stmt->setInt32 (4, guildBR.GetSlots());
357 CharacterDatabase.Execute(stmt);
358 }
359 }
360
361 Guild::BankTab::BankTab(ObjectGuid::LowType guildId, uint8 tabId) : m_guildId(guildId), m_tabId(tabId)
362 {
363 memset(m_items, 0, GUILD_BANK_MAX_SLOTS * sizeof(Item*));
364 }
365
366 // BankTab
367 void Guild::BankTab::LoadFromDB(Field* fields)
368 {
369 m_name = fields[2].GetString();
370 m_icon = fields[3].GetString();
371 m_text = fields[4].GetString();
372 }
373
374 bool Guild::BankTab::LoadItemFromDB(Field* fields)
375 {
376 uint8 slotId = fields[47].GetUInt8();
377 ObjectGuid::LowType itemGuid = fields[0].GetUInt64();
378 uint32 itemEntry = fields[1].GetUInt32();
379 if (slotId >= GUILD_BANK_MAX_SLOTS)
380 {
381 TC_LOG_ERROR("guild", "Invalid slot for item (GUID: " UI64FMTD ", id: %u) in guild bank, skipped.", itemGuid, itemEntry);
382 return false;
383 }
384
385 ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry);
386 if (!proto)
387 {
388 TC_LOG_ERROR("guild", "Unknown item (GUID: " UI64FMTD ", id: %u) in guild bank, skipped.", itemGuid, itemEntry);
389 return false;
390 }
391
392 Item* pItem = NewItemOrBag(proto);
393 if (!pItem->LoadFromDB(itemGuid, ObjectGuid::Empty, fields, itemEntry))
394 {
395 TC_LOG_ERROR("guild", "Item (GUID " UI64FMTD ", id: %u) not found in item_instance, deleting from guild bank!", itemGuid, itemEntry);
396
397 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM);
398 stmt->setUInt64(0, m_guildId);
399 stmt->setUInt8 (1, m_tabId);
400 stmt->setUInt8 (2, slotId);
401 CharacterDatabase.Execute(stmt);
402
403 delete pItem;
404 return false;
405 }
406
407 pItem->AddToWorld();
408 m_items[slotId] = pItem;
409 return true;
410 }
411
412 // Deletes contents of the tab from the world (and from DB if necessary)
413 void Guild::BankTab::Delete(SQLTransaction& trans, bool removeItemsFromDB)
414 {
415 for (uint8 slotId = 0; slotId < GUILD_BANK_MAX_SLOTS; ++slotId)
416 {
417 if (Item* item = m_items[slotId])
418 {
419 item->RemoveFromWorld();
420 if (removeItemsFromDB)
421 item->DeleteFromDB(trans);
422 delete item;
423 item = nullptr;
424 }
425 }
426 }
427
428 void Guild::BankTab::SetInfo(std::string const& name, std::string const& icon)
429 {
430 if (m_name == name && m_icon == icon)
431 return;
432
433 m_name = name;
434 m_icon = icon;
435
436 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_INFO);
437 stmt->setString(0, m_name);
438 stmt->setString(1, m_icon);
439 stmt->setUInt64(2, m_guildId);
440 stmt->setUInt8 (3, m_tabId);
441 CharacterDatabase.Execute(stmt);
442 }
443
444 void Guild::BankTab::SetText(std::string const& text)
445 {
446 if (m_text == text)
447 return;
448
449 m_text = text;
450 utf8truncate(m_text, MAX_GUILD_BANK_TAB_TEXT_LEN); // DB and client size limitation
451
452 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_TEXT);
453 stmt->setString(0, m_text);
454 stmt->setUInt64(1, m_guildId);
455 stmt->setUInt8 (2, m_tabId);
456 CharacterDatabase.Execute(stmt);
457 }
458
459 // Sets/removes contents of specified slot.
460 // If pItem == nullptr contents are removed.
461 bool Guild::BankTab::SetItem(SQLTransaction& trans, uint8 slotId, Item* item)
462 {
463 if (slotId >= GUILD_BANK_MAX_SLOTS)
464 return false;
465
466 m_items[slotId] = item;
467
468 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_ITEM);
469 stmt->setUInt64(0, m_guildId);
470 stmt->setUInt8 (1, m_tabId);
471 stmt->setUInt8 (2, slotId);
472 trans->Append(stmt);
473
474 if (item)
475 {
476 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_ITEM);
477 stmt->setUInt64(0, m_guildId);
478 stmt->setUInt8 (1, m_tabId);
479 stmt->setUInt8 (2, slotId);
480 stmt->setUInt64(3, item->GetGUID().GetCounter());
481 trans->Append(stmt);
482
483 item->SetGuidValue(ITEM_FIELD_CONTAINED, ObjectGuid::Empty);
484 item->SetOwnerGUID(ObjectGuid::Empty);
485 item->FSetState(ITEM_NEW);
486 item->SaveToDB(trans); // Not in inventory and can be saved standalone
487 }
488
489 return true;
490 }
491
492 void Guild::BankTab::SendText(Guild const* guild, WorldSession* session) const
493 {
494 WorldPackets::Guild::GuildBankTextQueryResult textQuery;
495 textQuery.Tab = m_tabId;
496 textQuery.Text = m_text;
497
498 if (session)
499 {
500 TC_LOG_DEBUG("guild", "SMSG_GUILD_BANK_QUERY_TEXT_RESULT [%s]: Tabid: %u, Text: %s"
501 , session->GetPlayerInfo().c_str(), m_tabId, m_text.c_str());
502 session->SendPacket(textQuery.Write());
503 }
504 else
505 {
506 TC_LOG_DEBUG("guild", "SMSG_GUILD_BANK_QUERY_TEXT_RESULT [Broadcast]: Tabid: %u, Text: %s", m_tabId, m_text.c_str());
507 guild->BroadcastPacket(textQuery.Write());
508 }
509 }
510
511 Guild::Member::Member(ObjectGuid::LowType guildId, ObjectGuid guid, uint8 rankId) :
512 m_guildId(guildId),
513 m_guid(guid),
514 m_zoneId(0),
515 m_level(0),
516 m_class(0),
517 _gender(0),
518 m_flags(GUILDMEMBER_STATUS_NONE),
519 m_logoutTime(::time(nullptr)),
520 m_accountId(0),
521 m_rankId(rankId),
522 m_bankWithdrawMoney(0),
523 m_achievementPoints(0),
524 m_totalActivity(0),
525 m_weekActivity(0),
526 m_totalReputation(0),
527 m_weekReputation(0)
528 {
529 memset(m_bankWithdraw, 0, (GUILD_BANK_MAX_TABS) * sizeof(uint32));
530 }
531
532 // Member
533 void Guild::Member::SetStats(Player* player)
534 {
535 m_name = player->GetName();
536 m_level = player->getLevel();
537 m_class = player->getClass();
538 _gender = player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER);
539 m_zoneId = player->GetZoneId();
540 m_accountId = player->GetSession()->GetAccountId();
541 m_achievementPoints = player->GetAchievementPoints();
542 }
543
544 void Guild::Member::SetStats(std::string const& name, uint8 level, uint8 _class, uint8 gender, uint32 zoneId, uint32 accountId, uint32 reputation)
545 {
546 m_name = name;
547 m_level = level;
548 m_class = _class;
549 _gender = gender;
550 m_zoneId = zoneId;
551 m_accountId = accountId;
552 m_totalReputation = reputation;
553 }
554
555 void Guild::Member::SetPublicNote(std::string const& publicNote)
556 {
557 if (m_publicNote == publicNote)
558 return;
559
560 m_publicNote = publicNote;
561
562 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_PNOTE);
563 stmt->setString(0, publicNote);
564 stmt->setUInt64(1, m_guid.GetCounter());
565 CharacterDatabase.Execute(stmt);
566 }
567
568 void Guild::Member::SetOfficerNote(std::string const& officerNote)
569 {
570 if (m_officerNote == officerNote)
571 return;
572
573 m_officerNote = officerNote;
574
575 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_OFFNOTE);
576 stmt->setString(0, officerNote);
577 stmt->setUInt64(1, m_guid.GetCounter());
578 CharacterDatabase.Execute(stmt);
579 }
580
581 void Guild::Member::ChangeRank(SQLTransaction& trans, uint8 newRank)
582 {
583 m_rankId = newRank;
584
585 // Update rank information in player's field, if he is online.
586 if (Player* player = FindConnectedPlayer())
587 player->SetGuildRank(newRank);
588
589 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_RANK);
590 stmt->setUInt8 (0, newRank);
591 stmt->setUInt64(1, m_guid.GetCounter());
592 CharacterDatabase.ExecuteOrAppend(trans, stmt);
593 }
594
595 void Guild::Member::SaveToDB(SQLTransaction& trans) const
596 {
597 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER);
598 stmt->setUInt64(0, m_guildId);
599 stmt->setUInt64(1, m_guid.GetCounter());
600 stmt->setUInt8 (2, m_rankId);
601 stmt->setString(3, m_publicNote);
602 stmt->setString(4, m_officerNote);
603 CharacterDatabase.ExecuteOrAppend(trans, stmt);
604 }
605
606 // Loads member's data from database.
607 // If member has broken fields (level, class) returns false.
608 // In this case member has to be removed from guild.
609 bool Guild::Member::LoadFromDB(Field* fields)
610 {
611 m_publicNote = fields[3].GetString();
612 m_officerNote = fields[4].GetString();
613
614 for (uint8 i = 0; i < GUILD_BANK_MAX_TABS; ++i)
615 m_bankWithdraw[i] = fields[5 + i].GetUInt32();
616
617 m_bankWithdrawMoney = fields[13].GetUInt64();
618
619 SetStats(fields[14].GetString(),
620 fields[15].GetUInt8(), // characters.level
621 fields[16].GetUInt8(), // characters.class
622 fields[17].GetUInt8(), // characters.gender
623 fields[18].GetUInt16(), // characters.zone
624 fields[19].GetUInt32(), // characters.account
625 0);
626 m_logoutTime = fields[20].GetUInt32(); // characters.logout_time
627 m_totalActivity = 0;
628 m_weekActivity = 0;
629 m_weekReputation = 0;
630
631 if (!CheckStats())
632 return false;
633
634 if (!m_zoneId)
635 {
636 TC_LOG_DEBUG("guild", "%s has broken zone-data", m_guid.ToString().c_str());
637 m_zoneId = Player::GetZoneIdFromDB(m_guid);
638 }
639
640 ResetFlags();
641 return true;
642 }
643
644 // Validate player fields. Returns false if corrupted fields are found.
645 bool Guild::Member::CheckStats() const
646 {
647 if (m_level < 1)
648 {
649 TC_LOG_ERROR("guild", "%s has a broken data in field `characters`.`level`, deleting him from guild!", m_guid.ToString().c_str());
650 return false;
651 }
652
653 if (m_class < CLASS_WARRIOR || m_class >= MAX_CLASSES)
654 {
655 TC_LOG_ERROR("guild", "%s has a broken data in field `characters`.`class`, deleting him from guild!", m_guid.ToString().c_str());
656 return false;
657 }
658 return true;
659 }
660
661 // Decreases amount of slots left for today.
662 void Guild::Member::UpdateBankTabWithdrawValue(SQLTransaction& trans, uint8 tabId, uint32 amount)
663 {
664 m_bankWithdraw[tabId] += amount;
665
666 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER_WITHDRAW_TABS);
667 stmt->setUInt64(0, m_guid.GetCounter());
668 for (uint8 i = 0; i < GUILD_BANK_MAX_TABS;)
669 {
670 uint32 withdraw = m_bankWithdraw[i++];
671 stmt->setUInt32(i, withdraw);
672 }
673
674 CharacterDatabase.ExecuteOrAppend(trans, stmt);
675 }
676
677 // Decreases amount of money left for today.
678 void Guild::Member::UpdateBankMoneyWithdrawValue(SQLTransaction& trans, uint64 amount)
679 {
680 m_bankWithdrawMoney += amount;
681
682 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER_WITHDRAW_MONEY);
683 stmt->setUInt64(0, m_guid.GetCounter());
684 stmt->setUInt64(1, m_bankWithdrawMoney);
685 CharacterDatabase.ExecuteOrAppend(trans, stmt);
686 }
687
688 void Guild::Member::ResetValues(bool weekly /* = false*/)
689 {
690 for (uint8 tabId = 0; tabId < GUILD_BANK_MAX_TABS; ++tabId)
691 m_bankWithdraw[tabId] = 0;
692
693 m_bankWithdrawMoney = 0;
694
695 if (weekly)
696 {
697 m_weekActivity = 0;
698 m_weekReputation = 0;
699 }
700 }
701
702 Player* Guild::Member::FindPlayer() const
703 {
704 return ObjectAccessor::FindPlayer(m_guid);
705 }
706
707 Player* Guild::Member::FindConnectedPlayer() const
708 {
709 return ObjectAccessor::FindConnectedPlayer(m_guid);
710 }
711
712 // EmblemInfo
713 void EmblemInfo::ReadPacket(WorldPackets::Guild::SaveGuildEmblem& packet)
714 {
715 m_style = packet.EStyle;
716 m_color = packet.EColor;
717 m_borderStyle = packet.BStyle;
718 m_borderColor = packet.BColor;
719 m_backgroundColor = packet.Bg;
720 }
721
722 bool EmblemInfo::ValidateEmblemColors() const
723 {
724 return sGuildColorBackgroundStore.LookupEntry(m_backgroundColor) &&
725 sGuildColorBorderStore.LookupEntry(m_borderColor) &&
726 sGuildColorEmblemStore.LookupEntry(m_color);
727
728 }
729
730 bool EmblemInfo::LoadFromDB(Field* fields)
731 {
732 m_style = fields[3].GetUInt8();
733 m_color = fields[4].GetUInt8();
734 m_borderStyle = fields[5].GetUInt8();
735 m_borderColor = fields[6].GetUInt8();
736 m_backgroundColor = fields[7].GetUInt8();
737
738 return ValidateEmblemColors();
739 }
740
741 void EmblemInfo::SaveToDB(ObjectGuid::LowType guildId) const
742 {
743 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_EMBLEM_INFO);
744 stmt->setUInt32(0, m_style);
745 stmt->setUInt32(1, m_color);
746 stmt->setUInt32(2, m_borderStyle);
747 stmt->setUInt32(3, m_borderColor);
748 stmt->setUInt32(4, m_backgroundColor);
749 stmt->setUInt64(5, guildId);
750 CharacterDatabase.Execute(stmt);
751 }
752
753 Guild::MoveItemData::MoveItemData(Guild* guild, Player* player, uint8 container, uint8 slotId) : m_pGuild(guild), m_pPlayer(player),
754 m_container(container), m_slotId(slotId), m_pItem(nullptr), m_pClonedItem(nullptr)
755 {
756 }
757
758 Guild::MoveItemData::~MoveItemData()
759 {
760 }
761
762 // MoveItemData
763 bool Guild::MoveItemData::CheckItem(uint32& splitedAmount)
764 {
765 ASSERT(m_pItem);
766 if (splitedAmount > m_pItem->GetCount())
767 return false;
768 if (splitedAmount == m_pItem->GetCount())
769 splitedAmount = 0;
770 return true;
771 }
772
773 bool Guild::MoveItemData::CanStore(Item* pItem, bool swap, bool sendError)
774 {
775 m_vec.clear();
776 InventoryResult msg = CanStore(pItem, swap);
777 if (sendError && msg != EQUIP_ERR_OK)
778 m_pPlayer->SendEquipError(msg, pItem);
779 return (msg == EQUIP_ERR_OK);
780 }
781
782 bool Guild::MoveItemData::CloneItem(uint32 count)
783 {
784 ASSERT(m_pItem);
785 m_pClonedItem = m_pItem->CloneItem(count);
786 if (!m_pClonedItem)
787 {
788 m_pPlayer->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, m_pItem);
789 return false;
790 }
791 return true;
792 }
793
794 void Guild::MoveItemData::LogAction(MoveItemData* pFrom) const
795 {
796 ASSERT(pFrom->GetItem());
797
798 sScriptMgr->OnGuildItemMove(m_pGuild, m_pPlayer, pFrom->GetItem(),
799 pFrom->IsBank(), pFrom->GetContainer(), pFrom->GetSlotId(),
800 IsBank(), GetContainer(), GetSlotId());
801 }
802
803 inline void Guild::MoveItemData::CopySlots(SlotIds& ids) const
804 {
805 for (auto itr = m_vec.begin(); itr != m_vec.end(); ++itr)
806 ids.insert(uint8(itr->pos));
807 }
808
809 // PlayerMoveItemData
810 bool Guild::PlayerMoveItemData::InitItem()
811 {
812 m_pItem = m_pPlayer->GetItemByPos(m_container, m_slotId);
813 if (m_pItem)
814 {
815 // Anti-WPE protection. Do not move non-empty bags to bank.
816 if (m_pItem->IsNotEmptyBag())
817 {
818 m_pPlayer->SendEquipError(EQUIP_ERR_DESTROY_NONEMPTY_BAG, m_pItem);
819 m_pItem = nullptr;
820 }
821 // Bound items cannot be put into bank.
822 else if (!m_pItem->CanBeTraded())
823 {
824 m_pPlayer->SendEquipError(EQUIP_ERR_CANT_SWAP, m_pItem);
825 m_pItem = nullptr;
826 }
827 }
828 return (m_pItem != nullptr);
829 }
830
831 void Guild::PlayerMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* /*pOther*/, uint32 splitedAmount)
832 {
833 if (splitedAmount)
834 {
835 m_pItem->SetCount(m_pItem->GetCount() - splitedAmount);
836 m_pItem->SetState(ITEM_CHANGED, m_pPlayer);
837 m_pPlayer->SaveInventoryAndGoldToDB(trans);
838 }
839 else
840 {
841 m_pPlayer->MoveItemFromInventory(m_container, m_slotId, true);
842 m_pItem->DeleteFromInventoryDB(trans);
843 m_pItem = nullptr;
844 }
845 }
846
847 Item* Guild::PlayerMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem)
848 {
849 ASSERT(pItem);
850 m_pPlayer->MoveItemToInventory(m_vec, pItem, true);
851 m_pPlayer->SaveInventoryAndGoldToDB(trans);
852 return pItem;
853 }
854
855 void Guild::PlayerMoveItemData::LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const
856 {
857 ASSERT(pFrom);
858 // Bank -> Char
859 m_pGuild->_LogBankEvent(trans, GUILD_BANK_LOG_WITHDRAW_ITEM, pFrom->GetContainer(), m_pPlayer->GetGUID().GetCounter(),
860 pFrom->GetItem()->GetEntry(), count);
861 }
862
863 inline InventoryResult Guild::PlayerMoveItemData::CanStore(Item* pItem, bool swap)
864 {
865 return m_pPlayer->CanStoreItem(m_container, m_slotId, m_vec, pItem, swap);
866 }
867
868 // BankMoveItemData
869 bool Guild::BankMoveItemData::InitItem()
870 {
871 m_pItem = m_pGuild->_GetItem(m_container, m_slotId);
872 return (m_pItem != nullptr);
873 }
874
875 bool Guild::BankMoveItemData::HasStoreRights(MoveItemData* pOther) const
876 {
877 ASSERT(pOther);
878 // Do not check rights if item is being swapped within the same bank tab
879 if (pOther->IsBank() && pOther->GetContainer() == m_container)
880 return true;
881 return m_pGuild->_MemberHasTabRights(m_pPlayer->GetGUID(), m_container, GUILD_BANK_RIGHT_DEPOSIT_ITEM);
882 }
883
884 bool Guild::BankMoveItemData::HasWithdrawRights(MoveItemData* pOther) const
885 {
886 ASSERT(pOther);
887 // Do not check rights if item is being swapped within the same bank tab
888 if (pOther->IsBank() && pOther->GetContainer() == m_container)
889 return true;
890
891 int32 slots = 0;
892 if (Member const* member = m_pGuild->GetMember(m_pPlayer->GetGUID()))
893 slots = m_pGuild->_GetMemberRemainingSlots(member, m_container);
894
895 return slots != 0;
896 }
897
898 void Guild::BankMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount)
899 {
900 ASSERT(m_pItem);
901 if (splitedAmount)
902 {
903 m_pItem->SetCount(m_pItem->GetCount() - splitedAmount);
904 m_pItem->FSetState(ITEM_CHANGED);
905 m_pItem->SaveToDB(trans);
906 }
907 else
908 {
909 m_pGuild->_RemoveItem(trans, m_container, m_slotId);
910 m_pItem = nullptr;
911 }
912 // Decrease amount of player's remaining items (if item is moved to different tab or to player)
913 if (!pOther->IsBank() || pOther->GetContainer() != m_container)
914 m_pGuild->_UpdateMemberWithdrawSlots(trans, m_pPlayer->GetGUID(), m_container);
915 }
916
917 Item* Guild::BankMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem)
918 {
919 if (!pItem)
920 return nullptr;
921
922 BankTab* pTab = m_pGuild->GetBankTab(m_container);
923 if (!pTab)
924 return nullptr;
925
926 Item* pLastItem = pItem;
927 for (auto itr = m_vec.begin(); itr != m_vec.end(); )
928 {
929 ItemPosCount pos(*itr);
930 ++itr;
931
932 TC_LOG_DEBUG("guild", "GUILD STORAGE: StoreItem tab = %u, slot = %u, item = %u, count = %u",
933 m_container, m_slotId, pItem->GetEntry(), pItem->GetCount());
934 pLastItem = _StoreItem(trans, pTab, pItem, pos, itr != m_vec.end());
935 }
936 return pLastItem;
937 }
938
939 void Guild::BankMoveItemData::LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const
940 {
941 ASSERT(pFrom->GetItem());
942 if (pFrom->IsBank())
943 // Bank -> Bank
944 m_pGuild->_LogBankEvent(trans, GUILD_BANK_LOG_MOVE_ITEM, pFrom->GetContainer(), m_pPlayer->GetGUID().GetCounter(),
945 pFrom->GetItem()->GetEntry(), count, m_container);
946 else
947 // Char -> Bank
948 m_pGuild->_LogBankEvent(trans, GUILD_BANK_LOG_DEPOSIT_ITEM, m_container, m_pPlayer->GetGUID().GetCounter(),
949 pFrom->GetItem()->GetEntry(), count);
950 }
951
952 void Guild::BankMoveItemData::LogAction(MoveItemData* pFrom) const
953 {
954 MoveItemData::LogAction(pFrom);
955 if (!pFrom->IsBank() && m_pPlayer->GetSession()->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) /// @todo Move this to scripts
956 {
957 sLog->outCommand(m_pPlayer->GetSession()->GetAccountId(),
958 "GM %s (%s) (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank named: %s (Guild ID: " UI64FMTD ")",
959 m_pPlayer->GetName().c_str(), m_pPlayer->GetGUID().ToString().c_str(), m_pPlayer->GetSession()->GetAccountId(),
960 pFrom->GetItem()->GetTemplate()->GetDefaultLocaleName(), pFrom->GetItem()->GetEntry(), pFrom->GetItem()->GetCount(),
961 m_pGuild->GetName().c_str(), m_pGuild->GetId());
962 }
963 }
964
965 Item* Guild::BankMoveItemData::_StoreItem(SQLTransaction& trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const
966 {
967 uint8 slotId = uint8(pos.pos);
968 uint32 count = pos.count;
969 if (Item* pItemDest = pTab->GetItem(slotId))
970 {
971 pItemDest->SetCount(pItemDest->GetCount() + count);
972 pItemDest->FSetState(ITEM_CHANGED);
973 pItemDest->SaveToDB(trans);
974 if (!clone)
975 {
976 pItem->RemoveFromWorld();
977 pItem->DeleteFromDB(trans);
978 delete pItem;
979 }
980 return pItemDest;
981 }
982
983 if (clone)
984 pItem = pItem->CloneItem(count);
985 else
986 pItem->SetCount(count);
987
988 if (pItem && pTab->SetItem(trans, slotId, pItem))
989 return pItem;
990
991 return nullptr;
992 }
993
994 // Tries to reserve space for source item.
995 // If item in destination slot exists it must be the item of the same entry
996 // and stack must have enough space to take at least one item.
997 // Returns false if destination item specified and it cannot be used to reserve space.
998 bool Guild::BankMoveItemData::_ReserveSpace(uint8 slotId, Item* pItem, Item* pItemDest, uint32& count)
999 {
1000 uint32 requiredSpace = pItem->GetMaxStackCount();
1001 if (pItemDest)
1002 {
1003 // Make sure source and destination items match and destination item has space for more stacks.
1004 if (pItemDest->GetEntry() != pItem->GetEntry() || pItemDest->GetCount() >= pItem->GetMaxStackCount())
1005 return false;
1006 requiredSpace -= pItemDest->GetCount();
1007 }
1008 // Let's not be greedy, reserve only required space
1009 requiredSpace = std::min(requiredSpace, count);
1010
1011 // Reserve space
1012 ItemPosCount pos(slotId, requiredSpace);
1013 if (!pos.isContainedIn(m_vec))
1014 {
1015 m_vec.push_back(pos);
1016 count -= requiredSpace;
1017 }
1018 return true;
1019 }
1020
1021 void Guild::BankMoveItemData::CanStoreItemInTab(Item* pItem, uint8 skipSlotId, bool merge, uint32& count)
1022 {
1023 for (uint8 slotId = 0; (slotId < GUILD_BANK_MAX_SLOTS) && (count > 0); ++slotId)
1024 {
1025 // Skip slot already processed in CanStore (when destination slot was specified)
1026 if (slotId == skipSlotId)
1027 continue;
1028
1029 Item* pItemDest = m_pGuild->_GetItem(m_container, slotId);
1030 if (pItemDest == pItem)
1031 pItemDest = nullptr;
1032
1033 // If merge skip empty, if not merge skip non-empty
1034 if ((pItemDest != nullptr) != merge)
1035 continue;
1036
1037 _ReserveSpace(slotId, pItem, pItemDest, count);
1038 }
1039 }
1040
1041 InventoryResult Guild::BankMoveItemData::CanStore(Item* pItem, bool swap)
1042 {
1043 TC_LOG_DEBUG("guild", "GUILD STORAGE: CanStore() tab = %u, slot = %u, item = %u, count = %u",
1044 m_container, m_slotId, pItem->GetEntry(), pItem->GetCount());
1045
1046 uint32 count = pItem->GetCount();
1047 // Soulbound items cannot be moved
1048 if (pItem->IsSoulBound())
1049 return EQUIP_ERR_DROP_BOUND_ITEM;
1050
1051 // Make sure destination bank tab exists
1052 if (m_container >= m_pGuild->_GetPurchasedTabsSize())
1053 return EQUIP_ERR_WRONG_BAG_TYPE;
1054
1055 // Slot explicitely specified. Check it.
1056 if (m_slotId != NULL_SLOT)
1057 {
1058 Item* pItemDest = m_pGuild->_GetItem(m_container, m_slotId);
1059 // Ignore swapped item (this slot will be empty after move)
1060 if ((pItemDest == pItem) || swap)
1061 pItemDest = nullptr;
1062
1063 if (!_ReserveSpace(m_slotId, pItem, pItemDest, count))
1064 return EQUIP_ERR_CANT_STACK;
1065
1066 if (count == 0)
1067 return EQUIP_ERR_OK;
1068 }
1069
1070 // Slot was not specified or it has not enough space for all the items in stack
1071 // Search for stacks to merge with
1072 if (pItem->GetMaxStackCount() > 1)
1073 {
1074 CanStoreItemInTab(pItem, m_slotId, true, count);
1075 if (count == 0)
1076 return EQUIP_ERR_OK;
1077 }
1078
1079 // Search free slot for item
1080 CanStoreItemInTab(pItem, m_slotId, false, count);
1081 if (count == 0)
1082 return EQUIP_ERR_OK;
1083
1084 return EQUIP_ERR_BANK_FULL;
1085 }
1086
1087 // Guild
1088 Guild::Guild():
1089 m_id(UI64LIT(0)),
1090 m_leaderGuid(),
1091 m_createdDate(0),
1092 m_accountsNumber(0),
1093 m_bankMoney(0),
1094 m_eventLog(nullptr),
1095 m_newsLog(nullptr),
1096 m_achievementMgr(this)
1097 {
1098 memset(&m_bankEventLog, 0, (GUILD_BANK_MAX_TABS + 1) * sizeof(LogHolder*));
1099 }
1100
1101 Guild::~Guild()
1102 {
1103 SQLTransaction temp(nullptr);
1104 _DeleteBankItems(temp);
1105
1106 // Cleanup
1107 delete m_eventLog;
1108 m_eventLog = nullptr;
1109 delete m_newsLog;
1110 m_newsLog = nullptr;
1111
1112 for (uint8 tabId = 0; tabId <= GUILD_BANK_MAX_TABS; ++tabId)
1113 {
1114 delete m_bankEventLog[tabId];
1115 m_bankEventLog[tabId] = nullptr;
1116 }
1117
1118 for (auto itr = m_members.begin(); itr != m_members.end(); ++itr)
1119 {
1120 delete itr->second;
1121 itr->second = nullptr;
1122 }
1123 }
1124
1125 // Creates new guild with default data and saves it to database.
1126 bool Guild::Create(Player* pLeader, std::string const& name)
1127 {
1128 // Check if guild with such name already exists
1129 if (sGuildMgr->GetGuildByName(name))
1130 return false;
1131
1132 WorldSession* pLeaderSession = pLeader->GetSession();
1133 if (!pLeaderSession)
1134 return false;
1135
1136 m_id = sGuildMgr->GenerateGuildId();
1137 m_leaderGuid = pLeader->GetGUID();
1138 m_name = name;
1139 m_info = "";
1140 m_motd = "No message set.";
1141 m_bankMoney = 0;
1142 m_createdDate = ::time(nullptr);
1143 _CreateLogHolders();
1144
1145 TC_LOG_DEBUG("guild", "GUILD: creating guild [%s] for leader %s (%s)",
1146 name.c_str(), pLeader->GetName().c_str(), m_leaderGuid.ToString().c_str());
1147
1148 SQLTransaction trans = CharacterDatabase.BeginTransaction();
1149
1150 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBERS);
1151 stmt->setUInt64(0, m_id);
1152 trans->Append(stmt);
1153
1154 uint8 index = 0;
1155 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD);
1156 stmt->setUInt64( index, m_id);
1157 stmt->setString(++index, name);
1158 stmt->setUInt64(++index, m_leaderGuid.GetCounter());
1159 stmt->setString(++index, m_info);
1160 stmt->setString(++index, m_motd);
1161 stmt->setUInt64(++index, uint32(m_createdDate));
1162 stmt->setUInt32(++index, m_emblemInfo.GetStyle());
1163 stmt->setUInt32(++index, m_emblemInfo.GetColor());
1164 stmt->setUInt32(++index, m_emblemInfo.GetBorderStyle());
1165 stmt->setUInt32(++index, m_emblemInfo.GetBorderColor());
1166 stmt->setUInt32(++index, m_emblemInfo.GetBackgroundColor());
1167 stmt->setUInt64(++index, m_bankMoney);
1168 trans->Append(stmt);
1169
1170 _CreateDefaultGuildRanks(trans, pLeaderSession->GetSessionDbLocaleIndex()); // Create default ranks
1171 bool ret = AddMember(trans, m_leaderGuid, GR_GUILDMASTER); // Add guildmaster
1172
1173 CharacterDatabase.CommitTransaction(trans);
1174
1175 if (ret)
1176 {
1177 Member* leader = GetMember(m_leaderGuid);
1178 if (leader)
1179 SendEventNewLeader(leader, nullptr);
1180
1181 sScriptMgr->OnGuildCreate(this, pLeader, name);
1182 }
1183
1184 return ret;
1185 }
1186
1187 // Disbands guild and deletes all related data from database
1188 void Guild::Disband()
1189 {
1190 // Call scripts before guild data removed from database
1191 sScriptMgr->OnGuildDisband(this);
1192
1193 WorldPackets::Guild::GuildEventDisbanded packet;
1194 BroadcastPacket(packet.Write());
1195
1196 SQLTransaction trans = CharacterDatabase.BeginTransaction();
1197 // Remove all members
1198 while (!m_members.empty())
1199 {
1200 auto itr = m_members.begin();
1201 DeleteMember(trans, itr->second->GetGUID(), true);
1202 }
1203
1204 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD);
1205 stmt->setUInt64(0, m_id);
1206 trans->Append(stmt);
1207
1208 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_RANKS);
1209 stmt->setUInt64(0, m_id);
1210 trans->Append(stmt);
1211
1212 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_TABS);
1213 stmt->setUInt64(0, m_id);
1214 trans->Append(stmt);
1215
1216 // Free bank tab used memory and delete items stored in them
1217 _DeleteBankItems(trans, true);
1218
1219 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_ITEMS);
1220 stmt->setUInt64(0, m_id);
1221 trans->Append(stmt);
1222
1223 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_RIGHTS);
1224 stmt->setUInt64(0, m_id);
1225 trans->Append(stmt);
1226
1227 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_EVENTLOGS);
1228 stmt->setUInt64(0, m_id);
1229 trans->Append(stmt);
1230
1231 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_EVENTLOGS);
1232 stmt->setUInt64(0, m_id);
1233 trans->Append(stmt);
1234
1235 CharacterDatabase.CommitTransaction(trans);
1236
1237 sGuildFinderMgr->DeleteGuild(GetGUID());
1238
1239 sGuildMgr->RemoveGuild(m_id);
1240 }
1241
1242 void Guild::SaveToDB()
1243 {
1244 SQLTransaction trans = CharacterDatabase.BeginTransaction();
1245
1246 m_achievementMgr.SaveToDB(trans);
1247
1248 CharacterDatabase.CommitTransaction(trans);
1249 }
1250
1251 void Guild::UpdateMemberData(Player* player, uint8 dataid, uint32 value)
1252 {
1253 if (Member* member = GetMember(player->GetGUID()))
1254 {
1255 switch (dataid)
1256 {
1257 case GUILD_MEMBER_DATA_ZONEID:
1258 member->SetZoneId(value);
1259 break;
1260 case GUILD_MEMBER_DATA_ACHIEVEMENT_POINTS:
1261 member->SetAchievementPoints(value);
1262 break;
1263 case GUILD_MEMBER_DATA_LEVEL:
1264 member->SetLevel(value);
1265 break;
1266 default:
1267 TC_LOG_ERROR("guild", "Guild::UpdateMemberData: Called with incorrect DATAID %u (value %u)", dataid, value);
1268 return;
1269 }
1270 //HandleRoster();
1271 }
1272 }
1273
1274 void Guild::OnPlayerStatusChange(Player* player, uint32 flag, bool state)
1275 {
1276 if (Member* member = GetMember(player->GetGUID()))
1277 {
1278 if (state)
1279 member->AddFlag(flag);
1280 else member->RemFlag(flag);
1281 }
1282 }
1283
1284 bool Guild::SetName(std::string const& name)
1285 {
1286 if (m_name == name || name.empty() || name.length() > 24 || sObjectMgr->IsReservedName(name) || !ObjectMgr::IsValidCharterName(name))
1287 return false;
1288
1289 m_name = name;
1290 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_NAME);
1291 stmt->setString(0, m_name);
1292 stmt->setUInt64(1, GetId());
1293 CharacterDatabase.Execute(stmt);
1294
1295 WorldPackets::Guild::GuildNameChanged guildNameChanged;
1296 guildNameChanged.GuildGUID = GetGUID();
1297 guildNameChanged.GuildName = m_name;
1298 BroadcastPacket(guildNameChanged.Write());
1299
1300 return true;
1301 }
1302
1303 void Guild::HandleRoster(WorldSession* session)
1304 {
1305 WorldPackets::Guild::GuildRoster roster;
1306
1307 roster.NumAccounts = int32(m_accountsNumber);
1308 roster.CreateDate = uint32(m_createdDate);
1309 roster.GuildFlags = 0;
1310
1311 roster.MemberData.reserve(m_members.size());
1312
1313 for (auto itr : m_members)
1314 {
1315 Member* member = itr.second;
1316
1317 WorldPackets::Guild::GuildRosterMemberData memberData;
1318
1319 memberData.Guid = member->GetGUID();
1320 memberData.RankID = int32(member->GetRankId());
1321 memberData.AreaID = int32(member->GetZoneId());
1322 memberData.PersonalAchievementPoints = int32(member->GetAchievementPoints());
1323 memberData.GuildReputation = int32(member->GetTotalReputation());
1324 memberData.LastSave = float(member->IsOnline() ? 0.0f : float(::time(nullptr) - member->GetLogoutTime()) / DAY);
1325
1326 //GuildRosterProfessionData
1327
1328 memberData.VirtualRealmAddress = GetVirtualRealmAddress();
1329 memberData.Status = member->GetFlags();
1330 memberData.Level = member->GetLevel();
1331 memberData.ClassID = member->GetClass();
1332 memberData.Gender = member->GetGender();
1333
1334 memberData.Authenticated = false;
1335 memberData.SorEligible = false;
1336
1337 memberData.Name = member->GetName();
1338 memberData.Note = member->GetPublicNote();
1339 memberData.OfficerNote = member->GetOfficerNote();
1340
1341 roster.MemberData.push_back(memberData);
1342 }
1343
1344 roster.WelcomeText = m_motd;
1345 roster.InfoText = m_info;
1346
1347 TC_LOG_DEBUG("guild", "SMSG_GUILD_ROSTER [%s]", session->GetPlayerInfo().c_str());
1348 session->SendPacket(roster.Write());
1349 }
1350
1351 void Guild::SendQueryResponse(WorldSession* session)
1352 {
1353 WorldPackets::Guild::QueryGuildInfoResponse response;
1354 response.GuildGuid = GetGUID();
1355 response.Info = boost::in_place();
1356
1357 response.Info->GuildGUID = GetGUID();
1358 response.Info->VirtualRealmAddress = GetVirtualRealmAddress();
1359
1360 response.Info->EmblemStyle = m_emblemInfo.GetStyle();
1361 response.Info->EmblemColor = m_emblemInfo.GetColor();
1362 response.Info->BorderStyle = m_emblemInfo.GetBorderStyle();
1363 response.Info->BorderColor = m_emblemInfo.GetBorderColor();
1364 response.Info->BackgroundColor = m_emblemInfo.GetBackgroundColor();
1365
1366 for (uint8 i = 0; i < _GetRanksSize(); ++i)
1367 {
1368 WorldPackets::Guild::QueryGuildInfoResponse::GuildInfo::GuildInfoRank info
1369 (m_ranks[i].GetId(), i, m_ranks[i].GetName());
1370 response.Info->Ranks.insert(info);
1371 }
1372
1373 response.Info->GuildName = m_name;
1374
1375 session->SendPacket(response.Write());
1376 TC_LOG_DEBUG("guild", "SMSG_GUILD_QUERY_RESPONSE [%s]", session->GetPlayerInfo().c_str());
1377 }
1378
1379 void Guild::SendGuildRankInfo(WorldSession* session) const
1380 {
1381 WorldPackets::Guild::GuildRanks ranks;
1382
1383 ranks.Ranks.reserve(_GetRanksSize());
1384
1385 for (uint8 i = 0; i < _GetRanksSize(); i++)
1386 {
1387 RankInfo const* rankInfo = GetRankInfo(i);
1388 if (!rankInfo)
1389 continue;
1390
1391 WorldPackets::Guild::GuildRankData rankData;
1392
1393 rankData.RankID = uint32(rankInfo->GetId());
1394 rankData.RankOrder = uint32(i);
1395 rankData.Flags = rankInfo->GetRights();
1396 rankData.WithdrawGoldLimit = rankInfo->GetBankMoneyPerDay();
1397 rankData.RankName = rankInfo->GetName();
1398
1399 for (uint8 j = 0; j < GUILD_BANK_MAX_TABS; ++j)
1400 {
1401 rankData.TabFlags[j] = uint32(rankInfo->GetBankTabRights(j));
1402 rankData.TabWithdrawItemLimit[j] = uint32(rankInfo->GetBankTabSlotsPerDay(j));
1403 }
1404
1405 ranks.Ranks.push_back(rankData);
1406 }
1407
1408 session->SendPacket(ranks.Write());
1409 TC_LOG_DEBUG("guild", "SMSG_GUILD_RANK [%s]", session->GetPlayerInfo().c_str());
1410 }
1411
1412 void Guild::HandleSetAchievementTracking(WorldSession* session, std::set<uint32> const& achievementIds)
1413 {
1414 Player* player = session->GetPlayer();
1415
1416 if (Member* member = GetMember(player->GetGUID()))
1417 {
1418 std::set<uint32> criteriaIds;
1419
1420 for (uint32 achievementId : achievementIds)
1421 {
1422 if (AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementId))
1423 {
1424 if (CriteriaTree const* tree = sCriteriaMgr->GetCriteriaTree(achievement->CriteriaTree))
1425 {
1426 CriteriaMgr::WalkCriteriaTree(tree, [&criteriaIds](CriteriaTree const* node)
1427 {
1428 if (node->Criteria)
1429 criteriaIds.insert(node->Criteria->ID);
1430 });
1431 }
1432 }
1433 }
1434
1435 member->SetTrackedCriteriaIds(criteriaIds);
1436 m_achievementMgr.SendAllTrackedCriterias(player, member->GetTrackedCriteriaIds());
1437 }
1438 }
1439
1440 void Guild::HandleGetAchievementMembers(WorldSession* session, uint32 achievementId) const
1441 {
1442 m_achievementMgr.SendAchievementMembers(session->GetPlayer(), achievementId);
1443 }
1444
1445 void Guild::HandleSetMOTD(WorldSession* session, std::string const& motd)
1446 {
1447 if (m_motd == motd)
1448 return;
1449
1450 // Player must have rights to set MOTD
1451 if (!_HasRankRight(session->GetPlayer(), GR_RIGHT_SETMOTD))
1452 SendCommandResult(session, GUILD_COMMAND_EDIT_MOTD, ERR_GUILD_PERMISSIONS);
1453 else
1454 {
1455 m_motd = motd;
1456
1457 sScriptMgr->OnGuildMOTDChanged(this, motd);
1458
1459 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MOTD);
1460 stmt->setString(0, motd);
1461 stmt->setUInt64(1, m_id);
1462 CharacterDatabase.Execute(stmt);
1463
1464 SendEventMOTD(session, true);
1465 }
1466 }
1467
1468 void Guild::HandleSetInfo(WorldSession* session, std::string const& info)
1469 {
1470 if (m_info == info)
1471 return;
1472
1473 // Player must have rights to set guild's info
1474 if (_HasRankRight(session->GetPlayer(), GR_RIGHT_MODIFY_GUILD_INFO))
1475 {
1476 m_info = info;
1477
1478 sScriptMgr->OnGuildInfoChanged(this, info);
1479
1480 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_INFO);
1481 stmt->setString(0, info);
1482 stmt->setUInt64(1, m_id);
1483 CharacterDatabase.Execute(stmt);
1484 }
1485 }
1486
1487 void Guild::HandleSetEmblem(WorldSession* session, const EmblemInfo& emblemInfo)
1488 {
1489 Player* player = session->GetPlayer();
1490 if (!_IsLeader(player))
1491 SendSaveEmblemResult(session, ERR_GUILDEMBLEM_NOTGUILDMASTER); // "Only guild leaders can create emblems."
1492 else if (!player->HasEnoughMoney(uint64(EMBLEM_PRICE)))
1493 SendSaveEmblemResult(session, ERR_GUILDEMBLEM_NOTENOUGHMONEY); // "You can't afford to do that."
1494 else
1495 {
1496 player->ModifyMoney(-int64(EMBLEM_PRICE));
1497
1498 m_emblemInfo = emblemInfo;
1499 m_emblemInfo.SaveToDB(m_id);
1500
1501 SendSaveEmblemResult(session, ERR_GUILDEMBLEM_SUCCESS); // "Guild Emblem saved."
1502
1503 SendQueryResponse(session);
1504 }
1505 }
1506
1507 void Guild::HandleSetNewGuildMaster(WorldSession* session, std::string const& name)
1508 {
1509 Player* player = session->GetPlayer();
1510 // Only the guild master can throne a new guild master
1511 if (!_IsLeader(player))
1512 SendCommandResult(session, GUILD_COMMAND_CHANGE_LEADER, ERR_GUILD_PERMISSIONS);
1513 // Old GM must be a guild member
1514 else if (Member* oldGuildMaster = GetMember(player->GetGUID()))
1515 {
1516 // Same for the new one
1517 if (Member* newGuildMaster = GetMember(name))
1518 {
1519 SQLTransaction trans = CharacterDatabase.BeginTransaction();
1520
1521 _SetLeader(trans, newGuildMaster);
1522 oldGuildMaster->ChangeRank(trans, GR_INITIATE);
1523
1524 SendEventNewLeader(newGuildMaster, oldGuildMaster);
1525
1526 CharacterDatabase.CommitTransaction(trans);
1527 }
1528 }
1529 }
1530
1531 void Guild::HandleSetBankTabInfo(WorldSession* session, uint8 tabId, std::string const& name, std::string const& icon)
1532 {
1533 BankTab* tab = GetBankTab(tabId);
1534 if (!tab)
1535 {
1536 TC_LOG_ERROR("guild", "Guild::HandleSetBankTabInfo: Player %s trying to change bank tab info from unexisting tab %d.",
1537 session->GetPlayerInfo().c_str(), tabId);
1538 return;
1539 }
1540
1541 tab->SetInfo(name, icon);
1542
1543 WorldPackets::Guild::GuildEventTabModified packet;
1544 packet.Tab = tabId;
1545 packet.Name = name;
1546 packet.Icon = icon;
1547 BroadcastPacket(packet.Write());
1548 }
1549
1550 void Guild::HandleSetMemberNote(WorldSession* session, std::string const& note, ObjectGuid guid, bool isPublic)
1551 {
1552 // Player must have rights to set public/officer note
1553 if (!_HasRankRight(session->GetPlayer(), isPublic ? GR_RIGHT_EDIT_PUBLIC_NOTE : GR_RIGHT_EOFFNOTE))
1554 SendCommandResult(session, GUILD_COMMAND_EDIT_PUBLIC_NOTE, ERR_GUILD_PERMISSIONS);
1555 else if (Member* member = GetMember(guid))
1556 {
1557 if (isPublic)
1558 member->SetPublicNote(note);
1559 else
1560 member->SetOfficerNote(note);
1561
1562 WorldPackets::Guild::GuildMemberUpdateNote updateNote;
1563 updateNote.Member = guid;
1564 updateNote.IsPublic = isPublic;
1565 updateNote.Note = note;
1566 BroadcastPacket(updateNote.Write());
1567 }
1568 }
1569
1570 void Guild::HandleSetRankInfo(WorldSession* session, uint8 rankId, std::string const& name, uint32 rights, uint32 moneyPerDay, const GuildBankRightsAndSlotsVec& rightsAndSlots)
1571 {
1572 // Only leader can modify ranks
1573 if (!_IsLeader(session->GetPlayer()))
1574 SendCommandResult(session, GUILD_COMMAND_CHANGE_RANK, ERR_GUILD_PERMISSIONS);
1575 else if (RankInfo* rankInfo = GetRankInfo(rankId))
1576 {
1577 TC_LOG_DEBUG("guild", "Changed RankName to '%s', rights to 0x%08X", name.c_str(), rights);
1578
1579 rankInfo->SetName(name);
1580 rankInfo->SetRights(rights);
1581 _SetRankBankMoneyPerDay(rankId, moneyPerDay);
1582
1583 for (auto itr = rightsAndSlots.begin(); itr != rightsAndSlots.end(); ++itr)
1584 _SetRankBankTabRightsAndSlots(rankId, *itr);
1585
1586 WorldPackets::Guild::GuildEventRankChanged packet;
1587 packet.RankID = rankId;
1588 BroadcastPacket(packet.Write());
1589 }
1590 }
1591
1592 void Guild::HandleBuyBankTab(WorldSession* session, uint8 tabId)
1593 {
1594 Player* player = session->GetPlayer();
1595 if (!player)
1596 return;
1597
1598 Member const* member = GetMember(player->GetGUID());
1599 if (!member)
1600 return;
1601
1602 if (_GetPurchasedTabsSize() >= GUILD_BANK_MAX_TABS)
1603 return;
1604
1605 if (tabId != _GetPurchasedTabsSize())
1606 return;
1607
1608 if (tabId >= GUILD_BANK_MAX_TABS)
1609 return;
1610
1611 // Do not get money for bank tabs that the GM bought, we had to buy them already.
1612 // This is just a speedup check, GetGuildBankTabPrice will return 0.
1613 if (tabId < GUILD_BANK_MAX_TABS - 2) // 7th tab is actually the 6th
1614 {
1615 int64 tabCost = GetGuildBankTabPrice(tabId) * GOLD;
1616 if (!player->HasEnoughMoney(tabCost)) // Should not happen, this is checked by client
1617 return;
1618
1619 player->ModifyMoney(-tabCost);
1620 }
1621
1622 _CreateNewBankTab();
1623
1624 WorldPackets::Guild::GuildEventTabAdded packet;
1625 BroadcastPacket(packet.Write());
1626
1627 SendPermissions(session); /// Hack to force client to update permissions
1628 }
1629
1630 void Guild::HandleInviteMember(WorldSession* session, std::string const& name)
1631 {
1632 Player* pInvitee = ObjectAccessor::FindPlayerByName(name);
1633 if (!pInvitee)
1634 {
1635 SendCommandResult(session, GUILD_COMMAND_INVITE_PLAYER, ERR_GUILD_PLAYER_NOT_FOUND_S, name);
1636 return;
1637 }
1638
1639 Player* player = session->GetPlayer();
1640 // Do not show invitations from ignored players
1641 if (pInvitee->GetSocial()->HasIgnore(player->GetGUID()))
1642 return;
1643
1644 if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && pInvitee->GetTeam() != player->GetTeam())
1645 {
1646 SendCommandResult(session, GUILD_COMMAND_INVITE_PLAYER, ERR_GUILD_NOT_ALLIED, name);
1647 return;
1648 }
1649
1650 // Invited player cannot be in another guild
1651 if (pInvitee->GetGuildId())
1652 {
1653 SendCommandResult(session, GUILD_COMMAND_INVITE_PLAYER, ERR_ALREADY_IN_GUILD_S, name);
1654 return;
1655 }
1656
1657 // Invited player cannot be invited
1658 if (pInvitee->GetGuildIdInvited())
1659 {
1660 SendCommandResult(session, GUILD_COMMAND_INVITE_PLAYER, ERR_ALREADY_INVITED_TO_GUILD_S, name);
1661 return;
1662 }
1663
1664 // Inviting player must have rights to invite
1665 if (!_HasRankRight(player, GR_RIGHT_INVITE))
1666 {
1667 SendCommandResult(session, GUILD_COMMAND_INVITE_PLAYER, ERR_GUILD_PERMISSIONS);
1668 return;
1669 }
1670
1671 SendCommandResult(session, GUILD_COMMAND_INVITE_PLAYER, ERR_GUILD_COMMAND_SUCCESS, name);
1672
1673 TC_LOG_DEBUG("guild", "Player %s invited %s to join his Guild", player->GetName().c_str(), name.c_str());
1674
1675 pInvitee->SetGuildIdInvited(m_id);
1676 _LogEvent(GUILD_EVENT_LOG_INVITE_PLAYER, player->GetGUID().GetCounter(), pInvitee->GetGUID().GetCounter());
1677
1678 WorldPackets::Guild::GuildInvite invite;
1679
1680 invite.InviterVirtualRealmAddress = GetVirtualRealmAddress();
1681 invite.GuildVirtualRealmAddress = GetVirtualRealmAddress();
1682 invite.GuildGUID = GetGUID();
1683
1684 invite.EmblemStyle = uint32(m_emblemInfo.GetStyle());
1685 invite.EmblemColor = uint32(m_emblemInfo.GetColor());
1686 invite.BorderStyle = uint32(m_emblemInfo.GetBorderStyle());
1687 invite.BorderColor = uint32(m_emblemInfo.GetBorderColor());
1688 invite.Background = uint32(m_emblemInfo.GetBackgroundColor());
1689 invite.AchievementPoints = GetAchievementMgr().GetAchievementPoints();
1690
1691 invite.InviterName = player->GetName();
1692 invite.GuildName = GetName();
1693
1694 if (Guild* oldGuild = pInvitee->GetGuild())
1695 {
1696 invite.OldGuildGUID = oldGuild->GetGUID();
1697 invite.OldGuildName = oldGuild->GetName();
1698 invite.OldGuildVirtualRealmAddress = GetVirtualRealmAddress();
1699 }
1700
1701 pInvitee->GetSession()->SendPacket(invite.Write());
1702 TC_LOG_DEBUG("guild", "SMSG_GUILD_INVITE [%s]", pInvitee->GetName().c_str());
1703 }
1704
1705 void Guild::HandleAcceptMember(WorldSession* session)
1706 {
1707 Player* player = session->GetPlayer();
1708 if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) &&
1709 player->GetTeam() != ObjectMgr::GetPlayerTeamByGUID(GetLeaderGUID()))
1710 return;
1711
1712 SQLTransaction trans(nullptr);
1713 AddMember(trans, player->GetGUID());
1714 }
1715
1716 void Guild::HandleLeaveMember(WorldSession* session)
1717 {
1718 Player* player = session->GetPlayer();
1719 bool disband = false;
1720
1721 // If leader is leaving
1722 if (_IsLeader(player))
1723 {
1724 if (m_members.size() > 1)
1725 // Leader cannot leave if he is not the last member
1726 SendCommandResult(session, GUILD_COMMAND_LEAVE_GUILD, ERR_GUILD_LEADER_LEAVE);
1727 else
1728 {
1729 // Guild is disbanded if leader leaves.
1730 Disband();
1731 disband = true;
1732 }
1733 }
1734 else
1735 {
1736 _LogEvent(GUILD_EVENT_LOG_LEAVE_GUILD, player->GetGUID().GetCounter());
1737 SendEventPlayerLeft(GetMember(player->GetGUID()));
1738
1739 SQLTransaction trans(nullptr);
1740 DeleteMember(trans, player->GetGUID(), false, false);
1741
1742 SendCommandResult(session, GUILD_COMMAND_LEAVE_GUILD, ERR_GUILD_COMMAND_SUCCESS, m_name);
1743 }
1744
1745 sCalendarMgr->RemovePlayerGuildEventsAndSignups(player->GetGUID(), GetId());
1746
1747 if (disband)
1748 delete this;
1749 }
1750
1751 void Guild::HandleRemoveMember(WorldSession* session, ObjectGuid guid)
1752 {
1753 Player* player = session->GetPlayer();
1754
1755 // Player must have rights to remove members
1756 if (!_HasRankRight(player, GR_RIGHT_REMOVE))
1757 SendCommandResult(session, GUILD_COMMAND_REMOVE_PLAYER, ERR_GUILD_PERMISSIONS);
1758 else if (Member* member = GetMember(guid))
1759 {
1760 std::string name = member->GetName();
1761
1762 // Guild masters cannot be removed
1763 if (member->IsRank(GR_GUILDMASTER))
1764 SendCommandResult(session, GUILD_COMMAND_REMOVE_PLAYER, ERR_GUILD_LEADER_LEAVE);
1765 // Do not allow to remove player with the same rank or higher
1766 else
1767 {
1768 Member* memberMe = GetMember(player->GetGUID());
1769 if (!memberMe || member->IsRankNotLower(memberMe->GetRankId()))
1770 SendCommandResult(session, GUILD_COMMAND_REMOVE_PLAYER, ERR_GUILD_RANK_TOO_HIGH_S, name);
1771 else
1772 {
1773 _LogEvent(GUILD_EVENT_LOG_UNINVITE_PLAYER, player->GetGUID().GetCounter(), guid.GetCounter());
1774 SendEventPlayerLeft(member, memberMe, true);
1775
1776 // After call to DeleteMember pointer to member becomes invalid
1777 SQLTransaction trans(nullptr);
1778 DeleteMember(trans, guid, false, true);
1779
1780 SendCommandResult(session, GUILD_COMMAND_REMOVE_PLAYER, ERR_GUILD_COMMAND_SUCCESS, name);
1781 }
1782 }
1783 }
1784 }
1785
1786 void Guild::HandleUpdateMemberRank(WorldSession* session, ObjectGuid guid, bool demote)
1787 {
1788 Player* player = session->GetPlayer();
1789 GuildCommandType type = demote ? GUILD_COMMAND_DEMOTE_PLAYER : GUILD_COMMAND_PROMOTE_PLAYER;
1790 // Player must have rights to promote
1791 if (!_HasRankRight(player, demote ? GR_RIGHT_DEMOTE : GR_RIGHT_PROMOTE))
1792 SendCommandResult(session, type, ERR_GUILD_PERMISSIONS);
1793 // Promoted player must be a member of guild
1794 else if (Member* member = GetMember(guid))
1795 {
1796 std::string name = member->GetName();
1797 // Player cannot promote himself
1798 if (member->IsSamePlayer(player->GetGUID()))
1799 {
1800 SendCommandResult(session, type, ERR_GUILD_NAME_INVALID);
1801 return;
1802 }
1803
1804 Member const* memberMe = GetMember(player->GetGUID());
1805 ASSERT(memberMe);
1806 uint8 rankId = memberMe->GetRankId();
1807 if (demote)
1808 {
1809 // Player can demote only lower rank members
1810 if (member->IsRankNotLower(rankId))
1811 {
1812 SendCommandResult(session, type, ERR_GUILD_RANK_TOO_HIGH_S, name);
1813 return;
1814 }
1815 // Lowest rank cannot be demoted
1816 if (member->GetRankId() >= _GetLowestRankId())
1817 {
1818 SendCommandResult(session, type, ERR_GUILD_RANK_TOO_LOW_S, name);
1819 return;
1820 }
1821 }
1822 else
1823 {
1824 // Allow to promote only to lower rank than member's rank
1825 // member->GetRankId() + 1 is the highest rank that current player can promote to
1826 if (member->IsRankNotLower(rankId + 1))
1827 {
1828 SendCommandResult(session, type, ERR_GUILD_RANK_TOO_HIGH_S, name);
1829 return;
1830 }
1831 }
1832
1833 uint32 newRankId = member->GetRankId() + (demote ? 1 : -1);
1834 SQLTransaction trans(nullptr);
1835 member->ChangeRank(trans, newRankId);
1836 _LogEvent(demote ? GUILD_EVENT_LOG_DEMOTE_PLAYER : GUILD_EVENT_LOG_PROMOTE_PLAYER, player->GetGUID().GetCounter(), member->GetGUID().GetCounter(), newRankId);
1837 //_BroadcastEvent(demote ? GE_DEMOTION : GE_PROMOTION, ObjectGuid::Empty, player->GetName().c_str(), name.c_str(), _GetRankName(newRankId).c_str());
1838 }
1839 }
1840
1841 void Guild::HandleSetMemberRank(WorldSession* session, ObjectGuid targetGuid, ObjectGuid setterGuid, uint32 rank)
1842 {
1843 Player* player = session->GetPlayer();
1844 Member* member = GetMember(targetGuid);
1845 if (!member)
1846 return;
1847 GuildRankRights rights = GR_RIGHT_PROMOTE;
1848 GuildCommandType type = GUILD_COMMAND_PROMOTE_PLAYER;
1849
1850 if (rank > member->GetRankId())
1851 {
1852 rights = GR_RIGHT_DEMOTE;
1853 type = GUILD_COMMAND_DEMOTE_PLAYER;
1854 }
1855
1856 // Promoted player must be a member of guild
1857 if (!_HasRankRight(player, rights))
1858 {
1859 SendCommandResult(session, type, ERR_GUILD_PERMISSIONS);
1860 return;
1861 }
1862
1863 // Player cannot promote himself
1864 if (member->IsSamePlayer(player->GetGUID()))
1865 {
1866 SendCommandResult(session, type, ERR_GUILD_NAME_INVALID);
1867 return;
1868 }
1869
1870 SendGuildRanksUpdate(setterGuid, targetGuid, rank);
1871 }
1872
1873 void Guild::HandleAddNewRank(WorldSession* session, std::string const& name)
1874 {
1875 uint8 size = _GetRanksSize();
1876 if (size >= GUILD_RANKS_MAX_COUNT)
1877 return;
1878
1879 // Only leader can add new rank
1880 if (_IsLeader(session->GetPlayer()))
1881 {
1882 SQLTransaction trans(nullptr);
1883 if (_CreateRank(trans, name, GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK))
1884 {
1885 WorldPackets::Guild::GuildEventRanksUpdated eventPacket;
1886 BroadcastPacket(eventPacket.Write());
1887 }
1888 }
1889 }
1890
1891 void Guild::HandleRemoveRank(WorldSession* session, uint8 rankId)
1892 {
1893 // Cannot remove rank if total count is minimum allowed by the client or is not leader
1894 if (_GetRanksSize() <= GUILD_RANKS_MIN_COUNT || rankId >= _GetRanksSize() || !_IsLeader(session->GetPlayer()))
1895 return;
1896
1897 // Delete bank rights for rank
1898 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_RIGHTS_FOR_RANK);
1899 stmt->setUInt64(0, m_id);
1900 stmt->setUInt8(1, rankId);
1901 CharacterDatabase.Execute(stmt);
1902
1903 // Delete rank
1904 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_RANK);
1905 stmt->setUInt64(0, m_id);
1906 stmt->setUInt8(1, rankId);
1907 CharacterDatabase.Execute(stmt);
1908
1909 m_ranks.erase(m_ranks.begin() + rankId);
1910
1911 WorldPackets::Guild::GuildEventRanksUpdated eventPacket;
1912 BroadcastPacket(eventPacket.Write());
1913 }
1914
1915 void Guild::HandleMemberDepositMoney(WorldSession* session, uint64 amount, bool cashFlow /*=false*/)
1916 {
1917 // guild bank cannot have more than MAX_MONEY_AMOUNT
1918 amount = std::min(amount, MAX_MONEY_AMOUNT - m_bankMoney);
1919 if (!amount)
1920 return;
1921
1922 Player* player = session->GetPlayer();
1923
1924 // Call script after validation and before money transfer.
1925 sScriptMgr->OnGuildMemberDepositMoney(this, player, amount);
1926
1927 SQLTransaction trans = CharacterDatabase.BeginTransaction();
1928 _ModifyBankMoney(trans, amount, true);
1929 if (!cashFlow)
1930 {
1931 player->ModifyMoney(-int64(amount));
1932 player->SaveGoldToDB(trans);
1933 }
1934
1935 _LogBankEvent(trans, cashFlow ? GUILD_BANK_LOG_CASH_FLOW_DEPOSIT : GUILD_BANK_LOG_DEPOSIT_MONEY, uint8(0), player->GetGUID().GetCounter(), amount);
1936 CharacterDatabase.CommitTransaction(trans);
1937
1938 SendEventBankMoneyChanged();
1939
1940 if (player->GetSession()->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE))
1941 {
1942 sLog->outCommand(player->GetSession()->GetAccountId(),
1943 "GM %s (Account: %u) deposit money (Amount: " UI64FMTD ") to guild bank (Guild ID " UI64FMTD ")",
1944 player->GetName().c_str(), player->GetSession()->GetAccountId(), amount, m_id);
1945 }
1946 }
1947
1948 bool Guild::HandleMemberWithdrawMoney(WorldSession* session, uint64 amount, bool repair)
1949 {
1950 // clamp amount to MAX_MONEY_AMOUNT, Players can't hold more than that anyway
1951 amount = std::min(amount, MAX_MONEY_AMOUNT);
1952
1953 if (m_bankMoney < amount) // Not enough money in bank
1954 return false;
1955
1956 Player* player = session->GetPlayer();
1957
1958 Member* member = GetMember(player->GetGUID());
1959 if (!member)
1960 return false;
1961
1962 if (!_HasRankRight(player, repair ? GR_RIGHT_WITHDRAW_REPAIR : GR_RIGHT_WITHDRAW_GOLD))
1963 return false;
1964
1965 if (_GetMemberRemainingMoney(member) < int64(amount)) // Check if we have enough slot/money today
1966 return false;
1967
1968 // Call script after validation and before money transfer.
1969 sScriptMgr->OnGuildMemberWitdrawMoney(this, player, amount, repair);
1970
1971 SQLTransaction trans = CharacterDatabase.BeginTransaction();
1972 // Add money to player (if required)
1973 if (!repair)
1974 {
1975 if (!player->ModifyMoney(amount))
1976 return false;
1977
1978 player->SaveGoldToDB(trans);
1979 }
1980
1981 // Update remaining money amount
1982 member->UpdateBankMoneyWithdrawValue(trans, amount);
1983 // Remove money from bank
1984 _ModifyBankMoney(trans, amount, false);
1985
1986 // Log guild bank event
1987 _LogBankEvent(trans, repair ? GUILD_BANK_LOG_REPAIR_MONEY : GUILD_BANK_LOG_WITHDRAW_MONEY, uint8(0), player->GetGUID().GetCounter(), amount);
1988 CharacterDatabase.CommitTransaction(trans);
1989
1990 SendEventBankMoneyChanged();
1991 return true;
1992 }
1993
1994 void Guild::HandleMemberLogout(WorldSession* session)
1995 {
1996 Player* player = session->GetPlayer();
1997 if (Member* member = GetMember(player->GetGUID()))
1998 {
1999 member->SetStats(player);
2000 member->UpdateLogoutTime();
2001 member->ResetFlags();
2002 }
2003
2004 SendEventPresenceChanged(session, false, true);
2005 SaveToDB();
2006 }
2007
2008 void Guild::HandleDelete(WorldSession* session)
2009 {
2010 // Only leader can disband guild
2011 if (_IsLeader(session->GetPlayer()))
2012 {
2013 Disband();
2014 TC_LOG_DEBUG("guild", "%s successfully deleted", GetGUID().ToString().c_str());
2015 delete this;
2016 }
2017 }
2018
2019 void Guild::HandleGuildPartyRequest(WorldSession* session) const
2020 {
2021 Player* player = session->GetPlayer();
2022 Group* group = player->GetGroup();
2023
2024 // Make sure player is a member of the guild and that he is in a group.
2025 if (!IsMember(player->GetGUID()) || !group)
2026 return;
2027
2028 WorldPackets::Guild::GuildPartyState partyStateResponse;
2029 partyStateResponse.InGuildParty = (player->GetMap()->GetOwnerGuildId(player->GetTeam()) == GetId());
2030 partyStateResponse.NumMembers = 0;
2031 partyStateResponse.NumRequired = 0;
2032 partyStateResponse.GuildXPEarnedMult = 0.0f;
2033 session->SendPacket(partyStateResponse.Write());
2034
2035 TC_LOG_DEBUG("guild", "SMSG_GUILD_PARTY_STATE_RESPONSE [%s]", session->GetPlayerInfo().c_str());
2036 }
2037
2038 void Guild::HandleGuildRequestChallengeUpdate(WorldSession* session) const
2039 {
2040 WorldPackets::Guild::GuildChallengeUpdate updatePacket;
2041
2042 for (int i = 0; i < GUILD_CHALLENGES_TYPES; ++i)
2043 updatePacket.CurrentCount[i] = int32(0); /// @todo current count
2044
2045 for (int i = 0; i < GUILD_CHALLENGES_TYPES; ++i)
2046 updatePacket.MaxCount[i] = int32(GuildChallengesMaxCount[i]);
2047
2048 for (int i = 0; i < GUILD_CHALLENGES_TYPES; ++i)
2049 updatePacket.MaxLevelGold[i] = int32(GuildChallengeMaxLevelGoldReward[i]);
2050
2051 for (int i = 0; i < GUILD_CHALLENGES_TYPES; ++i)
2052 updatePacket.Gold[i] = int32(GuildChallengeGoldReward[i]);
2053
2054 session->SendPacket(updatePacket.Write());
2055 }
2056
2057 void Guild::SendEventLog(WorldSession* session) const
2058 {
2059 GuildLog* logs = m_eventLog->GetGuildLog();
2060
2061 if (!logs)
2062 return;
2063
2064 WorldPackets::Guild::GuildEventLogQueryResults packet;
2065 packet.Entry.reserve(m_eventLog->GetSize());
2066
2067 for (GuildLog::const_iterator itr = logs->begin(); itr != logs->end(); ++itr)
2068 {
2069 EventLogEntry* eventLog = static_cast<EventLogEntry*>(*itr);
2070 eventLog->WritePacket(packet);
2071 }
2072
2073 session->SendPacket(packet.Write());
2074
2075 TC_LOG_DEBUG("guild", "SMSG_GUILD_EVENT_LOG_QUERY_RESULTS [%s]", session->GetPlayerInfo().c_str());
2076 }
2077
2078 void Guild::SendNewsUpdate(WorldSession* session) const
2079 {
2080 GuildLog* logs = m_newsLog->GetGuildLog();
2081
2082 if (!logs)
2083 return;
2084
2085 WorldPackets::Guild::GuildNews packet;
2086 packet.NewsEvents.reserve(m_newsLog->GetSize());
2087
2088 for (GuildLog::const_iterator itr = logs->begin(); itr != logs->end(); ++itr)
2089 {
2090 NewsLogEntry* eventLog = static_cast<NewsLogEntry*>(*itr);
2091 eventLog->WritePacket(packet);
2092 }
2093
2094 session->SendPacket(packet.Write());
2095
2096 TC_LOG_DEBUG("guild", "SMSG_GUILD_NEWS_UPDATE [%s]", session->GetPlayerInfo().c_str());
2097 }
2098
2099 void Guild::SendBankLog(WorldSession* session, uint8 tabId) const
2100 {
2101 // GUILD_BANK_MAX_TABS send by client for money log
2102 if (tabId < _GetPurchasedTabsSize() || tabId == GUILD_BANK_MAX_TABS)
2103 {
2104 GuildLog* logs = m_bankEventLog[tabId]->GetGuildLog();
2105
2106 if (!logs)
2107 return;
2108
2109 WorldPackets::Guild::GuildBankLogQueryResults packet;
2110 packet.Tab = int32(tabId);
2111
2112 //if (tabId == GUILD_BANK_MAX_TABS && hasCashFlow)
2113 // packet.WeeklyBonusMoney.Set(uint64(weeklyBonusMoney));
2114
2115 packet.Entry.reserve(m_bankEventLog[tabId]->GetSize());
2116 for (GuildLog::const_iterator itr = logs->begin(); itr != logs->end(); ++itr)
2117 {
2118 BankEventLogEntry* bankEventLog = static_cast<BankEventLogEntry*>(*itr);
2119 bankEventLog->WritePacket(packet);
2120 }
2121
2122 session->SendPacket(packet.Write());
2123
2124 TC_LOG_DEBUG("guild", "SMSG_GUILD_BANK_LOG_QUERY_RESULT [%s] TabId: %u", session->GetPlayerInfo().c_str(), tabId);
2125 }
2126 }
2127
2128 void Guild::SendBankTabText(WorldSession* session, uint8 tabId) const
2129 {
2130 if (BankTab const* tab = GetBankTab(tabId))
2131 tab->SendText(this, session);
2132 }
2133
2134 void Guild::SendPermissions(WorldSession* session) const
2135 {
2136 Member const* member = GetMember(session->GetPlayer()->GetGUID());
2137 if (!member)
2138 return;
2139
2140 uint8 rankId = member->GetRankId();
2141
2142 WorldPackets::Guild::GuildPermissionsQueryResults queryResult;
2143 queryResult.RankID = rankId;
2144 queryResult.WithdrawGoldLimit = int32(_GetRankBankMoneyPerDay(rankId));
2145 queryResult.Flags = _GetRankRights(rankId);
2146 queryResult.NumTabs = _GetPurchasedTabsSize();
2147 queryResult.Tab.reserve(GUILD_BANK_MAX_TABS);
2148
2149 for (uint8 tabId = 0; tabId < GUILD_BANK_MAX_TABS; ++tabId)
2150 {
2151 WorldPackets::Guild::GuildPermissionsQueryResults::GuildRankTabPermissions tabPerm;
2152 tabPerm.Flags = _GetRankBankTabRights(rankId, tabId);
2153 tabPerm.WithdrawItemLimit = _GetMemberRemainingSlots(member, tabId);
2154 queryResult.Tab.push_back(tabPerm);
2155 }
2156
2157 session->SendPacket(queryResult.Write());
2158 TC_LOG_DEBUG("guild", "SMSG_GUILD_PERMISSIONS_QUERY_RESULTS [%s] Rank: %u", session->GetPlayerInfo().c_str(), rankId);
2159 }
2160
2161 void Guild::SendMoneyInfo(WorldSession* session) const
2162 {
2163 Member const* member = GetMember(session->GetPlayer()->GetGUID());
2164 if (!member)
2165 return;
2166
2167 int64 amount = _GetMemberRemainingMoney(member);
2168
2169 WorldPackets::Guild::GuildBankRemainingWithdrawMoney packet;
2170 packet.RemainingWithdrawMoney = amount;
2171 session->SendPacket(packet.Write());
2172
2173 TC_LOG_DEBUG("guild", "SMSG_GUILD_BANK_MONEY_WITHDRAWN [%s] Money: " SI64FMTD, session->GetPlayerInfo().c_str(), amount);
2174 }
2175
2176 void Guild::SendLoginInfo(WorldSession* session)
2177 {
2178 Player* player = session->GetPlayer();
2179 Member* member = GetMember(player->GetGUID());
2180 if (!member)
2181 return;
2182
2183 /*
2184 Login sequence:
2185 SMSG_GUILD_EVENT_MOTD
2186 SMSG_GUILD_RANK
2187 SMSG_GUILD_EVENT_PRESENCE_CHANGE - LoggedOn: True
2188 -- learn perks
2189 SMSG_ALL_GUILD_ACHIEVEMENTS
2190 SMSG_GUILD_MEMBER_DAILY_RESET // bank withdrawal reset
2191 */
2192
2193 SendEventMOTD(session);
2194 SendGuildRankInfo(session);
2195 SendEventPresenceChanged(session, true, true); // Broadcast
2196
2197 // Send to self separately, player is not in world yet and is not found by _BroadcastEvent
2198 SendEventPresenceChanged(session, true);
2199
2200 if (member->GetGUID() == GetLeaderGUID())
2201 {
2202 WorldPackets::Guild::GuildFlaggedForRename renameFlag;
2203 renameFlag.FlagSet = false;
2204 player->GetSession()->SendPacket(renameFlag.Write());
2205 }
2206
2207 for (GuildPerkSpellsEntry const* entry : sGuildPerkSpellsStore)
2208 player->LearnSpell(entry->SpellID, true);
2209
2210 m_achievementMgr.SendAllData(player);
2211
2212 WorldPackets::Guild::GuildMemberDailyReset packet; // tells the client to request bank withdrawal limit
2213 player->GetSession()->SendPacket(packet.Write());
2214
2215 member->SetStats(player);
2216 member->AddFlag(GUILDMEMBER_STATUS_ONLINE);
2217 }
2218
2219 void Guild::SendEventBankMoneyChanged() const
2220 {
2221 WorldPackets::Guild::GuildEventBankMoneyChanged eventPacket;
2222 eventPacket.Money = GetBankMoney();
2223 BroadcastPacket(eventPacket.Write());
2224 }
2225
2226 void Guild::SendEventMOTD(WorldSession* session, bool broadcast) const
2227 {
2228 WorldPackets::Guild::GuildEventMotd eventPacket;
2229 eventPacket.MotdText = GetMOTD();
2230
2231 if (broadcast)
2232 BroadcastPacket(eventPacket.Write());
2233 else
2234 {
2235 session->SendPacket(eventPacket.Write());
2236 TC_LOG_DEBUG("guild", "SMSG_GUILD_EVENT_MOTD [%s] ", session->GetPlayerInfo().c_str());
2237 }
2238 }
2239
2240 void Guild::SendEventNewLeader(Member* newLeader, Member* oldLeader, bool isSelfPromoted) const
2241 {
2242 WorldPackets::Guild::GuildEventNewLeader eventPacket;
2243 eventPacket.SelfPromoted = isSelfPromoted;
2244 if (newLeader)
2245 {
2246 eventPacket.NewLeaderGUID = newLeader->GetGUID();
2247 eventPacket.NewLeaderName = newLeader->GetName();
2248 eventPacket.NewLeaderVirtualRealmAddress = GetVirtualRealmAddress();
2249 }
2250
2251 if (oldLeader)
2252 {
2253 eventPacket.OldLeaderGUID = oldLeader->GetGUID();
2254 eventPacket.OldLeaderName = oldLeader->GetName();
2255 eventPacket.OldLeaderVirtualRealmAddress = GetVirtualRealmAddress();
2256 }
2257
2258 BroadcastPacket(eventPacket.Write());
2259 }
2260
2261 void Guild::SendEventPlayerLeft(Member* leaver, Member* remover, bool isRemoved) const
2262 {
2263 WorldPackets::Guild::GuildEventPlayerLeft eventPacket;
2264 eventPacket.Removed = isRemoved;
2265 eventPacket.LeaverGUID = leaver->GetGUID();
2266 eventPacket.LeaverName = leaver->GetName();
2267 eventPacket.LeaverVirtualRealmAddress = GetVirtualRealmAddress();
2268
2269 if (isRemoved && remover != nullptr)
2270 {
2271 eventPacket.RemoverGUID = remover->GetGUID();
2272 eventPacket.RemoverName = remover->GetName();
2273 eventPacket.RemoverVirtualRealmAddress = GetVirtualRealmAddress();
2274 }
2275
2276 BroadcastPacket(eventPacket.Write());
2277 }
2278
2279 void Guild::SendEventPresenceChanged(WorldSession* session, bool loggedOn, bool broadcast) const
2280 {
2281 Player* player = session->GetPlayer();
2282
2283 WorldPackets::Guild::GuildEventPresenceChange eventPacket;
2284 eventPacket.Guid = player->GetGUID();
2285 eventPacket.Name = player->GetName();
2286 eventPacket.VirtualRealmAddress = GetVirtualRealmAddress();
2287 eventPacket.LoggedOn = loggedOn;
2288 eventPacket.Mobile = false;
2289
2290 if (broadcast)
2291 BroadcastPacket(eventPacket.Write());
2292 else
2293 session->SendPacket(eventPacket.Write());
2294 }
2295
2296 // Loading methods
2297 bool Guild::LoadFromDB(Field* fields)
2298 {
2299 m_id = fields[0].GetUInt64();
2300 m_name = fields[1].GetString();
2301 m_leaderGuid = ObjectGuid::Create<HighGuid::Player>(fields[2].GetUInt64());
2302
2303 if (!m_emblemInfo.LoadFromDB(fields))
2304 {
2305 TC_LOG_ERROR("guild", "Guild " UI64FMTD " has invalid emblem colors (Background: %u, Border: %u, Emblem: %u), skipped.",
2306 m_id, m_emblemInfo.GetBackgroundColor(), m_emblemInfo.GetBorderColor(), m_emblemInfo.GetColor());
2307 return false;
2308 }
2309
2310 m_info = fields[8].GetString();
2311 m_motd = fields[9].GetString();
2312 m_createdDate = time_t(fields[10].GetUInt32());
2313 m_bankMoney = fields[11].GetUInt64();
2314
2315 uint8 purchasedTabs = uint8(fields[12].GetUInt64());
2316 if (purchasedTabs > GUILD_BANK_MAX_TABS)
2317 purchasedTabs = GUILD_BANK_MAX_TABS;
2318
2319 m_bankTabs.resize(purchasedTabs);
2320 for (uint8 i = 0; i < purchasedTabs; ++i)
2321 m_bankTabs[i] = new BankTab(m_id, i);
2322
2323 _CreateLogHolders();
2324 return true;
2325 }
2326
2327 void Guild::LoadRankFromDB(Field* fields)
2328 {
2329 RankInfo rankInfo(m_id);
2330
2331 rankInfo.LoadFromDB(fields);
2332
2333 m_ranks.push_back(rankInfo);
2334 }
2335
2336 bool Guild::LoadMemberFromDB(Field* fields)
2337 {
2338 ObjectGuid::LowType lowguid = fields[1].GetUInt64();
2339 Member *member = new Member(m_id, ObjectGuid::Create<HighGuid::Player>(lowguid), fields[2].GetUInt8());
2340 if (!member->LoadFromDB(fields))
2341 {
2342 SQLTransaction trans(nullptr);
2343 Guild::_DeleteMemberFromDB(trans, lowguid);
2344 delete member;
2345 return false;
2346 }
2347
2348 m_members[member->GetGUID()] = member;
2349 return true;
2350 }
2351
2352 void Guild::LoadBankRightFromDB(Field* fields)
2353 {
2354 // tabId rights slots
2355 GuildBankRightsAndSlots rightsAndSlots(fields[1].GetUInt8(), fields[3].GetInt8(), fields[4].GetInt32());
2356 // rankId
2357 _SetRankBankTabRightsAndSlots(fields[2].GetUInt8(), rightsAndSlots, false);
2358 }
2359
2360 bool Guild::LoadEventLogFromDB(Field* fields) const
2361 {
2362 if (m_eventLog->CanInsert())
2363 {
2364 m_eventLog->LoadEvent(new EventLogEntry(
2365 m_id, // guild id
2366 fields[1].GetUInt32(), // guid
2367 time_t(fields[6].GetUInt32()), // timestamp
2368 GuildEventLogTypes(fields[2].GetUInt8()), // event type
2369 fields[3].GetUInt64(), // player guid 1
2370 fields[4].GetUInt64(), // player guid 2
2371 fields[5].GetUInt8())); // rank
2372 return true;
2373 }
2374 return false;
2375 }
2376
2377 bool Guild::LoadBankEventLogFromDB(Field* fields)
2378 {
2379 uint8 dbTabId = fields[1].GetUInt8();
2380 bool isMoneyTab = (dbTabId == GUILD_BANK_MONEY_LOGS_TAB);
2381 if (dbTabId < _GetPurchasedTabsSize() || isMoneyTab)
2382 {
2383 uint8 tabId = isMoneyTab ? uint8(GUILD_BANK_MAX_TABS) : dbTabId;
2384 LogHolder* pLog = m_bankEventLog[tabId];
2385 if (pLog->CanInsert())
2386 {
2387 uint32 guid = fields[2].GetUInt32();
2388 GuildBankEventLogTypes eventType = GuildBankEventLogTypes(fields[3].GetUInt8());
2389 if (BankEventLogEntry::IsMoneyEvent(eventType))
2390 {
2391 if (!isMoneyTab)
2392 {
2393 TC_LOG_ERROR("guild", "GuildBankEventLog ERROR: MoneyEvent(LogGuid: %u, Guild: " UI64FMTD ") does not belong to money tab (%u), ignoring...", guid, m_id, dbTabId);
2394 return false;
2395 }
2396 }
2397 else if (isMoneyTab)
2398 {
2399 TC_LOG_ERROR("guild", "GuildBankEventLog ERROR: non-money event (LogGuid: %u, Guild: " UI64FMTD ") belongs to money tab, ignoring...", guid, m_id);
2400 return false;
2401 }
2402 pLog->LoadEvent(new BankEventLogEntry(
2403 m_id, // guild id
2404 guid, // guid
2405 time_t(fields[8].GetUInt32()), // timestamp
2406 dbTabId, // tab id
2407 eventType, // event type
2408 fields[4].GetUInt64(), // player guid
2409 fields[5].GetUInt64(), // item or money
2410 fields[6].GetUInt16(), // itam stack count
2411 fields[7].GetUInt8())); // dest tab id
2412 }
2413 }
2414 return true;
2415 }
2416
2417 void Guild::LoadGuildNewsLogFromDB(Field* fields) const
2418 {
2419 if (!m_newsLog->CanInsert())
2420 return;
2421
2422 m_newsLog->LoadEvent(new NewsLogEntry(
2423 m_id, // guild id
2424 fields[1].GetUInt32(), // guid
2425 fields[6].GetUInt32(), // timestamp //64 bits?
2426 GuildNews(fields[2].GetUInt8()), // type
2427 ObjectGuid::Create<HighGuid::Player>(fields[3].GetUInt64()), // player guid
2428 fields[4].GetUInt32(), // Flags
2429 fields[5].GetUInt32())); // value
2430 }
2431
2432 void Guild::LoadBankTabFromDB(Field* fields)
2433 {
2434 uint8 tabId = fields[1].GetUInt8();
2435 if (tabId >= _GetPurchasedTabsSize())
2436 TC_LOG_ERROR("guild", "Invalid tab (tabId: %u) in guild bank, skipped.", tabId);
2437 else
2438 m_bankTabs[tabId]->LoadFromDB(fields);
2439 }
2440
2441 bool Guild::LoadBankItemFromDB(Field* fields)
2442 {
2443 uint8 tabId = fields[46].GetUInt8();
2444 if (tabId >= _GetPurchasedTabsSize())
2445 {
2446 TC_LOG_ERROR("guild", "Invalid tab for item (GUID: %u, id: #%u) in guild bank, skipped.",
2447 fields[0].GetUInt32(), fields[1].GetUInt32());
2448 return false;
2449 }
2450 return m_bankTabs[tabId]->LoadItemFromDB(fields);
2451 }
2452
2453 // Validates guild data loaded from database. Returns false if guild should be deleted.
2454 bool Guild::Validate()
2455 {
2456 // Validate ranks data
2457 // GUILD RANKS represent a sequence starting from 0 = GUILD_MASTER (ALL PRIVILEGES) to max 9 (lowest privileges).
2458 // The lower rank id is considered higher rank - so promotion does rank-- and demotion does rank++
2459 // Between ranks in sequence cannot be gaps - so 0, 1, 2, 4 is impossible
2460 // Min ranks count is 2 and max is 10.
2461 bool broken_ranks = false;
2462 uint8 ranks = _GetRanksSize();
2463
2464 SQLTransaction trans = CharacterDatabase.BeginTransaction();
2465 if (ranks < GUILD_RANKS_MIN_COUNT || ranks > GUILD_RANKS_MAX_COUNT)
2466 {
2467 TC_LOG_ERROR("guild", "Guild " UI64FMTD " has invalid number of ranks, creating new...", m_id);
2468 broken_ranks = true;
2469 }
2470 else
2471 {
2472 for (uint8 rankId = 0; rankId < ranks; ++rankId)
2473 {
2474 RankInfo* rankInfo = GetRankInfo(rankId);
2475 if (rankInfo->GetId() != rankId)
2476 {
2477 TC_LOG_ERROR("guild", "Guild " UI64FMTD " has broken rank id %u, creating default set of ranks...", m_id, rankId);
2478 broken_ranks = true;
2479 }
2480 else
2481 rankInfo->CreateMissingTabsIfNeeded(_GetPurchasedTabsSize(), trans, true);
2482 }
2483 }
2484
2485 if (broken_ranks)
2486 {
2487 m_ranks.clear();
2488 _CreateDefaultGuildRanks(trans, DEFAULT_LOCALE);
2489 }
2490
2491 // Validate members' data
2492 for (auto itr = m_members.begin(); itr != m_members.end(); ++itr)
2493 if (itr->second->GetRankId() > _GetRanksSize())
2494 itr->second->ChangeRank(trans, _GetLowestRankId());
2495
2496 // Repair the structure of the guild.
2497 // If the guildmaster doesn't exist or isn't member of the guild
2498 // attempt to promote another member.
2499 Member* leader = GetMember(m_leaderGuid);
2500 if (!leader)
2501 {
2502 SQLTransaction trans(nullptr);
2503 DeleteMember(trans, m_leaderGuid);
2504 // If no more members left, disband guild
2505 if (m_members.empty())
2506 {
2507 Disband();
2508 return false;
2509 }
2510 }
2511 else if (!leader->IsRank(GR_GUILDMASTER))
2512 _SetLeader(trans, leader);
2513
2514 // Check config if multiple guildmasters are allowed
2515 if (!sConfigMgr->GetBoolDefault("Guild.AllowMultipleGuildMaster", false))
2516 for (auto itr = m_members.begin(); itr != m_members.end(); ++itr)
2517 if (itr->second->GetRankId() == GR_GUILDMASTER && !itr->second->IsSamePlayer(m_leaderGuid))
2518 itr->second->ChangeRank(trans, GR_OFFICER);
2519
2520 if (trans->GetSize() > 0)
2521 CharacterDatabase.CommitTransaction(trans);
2522 _UpdateAccountsNumber();
2523 return true;
2524 }
2525
2526 // Broadcasts
2527 void Guild::BroadcastToGuild(WorldSession* session, bool officerOnly, std::string const& msg, uint32 language) const
2528 {
2529 if (session && session->GetPlayer() && _HasRankRight(session->GetPlayer(), officerOnly ? GR_RIGHT_OFFCHATSPEAK : GR_RIGHT_GCHATSPEAK))
2530 {
2531 WorldPackets::Chat::Chat packet;
2532 packet.Initialize(officerOnly ? CHAT_MSG_OFFICER : CHAT_MSG_GUILD, Language(language), session->GetPlayer(), nullptr, msg);
2533 WorldPacket const* data = packet.Write();
2534 for (auto itr = m_members.begin(); itr != m_members.end(); ++itr)
2535 if (Player* player = itr->second->FindConnectedPlayer())
2536 if (player->GetSession() && _HasRankRight(player, officerOnly ? GR_RIGHT_OFFCHATLISTEN : GR_RIGHT_GCHATLISTEN) &&
2537 !player->GetSocial()->HasIgnore(session->GetPlayer()->GetGUID()))
2538 player->GetSession()->SendPacket(data);
2539 }
2540 }
2541
2542 void Guild::BroadcastAddonToGuild(WorldSession* session, bool officerOnly, std::string const& msg, std::string const& prefix) const
2543 {
2544 if (session && session->GetPlayer() && _HasRankRight(session->GetPlayer(), officerOnly ? GR_RIGHT_OFFCHATSPEAK : GR_RIGHT_GCHATSPEAK))
2545 {
2546 WorldPackets::Chat::Chat packet;
2547 packet.Initialize(officerOnly ? CHAT_MSG_OFFICER : CHAT_MSG_GUILD, LANG_ADDON, session->GetPlayer(), nullptr, msg, 0, "", DEFAULT_LOCALE, prefix);
2548 WorldPacket const* data = packet.Write();
2549 for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
2550 if (Player* player = itr->second->FindPlayer())
2551 if (player->GetSession() && _HasRankRight(player, officerOnly ? GR_RIGHT_OFFCHATLISTEN : GR_RIGHT_GCHATLISTEN) &&
2552 !player->GetSocial()->HasIgnore(session->GetPlayer()->GetGUID()) &&
2553 player->GetSession()->IsAddonRegistered(prefix))
2554 player->GetSession()->SendPacket(data);
2555 }
2556 }
2557
2558 void Guild::BroadcastPacketToRank(WorldPacket const* packet, uint8 rankId) const
2559 {
2560 for (auto itr = m_members.begin(); itr != m_members.end(); ++itr)
2561 if (itr->second->IsRank(rankId))
2562 if (Player* player = itr->second->FindConnectedPlayer())
2563 player->GetSession()->SendPacket(packet);
2564 }
2565
2566 void Guild::BroadcastPacket(WorldPacket const* packet) const
2567 {
2568 for (auto itr = m_members.begin(); itr != m_members.end(); ++itr)
2569 if (Player* player = itr->second->FindPlayer())
2570 player->GetSession()->SendPacket(packet);
2571 }
2572
2573 void Guild::BroadcastPacketIfTrackingAchievement(WorldPacket const* packet, uint32 criteriaId) const
2574 {
2575 for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
2576 if (itr->second->IsTrackingCriteriaId(criteriaId))
2577 if (Player* player = itr->second->FindPlayer())
2578 player->GetSession()->SendPacket(packet);
2579 }
2580
2581 void Guild::MassInviteToEvent(WorldSession* session, uint32 minLevel, uint32 maxLevel, uint32 minRank)
2582 {
2583 WorldPackets::Calendar::CalendarEventInitialInvites packet;
2584
2585 for (auto itr = m_members.begin(); itr != m_members.end(); ++itr)
2586 {
2587 // not sure if needed, maybe client checks it as well
2588 if (packet.Invites.size() >= CALENDAR_MAX_INVITES)
2589 {
2590 if (Player* player = session->GetPlayer())
2591 sCalendarMgr->SendCalendarCommandResult(player->GetGUID(), CALENDAR_ERROR_INVITES_EXCEEDED);
2592 return;
2593 }
2594
2595 Member* member = itr->second;
2596 uint32 level = Player::GetLevelFromDB(member->GetGUID());
2597
2598 if (member->GetGUID() != session->GetPlayer()->GetGUID() && level >= minLevel && level <= maxLevel && member->IsRankNotLower(minRank))
2599 packet.Invites.emplace_back(member->GetGUID(), level);
2600 }
2601
2602 session->SendPacket(packet.Write());
2603 }
2604
2605 // Members handling
2606 bool Guild::AddMember(SQLTransaction& trans, ObjectGuid guid, uint8 rankId)
2607 {
2608 Player* player = ObjectAccessor::FindConnectedPlayer(guid);
2609 // Player cannot be in guild
2610 if (player)
2611 {
2612 if (player->GetGuildId())
2613 return false;
2614 }
2615 else if (Player::GetGuildIdFromDB(guid))
2616 return false;
2617
2618 // Remove all player signs from another petitions
2619 // This will be prevent attempt to join many guilds and corrupt guild data integrity
2620 Player::RemovePetitionsAndSigns(guid);
2621
2622 ObjectGuid::LowType lowguid = guid.GetCounter();
2623
2624 // If rank was not passed, assign lowest possible rank
2625 if (rankId == GUILD_RANK_NONE)
2626 rankId = _GetLowestRankId();
2627
2628 Member* member = new Member(m_id, guid, rankId);
2629 std::string name;
2630 if (player)
2631 {
2632 m_members[guid] = member;
2633 player->SetInGuild(m_id);
2634 player->SetGuildIdInvited(UI64LIT(0));
2635 player->SetGuildRank(rankId);
2636 player->SetGuildLevel(GetLevel());
2637 SendLoginInfo(player->GetSession());
2638 name = player->GetName();
2639 }
2640 else
2641 {
2642 member->ResetFlags();
2643
2644 bool ok = false;
2645 // Player must exist
2646 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_DATA_FOR_GUILD);
2647 stmt->setUInt64(0, lowguid);
2648 if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
2649 {
2650 Field* fields = result->Fetch();
2651 name = fields[0].GetString();
2652 member->SetStats(
2653 name,
2654 fields[1].GetUInt8(),
2655 fields[2].GetUInt8(),
2656 fields[3].GetUInt8(),
2657 fields[4].GetUInt16(),
2658 fields[5].GetUInt32(),
2659 0);
2660
2661 ok = member->CheckStats();
2662 }
2663
2664 if (!ok)
2665 {
2666 delete member;
2667 return false;
2668 }
2669 m_members[guid] = member;
2670 }
2671
2672 member->SaveToDB(trans);
2673
2674 _UpdateAccountsNumber();
2675 _LogEvent(GUILD_EVENT_LOG_JOIN_GUILD, lowguid);
2676
2677 WorldPackets::Guild::GuildEventPlayerJoined joinNotificationPacket;
2678 joinNotificationPacket.Guid = guid;
2679 joinNotificationPacket.Name = name;
2680 joinNotificationPacket.VirtualRealmAddress = GetVirtualRealmAddress();
2681 BroadcastPacket(joinNotificationPacket.Write());
2682
2683 sGuildFinderMgr->RemoveAllMembershipRequestsFromPlayer(guid);
2684
2685 // Call scripts if member was succesfully added (and stored to database)
2686 sScriptMgr->OnGuildAddMember(this, player, rankId);
2687
2688 return true;
2689 }
2690
2691 void Guild::DeleteMember(SQLTransaction& trans, ObjectGuid guid, bool isDisbanding, bool isKicked, bool canDeleteGuild)
2692 {
2693 // Guild master can be deleted when loading guild and guid doesn't exist in characters table
2694 // or when he is removed from guild by gm command
2695 if (m_leaderGuid == guid && !isDisbanding)
2696 {
2697 Member* oldLeader = nullptr;
2698 Member* newLeader = nullptr;
2699 for (auto i = m_members.begin(); i != m_members.end(); ++i)
2700 {
2701 if (i->first == guid)
2702 oldLeader = i->second;
2703 else if (!newLeader || newLeader->GetRankId() > i->second->GetRankId())
2704 newLeader = i->second;
2705 }
2706
2707 if (!newLeader)
2708 {
2709 Disband();
2710 if (canDeleteGuild)
2711 delete this;
2712 return;
2713 }
2714
2715 _SetLeader(trans, newLeader);
2716
2717 // If leader does not exist (at guild loading with deleted leader) do not send broadcasts
2718 if (oldLeader)
2719 {
2720 SendEventNewLeader(newLeader, oldLeader, true);
2721 SendEventPlayerLeft(oldLeader);
2722 }
2723 }
2724 // Call script on remove before member is actually removed from guild (and database)
2725 sScriptMgr->OnGuildRemoveMember(this, guid, isDisbanding, isKicked);
2726
2727 if (Member* member = GetMember(guid))
2728 delete member;
2729 m_members.erase(guid);
2730
2731 // If player not online data in data field will be loaded from guild tabs no need to update it !!
2732 Player* player = ObjectAccessor::FindConnectedPlayer(guid);
2733 if (player)
2734 {
2735 player->SetInGuild(UI64LIT(0));
2736 player->SetGuildRank(0);
2737 player->SetGuildLevel(0);
2738
2739 for (GuildPerkSpellsEntry const* entry : sGuildPerkSpellsStore)
2740 player->RemoveSpell(entry->SpellID, false, false);
2741 }
2742
2743 Guild::_DeleteMemberFromDB(trans, guid.GetCounter());
2744 if (!isDisbanding)
2745 _UpdateAccountsNumber();
2746 }
2747
2748 bool Guild::ChangeMemberRank(SQLTransaction& trans, ObjectGuid guid, uint8 newRank)
2749 {
2750 if (newRank <= _GetLowestRankId()) // Validate rank (allow only existing ranks)
2751 {
2752 if (Member* member = GetMember(guid))
2753 {
2754 member->ChangeRank(trans, newRank);
2755 return true;
2756 }
2757 }
2758
2759 return false;
2760 }
2761
2762 bool Guild::IsMember(ObjectGuid guid) const
2763 {
2764 Members::const_iterator itr = m_members.find(guid);
2765 return itr != m_members.end();
2766 }
2767
2768 // Bank (items move)
2769 void Guild::SwapItems(Player* player, uint8 tabId, uint8 slotId, uint8 destTabId, uint8 destSlotId, uint32 splitedAmount)
2770 {
2771 if (tabId >= _GetPurchasedTabsSize() || slotId >= GUILD_BANK_MAX_SLOTS ||
2772 destTabId >= _GetPurchasedTabsSize() || destSlotId >= GUILD_BANK_MAX_SLOTS)
2773 return;
2774
2775 if (tabId == destTabId && slotId == destSlotId)
2776 return;
2777
2778 BankMoveItemData from(this, player, tabId, slotId);
2779 BankMoveItemData to(this, player, destTabId, destSlotId);
2780 _MoveItems(&from, &to, splitedAmount);
2781 }
2782
2783 void Guild::SwapItemsWithInventory(Player* player, bool toChar, uint8 tabId, uint8 slotId, uint8 playerBag, uint8 playerSlotId, uint32 splitedAmount)
2784 {
2785 if ((slotId >= GUILD_BANK_MAX_SLOTS && slotId != NULL_SLOT) || tabId >= _GetPurchasedTabsSize())
2786 return;
2787
2788 BankMoveItemData bankData(this, player, tabId, slotId);
2789 PlayerMoveItemData charData(this, player, playerBag, playerSlotId);
2790 if (toChar)
2791 _MoveItems(&bankData, &charData, splitedAmount);
2792 else
2793 _MoveItems(&charData, &bankData, splitedAmount);
2794 }
2795
2796 // Bank tabs
2797 void Guild::SetBankTabText(uint8 tabId, std::string const& text)
2798 {
2799 if (BankTab* pTab = GetBankTab(tabId))
2800 {
2801 pTab->SetText(text);
2802 pTab->SendText(this, nullptr);
2803
2804 WorldPackets::Guild::GuildEventTabTextChanged eventPacket;
2805 eventPacket.Tab = tabId;
2806 BroadcastPacket(eventPacket.Write());
2807 }
2808 }
2809
2810 bool Guild::_HasRankRight(Player const* player, uint32 right) const
2811 {
2812 if (player)
2813 if (Member const* member = GetMember(player->GetGUID()))
2814 return (_GetRankRights(member->GetRankId()) & right) != GR_RIGHT_NONE;
2815 return false;
2816 }
2817
2818 void Guild::_DeleteMemberFromDB(SQLTransaction& trans, ObjectGuid::LowType lowguid)
2819 {
2820 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBER);
2821 stmt->setUInt64(0, lowguid);
2822 CharacterDatabase.ExecuteOrAppend(trans, stmt);
2823 }
2824
2825 // Private methods
2826 void Guild::_CreateLogHolders()
2827 {
2828 m_eventLog = new LogHolder(sWorld->getIntConfig(CONFIG_GUILD_EVENT_LOG_COUNT));
2829 m_newsLog = new LogHolder(sWorld->getIntConfig(CONFIG_GUILD_NEWS_LOG_COUNT));
2830 for (uint8 tabId = 0; tabId <= GUILD_BANK_MAX_TABS; ++tabId)
2831 m_bankEventLog[tabId] = new LogHolder(sWorld->getIntConfig(CONFIG_GUILD_BANK_EVENT_LOG_COUNT));
2832 }
2833
2834 void Guild::_CreateNewBankTab()
2835 {
2836 uint8 tabId = _GetPurchasedTabsSize(); // Next free id
2837 m_bankTabs.push_back(new BankTab(m_id, tabId));
2838
2839 SQLTransaction trans = CharacterDatabase.BeginTransaction();
2840
2841 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_TAB);
2842 stmt->setUInt64(0, m_id);
2843 stmt->setUInt8 (1, tabId);
2844 trans->Append(stmt);
2845
2846 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_TAB);
2847 stmt->setUInt64(0, m_id);
2848 stmt->setUInt8 (1, tabId);
2849 trans->Append(stmt);
2850
2851 ++tabId;
2852 for (auto itr = m_ranks.begin(); itr != m_ranks.end(); ++itr)
2853 (*itr).CreateMissingTabsIfNeeded(tabId, trans, false);
2854
2855 CharacterDatabase.CommitTransaction(trans);
2856 }
2857
2858 void Guild::_CreateDefaultGuildRanks(SQLTransaction& trans, LocaleConstant loc)
2859 {
2860 ASSERT(trans);
2861
2862 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_RANKS);
2863 stmt->setUInt64(0, m_id);
2864 trans->Append(stmt);
2865
2866 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_RIGHTS);
2867 stmt->setUInt64(0, m_id);
2868 trans->Append(stmt);
2869
2870 _CreateRank(trans, sObjectMgr->GetTrinityString(LANG_GUILD_MASTER, loc), GR_RIGHT_ALL);
2871 _CreateRank(trans, sObjectMgr->GetTrinityString(LANG_GUILD_OFFICER, loc), GR_RIGHT_ALL);
2872 _CreateRank(trans, sObjectMgr->GetTrinityString(LANG_GUILD_VETERAN, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
2873 _CreateRank(trans, sObjectMgr->GetTrinityString(LANG_GUILD_MEMBER, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
2874 _CreateRank(trans, sObjectMgr->GetTrinityString(LANG_GUILD_INITIATE, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
2875 }
2876
2877 bool Guild::_CreateRank(SQLTransaction& trans, std::string const& name, uint32 rights)
2878 {
2879 uint8 newRankId = _GetRanksSize();
2880 if (newRankId >= GUILD_RANKS_MAX_COUNT)
2881 return false;
2882
2883 // Ranks represent sequence 0, 1, 2, ... where 0 means guildmaster
2884 RankInfo info(m_id, newRankId, name, rights, 0);
2885 m_ranks.push_back(info);
2886
2887 bool const isInTransaction = bool(trans);
2888 if (!isInTransaction)
2889 trans = CharacterDatabase.BeginTransaction();
2890
2891 info.CreateMissingTabsIfNeeded(_GetPurchasedTabsSize(), trans);
2892 info.SaveToDB(trans);
2893
2894 if (!isInTransaction)
2895 CharacterDatabase.CommitTransaction(trans);
2896
2897 return true;
2898 }
2899
2900 // Updates the number of accounts that are in the guild
2901 // Player may have many characters in the guild, but with the same account
2902 void Guild::_UpdateAccountsNumber()
2903 {
2904 // We use a set to be sure each element will be unique
2905 std::set<uint32> accountsIdSet;
2906 for (auto itr = m_members.begin(); itr != m_members.end(); ++itr)
2907 accountsIdSet.insert(itr->second->GetAccountId());
2908
2909 m_accountsNumber = accountsIdSet.size();
2910 }
2911
2912 // Detects if player is the guild master.
2913 // Check both leader guid and player's rank (otherwise multiple feature with
2914 // multiple guild masters won't work)
2915 bool Guild::_IsLeader(Player* player) const
2916 {
2917 if (player->GetGUID() == m_leaderGuid)
2918 return true;
2919 if (const Member* member = GetMember(player->GetGUID()))
2920 return member->IsRank(GR_GUILDMASTER);
2921 return false;
2922 }
2923
2924 void Guild::_DeleteBankItems(SQLTransaction& trans, bool removeItemsFromDB)
2925 {
2926 for (uint8 tabId = 0; tabId < _GetPurchasedTabsSize(); ++tabId)
2927 {
2928 m_bankTabs[tabId]->Delete(trans, removeItemsFromDB);
2929 delete m_bankTabs[tabId];
2930 m_bankTabs[tabId] = nullptr;
2931 }
2932 m_bankTabs.clear();
2933 }
2934
2935 bool Guild::_ModifyBankMoney(SQLTransaction& trans, uint64 amount, bool add)
2936 {
2937 if (add)
2938 m_bankMoney += amount;
2939 else
2940 {
2941 // Check if there is enough money in bank.
2942 if (m_bankMoney < amount)
2943 return false;
2944 m_bankMoney -= amount;
2945 }
2946
2947 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_MONEY);
2948 stmt->setUInt64(0, m_bankMoney);
2949 stmt->setUInt64(1, m_id);
2950 trans->Append(stmt);
2951 return true;
2952 }
2953
2954 void Guild::_SetLeader(SQLTransaction& trans, Member* leader)
2955 {
2956 if (!leader)
2957 return;
2958
2959 bool isInTransaction = bool(trans);
2960 if (!isInTransaction)
2961 trans = CharacterDatabase.BeginTransaction();
2962
2963 m_leaderGuid = leader->GetGUID();
2964 leader->ChangeRank(trans, GR_GUILDMASTER);
2965
2966 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_LEADER);
2967 stmt->setUInt64(0, m_leaderGuid.GetCounter());
2968 stmt->setUInt64(1, m_id);
2969 trans->Append(stmt);
2970
2971 if (!isInTransaction)
2972 CharacterDatabase.CommitTransaction(trans);
2973 }
2974
2975 void Guild::_SetRankBankMoneyPerDay(uint8 rankId, uint32 moneyPerDay)
2976 {
2977 if (RankInfo* rankInfo = GetRankInfo(rankId))
2978 rankInfo->SetBankMoneyPerDay(moneyPerDay);
2979 }
2980
2981 void Guild::_SetRankBankTabRightsAndSlots(uint8 rankId, GuildBankRightsAndSlots rightsAndSlots, bool saveToDB)
2982 {
2983 if (rightsAndSlots.GetTabId() >= _GetPurchasedTabsSize())
2984 return;
2985
2986 if (RankInfo* rankInfo = GetRankInfo(rankId))
2987 rankInfo->SetBankTabSlotsAndRights(rightsAndSlots, saveToDB);
2988 }
2989
2990 inline std::string Guild::_GetRankName(uint8 rankId) const
2991 {
2992 if (const RankInfo* rankInfo = GetRankInfo(rankId))
2993 return rankInfo->GetName();
2994 return "<unknown>";
2995 }
2996
2997 inline uint32 Guild::_GetRankRights(uint8 rankId) const
2998 {
2999 if (const RankInfo* rankInfo = GetRankInfo(rankId))
3000 return rankInfo->GetRights();
3001 return 0;
3002 }
3003
3004 inline uint32 Guild::_GetRankBankMoneyPerDay(uint8 rankId) const
3005 {
3006 if (const RankInfo* rankInfo = GetRankInfo(rankId))
3007 return rankInfo->GetBankMoneyPerDay();
3008 return 0;
3009 }
3010
3011 inline int32 Guild::_GetRankBankTabSlotsPerDay(uint8 rankId, uint8 tabId) const
3012 {
3013 if (tabId < _GetPurchasedTabsSize())
3014 if (const RankInfo* rankInfo = GetRankInfo(rankId))
3015 return rankInfo->GetBankTabSlotsPerDay(tabId);
3016 return 0;
3017 }
3018
3019 inline int8 Guild::_GetRankBankTabRights(uint8 rankId, uint8 tabId) const
3020 {
3021 if (const RankInfo* rankInfo = GetRankInfo(rankId))
3022 return rankInfo->GetBankTabRights(tabId);
3023 return 0;
3024 }
3025
3026 inline int32 Guild::_GetMemberRemainingSlots(Member const* member, uint8 tabId) const
3027 {
3028 if (member)
3029 {
3030 uint8 rankId = member->GetRankId();
3031 if (rankId == GR_GUILDMASTER)
3032 return static_cast<int32>(GUILD_WITHDRAW_SLOT_UNLIMITED);
3033 if ((_GetRankBankTabRights(rankId, tabId) & GUILD_BANK_RIGHT_VIEW_TAB) != 0)
3034 {
3035 int32 remaining = _GetRankBankTabSlotsPerDay(rankId, tabId) - member->GetBankTabWithdrawValue(tabId);
3036 if (remaining > 0)
3037 return remaining;
3038 }
3039 }
3040 return 0;
3041 }
3042
3043 inline int64 Guild::_GetMemberRemainingMoney(Member const* member) const
3044 {
3045 if (member)
3046 {
3047 uint8 rankId = member->GetRankId();
3048 if (rankId == GR_GUILDMASTER)
3049 return std::numeric_limits<int64>::max();
3050
3051 if ((_GetRankRights(rankId) & (GR_RIGHT_WITHDRAW_REPAIR | GR_RIGHT_WITHDRAW_GOLD)) != 0)
3052 {
3053 int64 remaining = (int64(_GetRankBankMoneyPerDay(rankId)) * GOLD) - member->GetBankMoneyWithdrawValue();
3054 if (remaining > 0)
3055 return remaining;
3056 }
3057 }
3058 return 0;
3059 }
3060
3061 inline void Guild::_UpdateMemberWithdrawSlots(SQLTransaction& trans, ObjectGuid guid, uint8 tabId)
3062 {
3063 if (Member* member = GetMember(guid))
3064 member->UpdateBankTabWithdrawValue(trans, tabId, 1);
3065 }
3066
3067 inline bool Guild::_MemberHasTabRights(ObjectGuid guid, uint8 tabId, int32 rights) const
3068 {
3069 if (const Member* member = GetMember(guid))
3070 {
3071 // Leader always has full rights
3072 if (member->IsRank(GR_GUILDMASTER) || m_leaderGuid == guid)
3073 return true;
3074 return (_GetRankBankTabRights(member->GetRankId(), tabId) & rights) == rights;
3075 }
3076 return false;
3077 }
3078
3079 // Add new event log record
3080 inline void Guild::_LogEvent(GuildEventLogTypes eventType, ObjectGuid::LowType playerGuid1, ObjectGuid::LowType playerGuid2, uint8 newRank)
3081 {
3082 SQLTransaction trans = CharacterDatabase.BeginTransaction();
3083 m_eventLog->AddEvent(trans, new EventLogEntry(m_id, m_eventLog->GetNextGUID(), eventType, playerGuid1, playerGuid2, newRank));
3084 CharacterDatabase.CommitTransaction(trans);
3085
3086 sScriptMgr->OnGuildEvent(this, uint8(eventType), playerGuid1, playerGuid2, newRank);
3087 }
3088
3089 // Add new bank event log record
3090 void Guild::_LogBankEvent(SQLTransaction& trans, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid::LowType lowguid, uint64 itemOrMoney, uint16 itemStackCount, uint8 destTabId)
3091 {
3092 if (tabId > GUILD_BANK_MAX_TABS)
3093 return;
3094
3095 // not logging moves within the same tab
3096 if (eventType == GUILD_BANK_LOG_MOVE_ITEM && tabId == destTabId)
3097 return;
3098
3099 uint8 dbTabId = tabId;
3100 if (BankEventLogEntry::IsMoneyEvent(eventType))
3101 {
3102 tabId = GUILD_BANK_MAX_TABS;
3103 dbTabId = GUILD_BANK_MONEY_LOGS_TAB;
3104 }
3105 LogHolder* pLog = m_bankEventLog[tabId];
3106 pLog->AddEvent(trans, new BankEventLogEntry(m_id, pLog->GetNextGUID(), eventType, dbTabId, lowguid, itemOrMoney, itemStackCount, destTabId));
3107
3108 sScriptMgr->OnGuildBankEvent(this, uint8(eventType), tabId, lowguid, itemOrMoney, itemStackCount, destTabId);
3109 }
3110
3111 inline Item* Guild::_GetItem(uint8 tabId, uint8 slotId) const
3112 {
3113 if (const BankTab* tab = GetBankTab(tabId))
3114 return tab->GetItem(slotId);
3115 return nullptr;
3116 }
3117
3118 inline void Guild::_RemoveItem(SQLTransaction& trans, uint8 tabId, uint8 slotId)
3119 {
3120 if (BankTab* pTab = GetBankTab(tabId))
3121 pTab->SetItem(trans, slotId, nullptr);
3122 }
3123
3124 void Guild::_MoveItems(MoveItemData* pSrc, MoveItemData* pDest, uint32 splitedAmount) const
3125 {
3126 // 1. Initialize source item
3127 if (!pSrc->InitItem())
3128 return; // No source item
3129
3130 // 2. Check source item
3131 if (!pSrc->CheckItem(splitedAmount))
3132 return; // Source item or splited amount is invalid
3133 /*
3134 if (pItemSrc->GetCount() == 0)
3135 {
3136 TC_LOG_FATAL("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!",
3137 player->GetName(), player->GetGUIDLow(), pItemSrc->GetEntry(), tabId, slotId, destTabId, destSlotId, pItemSrc->GetEntry());
3138 //return; // Commented out for now, uncomment when it's verified that this causes a crash!!
3139 }
3140 // */
3141
3142 // 3. Check destination rights
3143 if (!pDest->HasStoreRights(pSrc))
3144 return; // Player has no rights to store item in destination
3145
3146 // 4. Check source withdraw rights
3147 if (!pSrc->HasWithdrawRights(pDest))
3148 return; // Player has no rights to withdraw items from source
3149
3150 // 5. Check split
3151 if (splitedAmount)
3152 {
3153 // 5.1. Clone source item
3154 if (!pSrc->CloneItem(splitedAmount))
3155 return; // Item could not be cloned
3156
3157 // 5.2. Move splited item to destination
3158 Guild::_DoItemsMove(pSrc, pDest, true, splitedAmount);
3159 }
3160 else // 6. No split
3161 {
3162 // 6.1. Try to merge items in destination (pDest->GetItem() == nullptr)
3163 if (!Guild::_DoItemsMove(pSrc, pDest, false)) // Item could not be merged
3164 {
3165 // 6.2. Try to swap items
3166 // 6.2.1. Initialize destination item
3167 if (!pDest->InitItem())
3168 return;
3169
3170 // 6.2.2. Check rights to store item in source (opposite direction)
3171 if (!pSrc->HasStoreRights(pDest))
3172 return; // Player has no rights to store item in source (opposite direction)
3173
3174 if (!pDest->HasWithdrawRights(pSrc))
3175 return; // Player has no rights to withdraw item from destination (opposite direction)
3176
3177 // 6.2.3. Swap items (pDest->GetItem() != nullptr)
3178 Guild::_DoItemsMove(pSrc, pDest, true);
3179 }
3180 }
3181 // 7. Send changes
3182 _SendBankContentUpdate(pSrc, pDest);
3183 }
3184
3185 bool Guild::_DoItemsMove(MoveItemData* pSrc, MoveItemData* pDest, bool sendError, uint32 splitedAmount)
3186 {
3187 Item* pDestItem = pDest->GetItem();
3188 bool swap = (pDestItem != nullptr);
3189
3190 Item* pSrcItem = pSrc->GetItem(splitedAmount != 0);
3191 // 1. Can store source item in destination
3192 if (!pDest->CanStore(pSrcItem, swap, sendError))
3193 return false;
3194
3195 // 2. Can store destination item in source
3196 if (swap)
3197 if (!pSrc->CanStore(pDestItem, true, true))
3198 return false;
3199
3200 // GM LOG (@todo move to scripts)
3201 pDest->LogAction(pSrc);
3202 if (swap)
3203 pSrc->LogAction(pDest);
3204
3205 SQLTransaction trans = CharacterDatabase.BeginTransaction();
3206 // 3. Log bank events
3207 pDest->LogBankEvent(trans, pSrc, pSrcItem->GetCount());
3208 if (swap)
3209 pSrc->LogBankEvent(trans, pDest, pDestItem->GetCount());
3210
3211 // 4. Remove item from source
3212 pSrc->RemoveItem(trans, pDest, splitedAmount);
3213
3214 // 5. Remove item from destination
3215 if (swap)
3216 pDest->RemoveItem(trans, pSrc);
3217
3218 // 6. Store item in destination
3219 pDest->StoreItem(trans, pSrcItem);
3220
3221 // 7. Store item in source
3222 if (swap)
3223 pSrc->StoreItem(trans, pDestItem);
3224
3225 CharacterDatabase.CommitTransaction(trans);
3226 return true;
3227 }
3228
3229 void Guild::_SendBankContentUpdate(MoveItemData* pSrc, MoveItemData* pDest) const
3230 {
3231 ASSERT(pSrc->IsBank() || pDest->IsBank());
3232
3233 uint8 tabId = 0;
3234 SlotIds slots;
3235 if (pSrc->IsBank()) // B ->
3236 {
3237 tabId = pSrc->GetContainer();
3238 slots.insert(pSrc->GetSlotId());
3239 if (pDest->IsBank()) // B -> B
3240 {
3241 // Same tab - add destination slots to collection
3242 if (pDest->GetContainer() == pSrc->GetContainer())
3243 pDest->CopySlots(slots);
3244 else // Different tabs - send second message
3245 {
3246 SlotIds destSlots;
3247 pDest->CopySlots(destSlots);
3248 _SendBankContentUpdate(pDest->GetContainer(), destSlots);
3249 }
3250 }
3251 }
3252 else if (pDest->IsBank()) // C -> B
3253 {
3254 tabId = pDest->GetContainer();
3255 pDest->CopySlots(slots);
3256 }
3257
3258 _SendBankContentUpdate(tabId, slots);
3259 }
3260
3261 void Guild::_SendBankContentUpdate(uint8 tabId, SlotIds slots) const
3262 {
3263 if (BankTab const* tab = GetBankTab(tabId))
3264 {
3265 WorldPackets::Guild::GuildBankQueryResults packet;
3266 packet.FullUpdate = true; // @todo
3267 packet.Tab = int32(tabId);
3268 packet.Money = m_bankMoney;
3269
3270 for (SlotIds::const_iterator itr = slots.begin(); itr != slots.end(); ++itr)
3271 {
3272 Item const* tabItem = tab->GetItem(*itr);
3273
3274 WorldPackets::Guild::GuildBankItemInfo itemInfo;
3275
3276 itemInfo.Slot = int32(*itr);
3277 itemInfo.Item.ItemID = int32(tabItem ? tabItem->GetEntry() : 0);
3278 itemInfo.Count = int32(tabItem ? tabItem->GetCount() : 0);
3279 itemInfo.Charges = int32(tabItem ? abs(tabItem->GetSpellCharges()) : 0);
3280 itemInfo.OnUseEnchantmentID = 0/*int32(tabItem->GetItemSuffixFactor())*/;
3281 itemInfo.Flags = 0;
3282 itemInfo.Locked = false;
3283
3284 if (tabItem)
3285 {
3286 uint8 i = 0;
3287 for (ItemDynamicFieldGems const& gemData : tabItem->GetGems())
3288 {
3289 if (gemData.ItemId)
3290 {
3291 WorldPackets::Item::ItemGemData gem;
3292 gem.Slot = i;
3293 gem.Item.Initialize(&gemData);
3294 itemInfo.SocketEnchant.push_back(gem);
3295 }
3296 ++i;
3297 }
3298 }
3299
3300 packet.ItemInfo.push_back(itemInfo);
3301 }
3302
3303 for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
3304 if (_MemberHasTabRights(itr->second->GetGUID(), tabId, GUILD_BANK_RIGHT_VIEW_TAB))
3305 if (Player* player = itr->second->FindPlayer())
3306 {
3307 packet.WithdrawalsRemaining = _GetMemberRemainingSlots(itr->second, tabId);
3308 player->GetSession()->SendPacket(packet.Write());
3309 }
3310 }
3311 }
3312
3313 void Guild::SendBankList(WorldSession* session, uint8 tabId, bool fullUpdate) const
3314 {
3315 Member const* member = GetMember(session->GetPlayer()->GetGUID());
3316 if (!member) // Shouldn't happen, just in case
3317 return;
3318
3319 WorldPackets::Guild::GuildBankQueryResults packet;
3320
3321 packet.Money = m_bankMoney;
3322 packet.WithdrawalsRemaining = _GetMemberRemainingSlots(member, tabId);
3323 packet.Tab = int32(tabId);
3324 packet.FullUpdate = fullUpdate;
3325
3326 // TabInfo
3327 if (fullUpdate)
3328 {
3329 packet.TabInfo.reserve(_GetPurchasedTabsSize());
3330 for (uint8 i = 0; i < _GetPurchasedTabsSize(); ++i)
3331 {
3332 WorldPackets::Guild::GuildBankTabInfo tabInfo;
3333 tabInfo.TabIndex = i;
3334 tabInfo.Name = m_bankTabs[i]->GetName();
3335 tabInfo.Icon = m_bankTabs[i]->GetIcon();
3336 packet.TabInfo.push_back(tabInfo);
3337 }
3338 }
3339
3340 // ItemInfo
3341 uint32 itemCount = 0;
3342 if (fullUpdate && _MemberHasTabRights(session->GetPlayer()->GetGUID(), tabId, GUILD_BANK_RIGHT_VIEW_TAB))
3343 if (BankTab const* tab = GetBankTab(tabId))
3344 for (uint8 slotId = 0; slotId < GUILD_BANK_MAX_SLOTS; ++slotId)
3345 if (tab->GetItem(slotId))
3346 ++itemCount;
3347
3348 packet.ItemInfo.reserve(itemCount);
3349
3350 if (fullUpdate && _MemberHasTabRights(session->GetPlayer()->GetGUID(), tabId, GUILD_BANK_RIGHT_VIEW_TAB))
3351 {
3352 if (BankTab const* tab = GetBankTab(tabId))
3353 {
3354 for (uint8 slotId = 0; slotId < GUILD_BANK_MAX_SLOTS; ++slotId)
3355 {
3356 if (Item* tabItem = tab->GetItem(slotId))
3357 {
3358 WorldPackets::Guild::GuildBankItemInfo itemInfo;
3359
3360 itemInfo.Slot = int32(slotId);
3361 itemInfo.Item.ItemID = tabItem->GetEntry();
3362 itemInfo.Count = int32(tabItem->GetCount());
3363 itemInfo.Charges = int32(abs(tabItem->GetSpellCharges()));
3364 itemInfo.EnchantmentID = int32(tabItem->GetItemRandomPropertyId()); // verify that...
3365 itemInfo.OnUseEnchantmentID = 0/*int32(tabItem->GetItemSuffixFactor())*/;
3366 itemInfo.Flags = 0;
3367
3368 uint8 i = 0;
3369 for (ItemDynamicFieldGems const& gemData : tabItem->GetGems())
3370 {
3371 if (gemData.ItemId)
3372 {
3373 WorldPackets::Item::ItemGemData gem;
3374 gem.Slot = i;
3375 gem.Item.Initialize(&gemData);
3376 itemInfo.SocketEnchant.push_back(gem);
3377 }
3378 ++i;
3379 }
3380
3381 itemInfo.Locked = false;
3382
3383 packet.ItemInfo.push_back(itemInfo);
3384 }
3385 }
3386 }
3387 }
3388
3389 session->SendPacket(packet.Write());
3390 }
3391
3392 void Guild::SendGuildRanksUpdate(ObjectGuid setterGuid, ObjectGuid targetGuid, uint32 rank)
3393 {
3394 Member* member = GetMember(targetGuid);
3395 ASSERT(member);
3396
3397 WorldPackets::Guild::GuildSendRankChange rankChange;
3398 rankChange.Officer = setterGuid;
3399 rankChange.Other = targetGuid;
3400 rankChange.RankID = rank;
3401 rankChange.Promote = (rank < member->GetRankId());
3402 BroadcastPacket(rankChange.Write());
3403
3404 SQLTransaction trans;
3405 member->ChangeRank(trans, rank);
3406
3407 TC_LOG_DEBUG("network", "SMSG_GUILD_RANKS_UPDATE [Broadcast] Target: %s, Issuer: %s, RankId: %u",
3408 targetGuid.ToString().c_str(), setterGuid.ToString().c_str(), rank);
3409 }
3410
3411 void Guild::ResetTimes(bool weekly)
3412 {
3413 for (auto itr = m_members.begin(); itr != m_members.end(); ++itr)
3414 {
3415 itr->second->ResetValues(weekly);
3416 if (Player* player = itr->second->FindPlayer())
3417 {
3418 WorldPackets::Guild::GuildMemberDailyReset packet; // tells the client to request bank withdrawal limit
3419 player->GetSession()->SendPacket(packet.Write());
3420 }
3421 }
3422 }
3423
3424 void Guild::AddGuildNews(uint8 type, ObjectGuid guid, uint32 flags, uint32 value) const
3425 {
3426 NewsLogEntry* news = new NewsLogEntry(m_id, m_newsLog->GetNextGUID(), GuildNews(type), guid, flags, value);
3427
3428 SQLTransaction trans = CharacterDatabase.BeginTransaction();
3429 m_newsLog->AddEvent(trans, news);
3430 CharacterDatabase.CommitTransaction(trans);
3431
3432 WorldPackets::Guild::GuildNews newsPacket;
3433 newsPacket.NewsEvents.reserve(1);
3434 news->WritePacket(newsPacket);
3435 BroadcastPacket(newsPacket.Write());
3436 }
3437
3438 bool Guild::HasAchieved(uint32 achievementId) const
3439 {
3440 return m_achievementMgr.HasAchieved(achievementId);
3441 }
3442
3443 void Guild::UpdateCriteria(CriteriaTypes type, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit* unit, Player* player)
3444 {
3445 m_achievementMgr.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, player);
3446 }
3447
3448 void Guild::HandleNewsSetSticky(WorldSession* session, uint32 newsId, bool sticky) const
3449 {
3450 GuildLog* logs = m_newsLog->GetGuildLog();
3451 GuildLog::iterator itr = logs->begin();
3452 while (itr != logs->end() && (*itr)->GetGUID() != newsId)
3453 ++itr;
3454
3455 if (itr == logs->end())
3456 {
3457 TC_LOG_DEBUG("guild", "HandleNewsSetSticky: [%s] requested unknown newsId %u - Sticky: %u",
3458 session->GetPlayerInfo().c_str(), newsId, sticky);
3459 return;
3460 }
3461
3462 NewsLogEntry* news = static_cast<NewsLogEntry