/** * Handles the Packet sent by the client when sending a mail. * * This methods takes the packet sent by the client and performs the following actions: * - Checks whether the mail is valid: i.e. can he send the selected items, * does he have enough money, etc. * - Creates a MailDraft and adds the needed items, money, cost data. * - Sends the mail. * * Depending on the outcome of the checks performed the player will recieve a different * MailResponseResult. * * @see MailResponseResult * @see SendMailResult() * * @param recv_data the WorldPacket containing the data sent by the client. */ void WorldSession::HandleSendMail(WorldPacket& recv_data) { sLog.outError("WORLD: CMSG_SEND_MAIL"); ObjectGuid mailboxGuid; uint64 money, COD; std::string receiver, subject, body; uint8 receiverLen, subjectLen, bodyLen; uint32 unk1, unk2; recv_data >> unk1; // stationery? recv_data >> unk2; // 0x00000000 recv_data >> COD >> money; // cod and money bodyLen = recv_data.ReadBits(12); subjectLen = recv_data.ReadBits(9); uint8 items_count = recv_data.ReadBits(5); // attached items count if (items_count > MAX_MAIL_ITEMS) // client limit { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS); recv_data.rfinish(); // set to end to avoid warnings spam return; } recv_data.ReadGuidMask<0>(mailboxGuid); ObjectGuid itemGuids[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) recv_data.ReadGuidMask<2, 6, 3, 7, 1, 0, 4, 5>(itemGuids[i]); recv_data.ReadGuidMask<3, 4>(mailboxGuid); receiverLen = recv_data.ReadBits(7); recv_data.ReadGuidMask<2, 6, 1, 7, 5>(mailboxGuid); recv_data.ReadGuidBytes<4>(mailboxGuid); for (uint8 i = 0; i < items_count; ++i) { recv_data.ReadGuidBytes<6, 1, 7, 2>(itemGuids[i]); recv_data.read_skip<uint8>(); // item slot in mail, not used recv_data.ReadGuidBytes<3, 0, 4, 5>(itemGuids[i]); } recv_data.ReadGuidBytes<7, 3, 6, 5>(mailboxGuid); subject = recv_data.ReadString(subjectLen); receiver = recv_data.ReadString(receiverLen); recv_data.ReadGuidBytes<2, 0>(mailboxGuid); body = recv_data.ReadString(bodyLen); recv_data.ReadGuidBytes<1>(mailboxGuid); DEBUG_LOG("WORLD: CMSG_SEND_MAIL receiver '%s' subject '%s' body '%s' mailbox " UI64FMTD " money " UI64FMTD " COD " UI64FMTD " unkt1 %u unk2 %u", receiver.c_str(), subject.c_str(), body.c_str(), mailboxGuid.GetRawValue(), money, COD, unk1, unk2); // packet read complete, now do check if (!CheckMailBox(mailboxGuid)) return; if (receiver.empty()) return; Player* pl = _player; ObjectGuid rc; if (normalizePlayerName(receiver)) rc = sObjectMgr.GetPlayerGuidByName(receiver); if (!rc) { DEBUG_LOG("%s is sending mail to %s (GUID: nonexistent!) with subject %s and body %s includes %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u", pl->GetGuidStr().c_str(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND); return; } DEBUG_LOG("%s is sending mail to %s with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGuidStr().c_str(), rc.GetString().c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); if (pl->GetObjectGuid() == rc) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF); return; } uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client uint64 reqmoney = cost + money; if (pl->GetMoney() < reqmoney) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player* receive = sObjectMgr.GetPlayer(rc); Team rc_team; uint8 mails_count = 0; // do not allow to send to one player more than 100 mails if (receive) { rc_team = receive->GetTeam(); mails_count = receive->GetMailSize(); } else { rc_team = sObjectMgr.GetPlayerTeamByGUID(rc); if (QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", rc.GetCounter())) { Field* fields = result->Fetch(); mails_count = fields[0].GetUInt32(); delete result; } } // do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mails_count > 100) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // check the receiver's Faction... if (!sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : sObjectMgr.GetPlayerAccountIdByGUID(rc); Item* items[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { if (!itemGuids[i].IsItem()) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } Item* item = pl->GetItemByGuid(itemGuids[i]); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if (!item) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded(true)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (item->IsBoundAccountWide() && item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS); return; } if ((item->GetProto()->Flags & ITEM_FLAG_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } items[i] = item; } pl->SendMailResult(0, MAIL_SEND, MAIL_OK); pl->ModifyMoney(-int64(reqmoney)); pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; MailDraft draft(subject, body); if (items_count > 0 || money > 0) { if (items_count > 0) { for (uint8 i = 0; i < items_count; ++i) { Item* item = items[i]; if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) { sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), item->GetProto()->Name1, item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account); } pl->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true); CharacterDatabase.BeginTransaction(); item->DeleteFromInventoryDB(); // deletes item from character's inventory item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone // owner in data will set at mail receive and item extracting CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", rc.GetCounter(), item->GetGUIDLow()); CharacterDatabase.CommitTransaction(); draft.AddItem(item); } // if item send to character at another account, then apply item delivery delay needItemDelay = pl->GetSession()->GetAccountId() != rc_account; } if (money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) { sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail money: " UI64FMTD " to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account); } } // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_UINT32_MAIL_DELIVERY_DELAY) : 0; // will delete item or place to receiver mail list draft .SetMoney(money) .SetCOD(COD) .SendMailTo(MailReceiver(receive, rc), pl, body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); }
void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data ) { sLog.outDebug("WORLD: CMSG_AUTOSTORE_LOOT_ITEM"); Player *player = GetPlayer(); uint64 lguid = player->GetLootGUID(); Loot *loot; uint8 lootSlot; recv_data >> lootSlot; if (IS_GAMEOBJECT_GUID(lguid)) { GameObject *go = player->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))) { player->SendLootRelease(lguid); return; } loot = &go->loot; } else if (IS_ITEM_GUID(lguid)) { Item *pItem = player->GetItemByGuid( lguid ); if (!pItem) { player->SendLootRelease(lguid); return; } loot = &pItem->loot; } else if (IS_CORPSE_GUID(lguid)) { Corpse *bones = ObjectAccessor::GetCorpse(*player, lguid); if (!bones) { player->SendLootRelease(lguid); return; } loot = &bones->loot; } else { Creature* pCreature = GetPlayer()->GetMap()->GetCreature(lguid); bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed); if( !ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) { player->SendLootRelease(lguid); return; } loot = &pCreature->loot; } QuestItem *qitem = NULL; QuestItem *ffaitem = NULL; QuestItem *conditem = NULL; LootItem *item = loot->LootItemInSlot(lootSlot,player,&qitem,&ffaitem,&conditem); if(!item) { player->SendEquipError( EQUIP_ERR_ALREADY_LOOTED, NULL, NULL ); return; } // questitems use the blocked field for other purposes if (!qitem && item->is_blocked) { player->SendLootRelease(lguid); return; } ItemPosCountVec dest; uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count ); if ( msg == EQUIP_ERR_OK ) { Item * newitem = player->StoreNewItem( dest, item->itemid, true, item->randomPropertyId); if (qitem) { qitem->is_looted = true; //freeforall is 1 if everyone's supposed to get the quest item. if (item->freeforall || loot->GetPlayerQuestItems().size() == 1) player->SendNotifyLootItemRemoved(lootSlot); else loot->NotifyQuestItemRemoved(qitem->index); } else { if (ffaitem) { //freeforall case, notify only one player of the removal ffaitem->is_looted=true; player->SendNotifyLootItemRemoved(lootSlot); } else { //not freeforall, notify everyone if(conditem) conditem->is_looted=true; loot->NotifyItemRemoved(lootSlot); } } //if only one person is supposed to loot the item, then set it to looted if (!item->freeforall) item->is_looted = true; --loot->unlootedCount; player->SendNewItem(newitem, uint32(item->count), false, false, true); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type, item->count); } else player->SendEquipError( msg, NULL, NULL ); }
void WorldSession::HandleLootMasterGiveOpcode( WorldPacket & recv_data ) { uint8 slotid; uint64 lootguid, target_playerguid; recv_data >> lootguid >> slotid >> target_playerguid; if(!_player->GetGroup() || _player->GetGroup()->GetLooterGuid() != _player->GetGUID()) { _player->SendLootRelease(GetPlayer()->GetLootGUID()); return; } Player *target = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(target_playerguid, 0, HIGHGUID_PLAYER)); if(!target) return; sLog.outDebug("WorldSession::HandleLootMasterGiveOpcode (CMSG_LOOT_MASTER_GIVE, 0x02A3) Target = [%s].", target->GetName()); if(_player->GetLootGUID() != lootguid) return; Loot *pLoot = NULL; if(IS_CREATURE_GUID(GetPlayer()->GetLootGUID())) { Creature *pCreature = GetPlayer()->GetMap()->GetCreature(lootguid); if(!pCreature) return; pLoot = &pCreature->loot; } else if(IS_GAMEOBJECT_GUID(GetPlayer()->GetLootGUID())) { GameObject *pGO = GetPlayer()->GetMap()->GetGameObject(lootguid); if(!pGO) return; pLoot = &pGO->loot; } if(!pLoot) return; if (slotid > pLoot->items.size()) { sLog.outDebug("AutoLootItem: Player %s might be using a hack! (slot %d, size %lu)",GetPlayer()->GetName(), slotid, (unsigned long)pLoot->items.size()); return; } LootItem& item = pLoot->items[slotid]; ItemPosCountVec dest; uint8 msg = target->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item.itemid, item.count ); if ( msg != EQUIP_ERR_OK ) { target->SendEquipError( msg, NULL, NULL ); _player->SendEquipError( msg, NULL, NULL ); // send duplicate of error massage to master looter return; } // now move item from loot to target inventory Item * newitem = target->StoreNewItem( dest, item.itemid, true, item.randomPropertyId ); target->SendNewItem(newitem, uint32(item.count), false, false, true ); target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item.itemid, item.count); target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, pLoot->loot_type, item.count); // mark as looted item.count=0; item.is_looted=true; pLoot->NotifyItemRemoved(slotid); --pLoot->unlootedCount; }
void WorldSession::HandleSendMail(WorldPacket & recv_data ) { uint64 mailbox, unk3; std::string receiver, subject, body; uint32 unk1, unk2, money, COD; uint8 unk4; recv_data >> mailbox; recv_data >> receiver; recv_data >> subject; recv_data >> body; recv_data >> unk1; // stationery? recv_data >> unk2; // 0x00000000 uint8 items_count; recv_data >> items_count; // attached items count if (items_count > MAX_MAIL_ITEMS) // client limit { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS); recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam return; } uint64 itemGUIDs[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { recv_data.read_skip<uint8>(); // item slot in mail, not used recv_data >> itemGUIDs[i]; } recv_data >> money >> COD; // money and cod recv_data >> unk3; // const 0 recv_data >> unk4; // const 0 // packet read complete, now do check if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; if (receiver.empty()) return; Player* pl = _player; uint64 rc = 0; if (normalizePlayerName(receiver)) rc = objmgr.GetPlayerGUIDByName(receiver); if (!rc) { sLog.outDetail("Player %u is sending mail to %s (GUID: not existed!) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND); return; } sLog.outDetail("Player %u is sending mail to %s (GUID: %u) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); if (pl->GetGUID() == rc) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF); return; } uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client uint32 reqmoney = cost + money; if (pl->GetMoney() < reqmoney) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player *receive = objmgr.GetPlayer(rc); uint32 rc_team = 0; uint8 mails_count = 0; //do not allow to send to one player more than 100 mails if (receive) { rc_team = receive->GetTeam(); mails_count = receive->GetMailSize(); } else { rc_team = objmgr.GetPlayerTeamByGUID(rc); if (QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc))) { Field *fields = result->Fetch(); mails_count = fields[0].GetUInt32(); } } //do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mails_count > 100) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // test the receiver's Faction... if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : objmgr.GetPlayerAccountIdByGUID(rc); Item* items[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { if (!itemGUIDs[i]) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } Item* item = pl->GetItemByGuid(itemGUIDs[i]); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if (!item) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded(true)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (item->IsBoundAccountWide() && item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS); return; } if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } items[i] = item; } pl->SendMailResult(0, MAIL_SEND, MAIL_OK); uint32 itemTextId = !body.empty() ? objmgr.CreateItemText( body ) : 0; pl->ModifyMoney( -int32(reqmoney) ); pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; MailDraft draft(subject, itemTextId); if (items_count > 0 || money > 0) { if (items_count > 0) { for (uint8 i = 0; i < items_count; ++i) { Item* item = items[i]; if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) { sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), item->GetProto()->Name1, item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account); } pl->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true); CharacterDatabase.BeginTransaction(); item->DeleteFromInventoryDB(); // deletes item from character's inventory item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone // owner in data will set at mail receive and item extracting CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", GUID_LOPART(rc), item->GetGUIDLow()); CharacterDatabase.CommitTransaction(); draft.AddItem(item); } // if item send to character at another account, then apply item delivery delay needItemDelay = pl->GetSession()->GetAccountId() != rc_account; } if (money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) { sLog.outCommand(GetAccountId(),"GM %s (Account: %u) mail money: %u to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account); } } // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; // will delete item or place to receiver mail list draft .AddMoney(money) .AddCOD(COD) .SendMailTo(MailReceiver(receive, GUID_LOPART(rc)), pl, MAIL_CHECK_MASK_NONE, deliver_delay); CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); }
// does not clear ram void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction) { Item *pItem = GetAItem(auction->itemGuidLow); if (!pItem) return; ObjectGuid bidder_guid = ObjectGuid(HIGHGUID_PLAYER, auction->bidder); Player *bidder = sObjectMgr.GetPlayer(bidder_guid); uint32 bidder_accId = 0; ObjectGuid ownerGuid = ObjectGuid(HIGHGUID_PLAYER, auction->owner); Player* auction_owner = sObjectMgr.GetPlayer(ownerGuid); // data for gm.log if (sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) { AccountTypes bidder_security = SEC_PLAYER; std::string bidder_name; if (bidder) { bidder_accId = bidder->GetSession()->GetAccountId(); bidder_security = bidder->GetSession()->GetSecurity(); bidder_name = bidder->GetName(); } else { bidder_accId = sObjectMgr.GetPlayerAccountIdByGUID(bidder_guid); bidder_security = bidder_accId ? sAccountMgr.GetSecurity(bidder_accId) : SEC_PLAYER; if (bidder_security > SEC_PLAYER) // not do redundant DB requests { if (!sObjectMgr.GetPlayerNameByGUID(bidder_guid, bidder_name)) bidder_name = sObjectMgr.GetMangosStringForDBCLocale(LANG_UNKNOWN); } } if (bidder_security > SEC_PLAYER) { std::string owner_name; if (auction_owner) owner_name = auction_owner->GetName(); else if (ownerGuid && !sObjectMgr.GetPlayerNameByGUID(ownerGuid, owner_name)) owner_name = sObjectMgr.GetMangosStringForDBCLocale(LANG_UNKNOWN); uint32 owner_accid = sObjectMgr.GetPlayerAccountIdByGUID(ownerGuid); sLog.outCommand(bidder_accId,"GM %s (Account: %u) won item in auction (Entry: %u Count: %u) and pay money: %u. Original owner %s (Account: %u)", bidder_name.c_str(), bidder_accId, auction->itemTemplate, auction->itemCount, auction->bid, owner_name.c_str(), owner_accid); } } else if (!bidder) bidder_accId = sObjectMgr.GetPlayerAccountIdByGUID(bidder_guid); if (auction_owner) auction_owner->GetSession()->SendAuctionOwnerNotification(auction); // receiver exist if (bidder || bidder_accId) { std::ostringstream msgAuctionWonSubject; msgAuctionWonSubject << auction->itemTemplate << ':' << auction->itemRandomPropertyId << ':' << AUCTION_WON; std::ostringstream msgAuctionWonBody; msgAuctionWonBody.width(16); msgAuctionWonBody << std::right << std::hex << auction->owner; msgAuctionWonBody << std::dec << ':' << auction->bid << ':' << auction->buyout; DEBUG_LOG("AuctionWon body string : %s", msgAuctionWonBody.str().c_str()); // set owner to bidder (to prevent delete item with sender char deleting) // owner in `data` will set at mail receive and item extracting CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", auction->bidder, auction->itemGuidLow); if (bidder) { bidder->GetSession()->SendAuctionBidderNotification(auction); // FIXME: for offline player need also bidder->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS, 1); } RemoveAItem(auction->itemGuidLow); // we have to remove the item, before we delete it !! auction->itemGuidLow = 0; // pending list will not use guid data // will delete item or place to receiver mail list MailDraft(msgAuctionWonSubject.str(), msgAuctionWonBody.str()) .AddItem(pItem) .SendMailTo(MailReceiver(bidder, bidder_guid), auction, MAIL_CHECK_MASK_COPIED); } // receiver not exist else { CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", auction->itemGuidLow); RemoveAItem(auction->itemGuidLow); // we have to remove the item, before we delete it !! auction->itemGuidLow = 0; delete pItem; } }
void WorldSession::HandleLootMasterGiveOpcode(WorldPacket & recv_data) { uint8 slotid; uint64 lootguid, target_playerguid; recv_data >> lootguid >> slotid >> target_playerguid; if (!_player->GetGroup() || _player->GetGroup()->GetLooterGuid() != _player->GetGUID()) { _player->SendLootRelease(GetPlayer()->GetLootGUID()); return; } Player* target = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(target_playerguid, 0, HIGHGUID_PLAYER)); if (!target) return; sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSession::HandleLootMasterGiveOpcode (CMSG_LOOT_MASTER_GIVE, 0x02A3) Target = [%s].", target->GetName()); if (_player->GetLootGUID() != lootguid) return; Loot* loot = NULL; if (IS_CRE_OR_VEH_GUID(GetPlayer()->GetLootGUID())) { Creature* creature = GetPlayer()->GetMap()->GetCreature(lootguid); if (!creature) return; loot = &creature->loot; } else if (IS_GAMEOBJECT_GUID(GetPlayer()->GetLootGUID())) { GameObject* pGO = GetPlayer()->GetMap()->GetGameObject(lootguid); if (!pGO) return; loot = &pGO->loot; } if (!loot) return; if (slotid >= loot->items.size() + loot->quest_items.size()) { sLog->outDebug(LOG_FILTER_LOOT, "MasterLootItem: Player %s might be using a hack! (slot %d, size %lu)", GetPlayer()->GetName(), slotid, (unsigned long)loot->items.size()); return; } LootItem& item = slotid >= loot->items.size() ? loot->quest_items[slotid - loot->items.size()] : loot->items[slotid]; ItemPosCountVec dest; InventoryResult msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.itemid, item.count); if (item.follow_loot_rules && !item.AllowedForPlayer(target)) msg = EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; if (msg != EQUIP_ERR_OK) { target->SendEquipError(msg, NULL, NULL, item.itemid); // send duplicate of error massage to master looter _player->SendEquipError(msg, NULL, NULL, item.itemid); return; } // list of players allowed to receive this item in trade AllowedLooterSet looters = item.GetAllowedLooters(); sScriptMgr->OnPlayerLootItem(target, item.itemid, dest); // not move item from loot to target inventory Item* newitem = target->StoreNewItem(dest, item.itemid, true, item.randomPropertyId, looters); target->SendNewItem(newitem, uint32(item.count), false, false, true); target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item.itemid, item.count); target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type, item.count); target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item.itemid, item.count); // mark as looted item.count=0; item.is_looted=true; loot->NotifyItemRemoved(slotid); --loot->unlootedCount; }
void WorldSession::HandleLootMasterGiveOpcode(WorldPacket& recv_data) { uint8 slotid; ObjectGuid lootguid; ObjectGuid target_playerguid; recv_data >> lootguid >> slotid >> target_playerguid; if (!_player->GetGroup() || _player->GetGroup()->GetLooterGuid() != _player->GetObjectGuid()) { _player->SendLootRelease(GetPlayer()->GetLootGuid()); return; } Player* target = ObjectAccessor::FindPlayer(target_playerguid); if (!target) return; DEBUG_LOG("WorldSession::HandleLootMasterGiveOpcode (CMSG_LOOT_MASTER_GIVE, 0x02A3) Target = %s [%s].", target_playerguid.GetString().c_str(), target->GetName()); if (_player->GetLootGuid() != lootguid) return; Loot* pLoot = NULL; if (lootguid.IsCreatureOrVehicle()) { Creature* pCreature = GetPlayer()->GetMap()->GetCreature(lootguid); if (!pCreature) return; pLoot = &pCreature->loot; } else if (lootguid.IsGameObject()) { GameObject* pGO = GetPlayer()->GetMap()->GetGameObject(lootguid); if (!pGO) return; pLoot = &pGO->loot; } else return; if (slotid > pLoot->items.size()) { DEBUG_LOG("AutoLootItem: Player %s might be using a hack! (slot %d, size " SIZEFMTD ")", GetPlayer()->GetName(), slotid, pLoot->items.size()); return; } LootItem& item = pLoot->items[slotid]; ItemPosCountVec dest; InventoryResult msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.itemid, item.count); if (msg != EQUIP_ERR_OK) { target->SendEquipError(msg, NULL, NULL, item.itemid); // send duplicate of error massage to master looter _player->SendEquipError(msg, NULL, NULL, item.itemid); return; } // now move item from loot to target inventory Item* newitem = target->StoreNewItem(dest, item.itemid, true, item.randomPropertyId); target->SendNewItem(newitem, uint32(item.count), false, false, true); target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item.itemid, item.count); target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, pLoot->loot_type, item.count); target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item.itemid, item.count); // mark as looted item.count = 0; item.is_looted = true; pLoot->NotifyItemRemoved(slotid); --pLoot->unlootedCount; }
void WorldSession::HandleSendMail(WorldPacket & recv_data) { uint64 mailbox, unk3, money, COD; std::string receiver, subject, body; uint32 unk1, unk2; uint8 unk4; recv_data >> mailbox; recv_data >> receiver; recv_data >> subject; recv_data >> body; recv_data >> unk1; // stationery? recv_data >> unk2; // 0x00000000 uint8 items_count; recv_data >> items_count; // attached items count if (items_count > MAX_MAIL_ITEMS) // client limit { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS); recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam return; } uint64 itemGUIDs[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { recv_data.read_skip<uint8>(); // item slot in mail, not used recv_data >> itemGUIDs[i]; } recv_data >> money >> COD; // money and cod recv_data >> unk3; // const 0 recv_data >> unk4; // const 0 // packet read complete, now do check if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; if (receiver.empty()) return; Player* pl = _player; if (pl->getLevel() < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetSkyFireString(LANG_MAIL_SENDER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } uint64 rc = 0; if (normalizePlayerName(receiver)) rc = sObjectMgr->GetPlayerGUIDByName(receiver); if (!rc) { sLog->outDetail("Player %u is sending mail to %s (GUID: not existed!) with subject %s and body %s includes %u items, "UI64FMTD" copper and "UI64FMTD" COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND); return; } sLog->outDetail("Player %u is sending mail to %s (GUID: %u) with subject %s and body %s includes %u items, "UI64FMTD" copper and "UI64FMTD" COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); if (pl->GetGUID() == rc) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF); return; } uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client uint64 reqmoney = cost + money; if (!pl->HasEnoughMoney(uint32(reqmoney))) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player *receive = sObjectMgr->GetPlayer(rc); uint32 rc_team = 0; uint8 mails_count = 0; //do not allow to send to one player more than 100 mails uint8 receiveLevel = 0; if (receive) { rc_team = receive->GetTeam(); mails_count = receive->GetMailSize(); receiveLevel = receive->getLevel(); } else { rc_team = sObjectMgr->GetPlayerTeamByGUID(rc); if (QueryResult result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc))) { Field *fields = result->Fetch(); mails_count = fields[0].GetUInt32(); } if (QueryResult result = CharacterDatabase.PQuery("SELECT level FROM characters WHERE guid = '%u'", GUID_LOPART(rc))) { Field *fields = result->Fetch(); receiveLevel = fields[0].GetUInt8(); } } //do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mails_count > 100) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // test the receiver's Faction... or all items are account bound bool accountBound = items_count ? true : false; for (uint8 i = 0; i < items_count; ++i) { Item* item = pl->GetItemByGuid(itemGUIDs[i]); if (item) { ItemPrototype const* itemProto = item->GetProto(); if (!itemProto || !(itemProto->Flags & ITEM_PROTO_FLAG_BIND_TO_ACCOUNT)) { accountBound = false; break; } } } if (!accountBound && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } if (receiveLevel < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetSkyFireString(LANG_MAIL_RECEIVER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : sObjectMgr->GetPlayerAccountIdByGUID(rc); Item* items[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { if (!itemGUIDs[i]) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } Item* item = pl->GetItemByGuid(itemGUIDs[i]); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if (!item) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded(true)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (item->IsBoundAccountWide() && item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS); return; } if (item->GetProto()->Flags & ITEM_PROTO_FLAG_CONJURED || item->GetUInt32Value(ITEM_FIELD_DURATION)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } if (item->IsBag() && !((Bag*)item)->IsEmpty()) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS); return; } items[i] = item; } pl->SendMailResult(0, MAIL_SEND, MAIL_OK); pl->ModifyMoney(-int32(reqmoney)); pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; MailDraft draft(subject, body); SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (items_count > 0 || money > 0) { if (items_count > 0) { for (uint8 i = 0; i < items_count; ++i) { Item* item = items[i]; if (GetSecurity() > SEC_PLAYER && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE)) { sLog->outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), item->GetProto()->Name1, item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account); } item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable pl->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true); item->DeleteFromInventoryDB(trans); // deletes item from character's inventory item->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone // owner in data will set at mail receive and item extracting PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SET_ITEM_OWNER); stmt->setUInt32(0, GUID_LOPART(rc)); stmt->setUInt32(1, item->GetGUIDLow()); trans->Append(stmt); draft.AddItem(item); } // if item send to character at another account, then apply item delivery delay needItemDelay = pl->GetSession()->GetAccountId() != rc_account; } if (money > 0 && GetSecurity() > SEC_PLAYER && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE)) { sLog->outCommand(GetAccountId(), "GM %s (Account: %u) mail money: "UI64FMTD" to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account); } } // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint32 deliver_delay = needItemDelay ? sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; // will delete item or place to receiver mail list draft .AddMoney(money) .AddCOD(COD) .SendMailTo(trans, MailReceiver(receive, GUID_LOPART(rc)), MailSender(pl), body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); pl->SaveInventoryAndGoldToDB(trans); CharacterDatabase.CommitTransaction(trans); }
void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recv_data*/) { sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT_MONEY"); Player* player = GetPlayer(); uint64 guid = player->GetLootGUID(); if (!guid) return; Loot* loot = NULL; bool shareMoney = true; switch (GUID_HIPART(guid)) { case HIGHGUID_GAMEOBJECT: { GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid); // do not check distance for GO if player is the owner of it (ex. fishing bobber) if (go && ((go->GetOwnerGUID() == player->GetGUID() || go->IsWithinDistInMap(player, INTERACTION_DISTANCE)))) loot = &go->loot; break; } case HIGHGUID_CORPSE: // remove insignia ONLY in BG { Corpse* bones = ObjectAccessor::GetCorpse(*player, guid); if (bones && bones->IsWithinDistInMap(player, INTERACTION_DISTANCE)) { loot = &bones->loot; shareMoney = false; } break; } case HIGHGUID_ITEM: { if (Item* item = player->GetItemByGuid(guid)) { loot = &item->loot; shareMoney = false; } break; } case HIGHGUID_UNIT: case HIGHGUID_VEHICLE: { Creature* creature = player->GetMap()->GetCreature(guid); bool lootAllowed = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed); if (lootAllowed && creature->IsWithinDistInMap(player, INTERACTION_DISTANCE)) { loot = &creature->loot; if (creature->isAlive()) shareMoney = false; } break; } default: return; // unlootable type } if (loot) { loot->NotifyMoneyRemoved(); if (shareMoney && player->GetGroup()) //item, pickpocket and players can be looted only single player { Group* group = player->GetGroup(); std::vector<Player*> playersNear; for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* member = itr->getSource(); if (!member) continue; if (player->IsWithinDistInMap(member, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) playersNear.push_back(member); } uint32 goldPerPlayer = uint32((loot->gold) / (playersNear.size())); for (std::vector<Player*>::const_iterator i = playersNear.begin(); i != playersNear.end(); ++i) { (*i)->ModifyMoney(goldPerPlayer); (*i)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, goldPerPlayer); WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4); data << uint32(goldPerPlayer); data << uint8(playersNear.size() > 1 ? 0 : 1); // Controls the text displayed in chat. 0 is "Your share is..." and 1 is "You loot..." (*i)->GetSession()->SendPacket(&data); } } else { player->ModifyMoney(loot->gold); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, loot->gold); WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4); data << uint32(loot->gold); data << uint8(1); SendPacket(&data); } loot->gold = 0; } }
void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) { uint32 opcode = recv_data.GetOpcode(); sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode); if(GetPlayer()->GetDontMove()) return; /* extract packet */ MovementInfo movementInfo; ReadMovementInfo(recv_data, &movementInfo); /*----------------*/ if(recv_data.size() != recv_data.rpos()) { sLog.outError("MovementHandler: player %s (guid %d, account %u) sent a packet (opcode %u) that is %u bytes larger than it should be. Kicked as cheater.", _player->GetName(), _player->GetGUIDLow(), _player->GetSession()->GetAccountId(), recv_data.GetOpcode(), recv_data.size() - recv_data.rpos()); KickPlayer(); return; } if (!MaNGOS::IsValidMapCoord(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o)) return; /* handle special cases */ if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if( movementInfo.t_x > 50 || movementInfo.t_y > 50 || movementInfo.t_z > 50 ) return; if( !MaNGOS::IsValidMapCoord(movementInfo.x+movementInfo.t_x, movementInfo.y+movementInfo.t_y, movementInfo.z+movementInfo.t_z, movementInfo.o+movementInfo.t_o) ) return; // if we boarded a transport, add us to it if (!GetPlayer()->m_transport) { // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { GetPlayer()->m_transport = (*iter); (*iter)->AddPassenger(GetPlayer()); break; } } } } else if (GetPlayer()->m_transport) // if we were on a transport, leave { GetPlayer()->m_transport->RemovePassenger(GetPlayer()); GetPlayer()->m_transport = NULL; movementInfo.t_x = 0.0f; movementInfo.t_y = 0.0f; movementInfo.t_z = 0.0f; movementInfo.t_o = 0.0f; movementInfo.t_time = 0; movementInfo.t_seat = -1; } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && !GetPlayer()->isInFlight()) { // calculate total z distance of the fall float z_diff = GetPlayer()->m_lastFallZ - movementInfo.z; sLog.outDebug("zDiff = %f", z_diff); Player *target = GetPlayer(); //Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored // 14.57 can be calculated by resolving damageperc formular below to 0 if (z_diff >= 14.57f && !target->isDead() && !target->isGameMaster() && !target->HasAuraType(SPELL_AURA_HOVER) && !target->HasAuraType(SPELL_AURA_FEATHER_FALL) && !target->HasAuraType(SPELL_AURA_FLY) && !target->IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL) ) { //Safe fall, fall height reduction int32 safe_fall = target->GetTotalAuraModifier(SPELL_AURA_SAFE_FALL); float damageperc = 0.018f*(z_diff-safe_fall)-0.2426f; if(damageperc >0 ) { uint32 damage = (uint32)(damageperc * target->GetMaxHealth()*sWorld.getRate(RATE_DAMAGE_FALL)); float height = movementInfo.z; target->UpdateGroundPositionZ(movementInfo.x,movementInfo.y,height); if (damage > 0) { //Prevent fall damage from being more than the player maximum health if (damage > target->GetMaxHealth()) damage = target->GetMaxHealth(); // Gust of Wind if (target->GetDummyAura(43621)) damage = target->GetMaxHealth()/2; target->EnvironmentalDamage(target->GetGUID(), DAMAGE_FALL, damage); // recheck alive, might have died of EnvironmentalDamage if (target->isAlive()) target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING, uint32(z_diff*100)); } //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction DEBUG_LOG("FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d" , movementInfo.z, height, target->GetPositionZ(), movementInfo.fallTime, height, damage, safe_fall); } } } if(((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != GetPlayer()->IsInWater()) { // now client not include swimming flag in case jumping under water GetPlayer()->SetInWater( !GetPlayer()->IsInWater() || GetPlayer()->GetBaseMap()->IsUnderWater(movementInfo.x, movementInfo.y, movementInfo.z) ); } /*----------------------*/ /* process position-change */ Unit *mover = _player->m_mover; recv_data.put<uint32>(6, getMSTime()); // fix time, offset flags(4) + unk(2) WorldPacket data(recv_data.GetOpcode(), (mover->GetPackGUID().size()+recv_data.size())); data.append(_player->m_mover->GetPackGUID()); // use mover guid data.append(recv_data.contents(), recv_data.size()); GetPlayer()->SendMessageToSet(&data, false); if(!_player->GetCharmGUID()) // nothing is charmed { _player->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); _player->m_movementInfo = movementInfo; _player->SetUnitMovementFlags(movementInfo.flags); } else { if(mover->GetTypeId() != TYPEID_PLAYER) // unit, creature, pet, vehicle... { if(Map *map = mover->GetMap()) map->CreatureRelocation((Creature*)mover, movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); mover->SetUnitMovementFlags(movementInfo.flags); } else // player { ((Player*)mover)->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); ((Player*)mover)->m_movementInfo = movementInfo; ((Player*)mover)->SetUnitMovementFlags(movementInfo.flags); } } if (GetPlayer()->m_lastFallTime >= movementInfo.fallTime || GetPlayer()->m_lastFallZ <=movementInfo.z || recv_data.GetOpcode() == MSG_MOVE_FALL_LAND) GetPlayer()->SetFallInformation(movementInfo.fallTime, movementInfo.z); if(GetPlayer()->isMovingOrTurning()) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); if(movementInfo.z < -500.0f) { if(GetPlayer()->InBattleGround() && GetPlayer()->GetBattleGround() && GetPlayer()->GetBattleGround()->HandlePlayerUnderMap(_player)) { // do nothing, the handle already did if returned true } else { // NOTE: this is actually called many times while falling // even after the player has been teleported away // TODO: discard movement packets after the player is rooted if(GetPlayer()->isAlive()) { GetPlayer()->EnvironmentalDamage(GetPlayer()->GetGUID(),DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // change the death state to CORPSE to prevent the death timer from // starting in the next player update GetPlayer()->KillPlayer(); GetPlayer()->BuildPlayerRepop(); } // cancel the death timer here if started GetPlayer()->RepopAtGraveyard(); } } }
void BattleGroundSA::Update(uint32 diff) { BattleGround::Update(diff); if (GetStatus() == STATUS_WAIT_JOIN && !shipsStarted) if (Phase == SA_ROUND_ONE) // Round one not started yet { if (shipsTimer <= diff) StartShips(); else shipsTimer -= diff; } if (GetStatus() == STATUS_IN_PROGRESS) // Battleground already in progress { if (Round_timer >= BG_SA_ROUNDLENGTH) { if (Phase == SA_ROUND_ONE) // Timeout of first round { PlaySoundToAll(BG_SA_SOUND_GYD_VICTORY); SendMessageToAll(defender == ALLIANCE ? LANG_BG_SA_ALLIANCE_TIMEOUT_END_1ROUND : LANG_BG_SA_HORDE_TIMEOUT_END_1ROUND, CHAT_MSG_BG_SYSTEM_NEUTRAL, NULL); SendWarningToAll(LANG_BG_SA_END_1ROUND); RoundScores[0].winner = GetDefender(); RoundScores[0].time = BG_SA_ROUNDLENGTH; for (BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) { Player* plr = sObjectMgr.GetPlayer(itr->first); if (plr) plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, BG_SA_END_OF_ROUND); } ResetBattle(0, defender); } else // Timeout of second round { SendMessageToAll(defender == ALLIANCE ? LANG_BG_SA_ALLIANCE_TIMEOUT_END_2ROUND : LANG_BG_SA_HORDE_TIMEOUT_END_2ROUND, CHAT_MSG_BG_SYSTEM_NEUTRAL, NULL); RoundScores[1].winner = GetDefender(); for (BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) { Player* plr = sObjectMgr.GetPlayer(itr->first); if (plr) plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, BG_SA_END_OF_ROUND); } if (RoundScores[0].winner == GetDefender()) EndBattleGround(GetDefender()); else EndBattleGround(TEAM_NONE); return; } } else Round_timer += diff; for (int gyd = 0; gyd < BG_SA_GRY_MAX; ++gyd) { // 3 sec delay to spawn new banner instead previous despawned one if (m_BannerTimers[gyd].timer) { if (m_BannerTimers[gyd].timer > diff) m_BannerTimers[gyd].timer -= diff; else { m_BannerTimers[gyd].timer = 0; _CreateBanner(gyd, m_BannerTimers[gyd].type, m_BannerTimers[gyd].teamIndex, false); } } } UpdateTimer(); } if (GetStatus() == STATUS_WAIT_JOIN && Phase == SA_ROUND_TWO) // Round two, not yet started { if (!shipsStarted) { if (shipsTimer <= diff) { SendMessageToAll(LANG_BG_SA_START_ONE_MINUTE, CHAT_MSG_BG_SYSTEM_NEUTRAL, NULL); StartShips(); } else shipsTimer -= diff; } if (TimeST2Round < diff) { Phase = SA_ROUND_TWO; SpawnEvent(SA_EVENT_ADD_NPC, 0, true); SpawnEvent(SA_EVENT_ADD_BOMB_B, (GetDefender() == ALLIANCE ? BG_SA_GRAVE_STATUS_HORDE_OCCUPIED : BG_SA_GRAVE_STATUS_ALLY_OCCUPIED), true); ToggleTimer(); SetStatus(STATUS_IN_PROGRESS); // Start round two PlaySoundToAll(SOUND_BG_START); SendWarningToAll(LANG_BG_SA_HAS_BEGUN); StartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, BG_SA_EVENT_START_BATTLE_2); for (BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) { Player* plr = sObjectMgr.GetPlayer(itr->first); if (plr) plr->RemoveAurasDueToSpell(SPELL_PREPARATION); } } else TimeST2Round -= diff; } }
//does not clear ram void AuctionHouseMgr::SendAuctionWonMail( AuctionEntry *auction ) { Item *pItem = GetAItem(auction->item_guidlow); if(!pItem) return; uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER); Player *bidder = sObjectMgr.GetPlayer(bidder_guid); uint32 bidder_accId = 0; // data for gm.log if( sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE) ) { uint32 bidder_security = 0; std::string bidder_name; if (bidder) { bidder_accId = bidder->GetSession()->GetAccountId(); bidder_security = bidder->GetSession()->GetSecurity(); bidder_name = bidder->GetName(); } else { bidder_accId = sObjectMgr.GetPlayerAccountIdByGUID(bidder_guid); bidder_security = sAccountMgr.GetSecurity(bidder_accId); if(bidder_security > SEC_PLAYER ) // not do redundant DB requests { if(!sObjectMgr.GetPlayerNameByGUID(bidder_guid,bidder_name)) bidder_name = sObjectMgr.GetMangosStringForDBCLocale(LANG_UNKNOWN); } } if( bidder_security > SEC_PLAYER ) { std::string owner_name; if(!sObjectMgr.GetPlayerNameByGUID(auction->owner,owner_name)) owner_name = sObjectMgr.GetMangosStringForDBCLocale(LANG_UNKNOWN); uint32 owner_accid = sObjectMgr.GetPlayerAccountIdByGUID(auction->owner); sLog.outCommand(bidder_accId,"GM %s (Account: %u) won item in auction: %s (Entry: %u Count: %u) and pay money: %u. Original owner %s (Account: %u)", bidder_name.c_str(),bidder_accId,pItem->GetProto()->Name1,pItem->GetEntry(),pItem->GetCount(),auction->bid,owner_name.c_str(),owner_accid); } } else if(!bidder) bidder_accId = sObjectMgr.GetPlayerAccountIdByGUID(bidder_guid); // receiver exist if(bidder || bidder_accId) { std::ostringstream msgAuctionWonSubject; msgAuctionWonSubject << auction->item_template << ":0:" << AUCTION_WON; std::ostringstream msgAuctionWonBody; msgAuctionWonBody.width(16); msgAuctionWonBody << std::right << std::hex << auction->owner; msgAuctionWonBody << std::dec << ":" << auction->bid << ":" << auction->buyout; sLog.outDebug( "AuctionWon body string : %s", msgAuctionWonBody.str().c_str() ); // set owner to bidder (to prevent delete item with sender char deleting) // owner in `data` will set at mail receive and item extracting CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'",auction->bidder,pItem->GetGUIDLow()); CharacterDatabase.CommitTransaction(); if (bidder) { bidder->GetSession()->SendAuctionBidderNotification( auction->GetHouseId(), auction->Id, bidder_guid, 0, 0, auction->item_template); // FIXME: for offline player need also bidder->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS, 1); } else RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !! // will delete item or place to receiver mail list MailDraft(msgAuctionWonSubject.str(), msgAuctionWonBody.str()) .AddItem(pItem) .SendMailTo(MailReceiver(bidder,auction->bidder), auction, MAIL_CHECK_MASK_AUCTION); } // receiver not exist else { CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", pItem->GetGUIDLow()); RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !! delete pItem; } }
// this function is called when client bids or buys out auction void WorldSession::HandleAuctionPlaceBid(WorldPacket& recv_data) { DEBUG_LOG("WORLD: HandleAuctionPlaceBid"); ObjectGuid auctioneerGuid; uint32 auctionId; uint32 price; recv_data >> auctioneerGuid; recv_data >> auctionId >> price; if (!auctionId || !price) return; // check for cheaters AuctionHouseEntry const* auctionHouseEntry = GetCheckedAuctionHouseForAuctioneer(auctioneerGuid); if (!auctionHouseEntry) return; // always return pointer AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); AuctionEntry* auction = auctionHouse->GetAuction(auctionId); Player* pl = GetPlayer(); if (!auction || auction->owner == pl->GetGUIDLow()) { // you cannot bid your own auction: SendAuctionCommandResult(nullptr, AUCTION_BID_PLACED, AUCTION_ERR_BID_OWN); return; } ObjectGuid ownerGuid = ObjectGuid(HIGHGUID_PLAYER, auction->owner); // impossible have online own another character (use this for speedup check in case online owner) Player* auction_owner = sObjectMgr.GetPlayer(ownerGuid); if (!auction_owner && sObjectMgr.GetPlayerAccountIdByGUID(ownerGuid) == pl->GetSession()->GetAccountId()) { // you cannot bid your another character auction: SendAuctionCommandResult(nullptr, AUCTION_BID_PLACED, AUCTION_ERR_BID_OWN); return; } // cheating or client lags if (price <= auction->bid) { // client test but possible in result lags SendAuctionCommandResult(auction, AUCTION_BID_PLACED, AUCTION_ERR_HIGHER_BID); return; } // price too low for next bid if not buyout if ((price < auction->buyout || auction->buyout == 0) && price < auction->bid + auction->GetAuctionOutBid()) { // client test but possible in result lags SendAuctionCommandResult(auction, AUCTION_BID_PLACED, AUCTION_ERR_BID_INCREMENT); return; } if (price > pl->GetMoney()) { // you don't have enough money!, client tests! // SendAuctionCommandResult(auction->auctionId, AUCTION_ERR_INVENTORY, EQUIP_ERR_NOT_ENOUGH_MONEY); return; } // cheating if (price < auction->startbid) return; SendAuctionCommandResult(auction, AUCTION_BID_PLACED, AUCTION_OK); if (auction->UpdateBid(price, pl)) pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price); else pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout); }
bool ChatHandler::HandleQuestFinishCommand(const char * args, WorldSession * m_session) { if(!*args) return false; Player *plr = getSelectedChar(m_session, true); if(!plr) { plr = m_session->GetPlayer(); SystemMessage(m_session, "Auto-targeting self."); } uint32 quest_id = atol(args); // reward_slot is for when quest has choice of rewards (0 is the first choice, 1 is the second choice, ...) // reward_slot will default to 0 if none is specified uint32 reward_slot; if(quest_id== 0) { quest_id = GetQuestIDFromLink(args); if(quest_id== 0) return false; if(strstr(args,"|r")) { reward_slot = atol(strstr(args,"|r")+2); } else { reward_slot = 0; } } else if(strchr(args,' ')) { reward_slot = atol(strchr(args,' ')+1); } else { reward_slot = 0; } // currently Quest::reward_choiceitem declaration is // uint32 reward_choiceitem[6]; // so reward_slot must be 0 to 5 if(reward_slot > 5) { reward_slot = 0; } std::string recout = "|cff00ff00"; Quest * qst = QuestStorage.LookupEntry(quest_id); if(qst) { if (plr->HasFinishedQuest(quest_id)) recout += "Player has already completed that quest.\n\n"; else { QuestLogEntry * IsPlrOnQuest = plr->GetQuestLogForEntry(quest_id); if (IsPlrOnQuest) { uint32 giver_id = 0; std::string my_query = ""; my_query = "SELECT id FROM creature_quest_starter WHERE quest = " + MyConvertIntToString(quest_id); QueryResult *creatureResult = WorldDatabase.Query(my_query.c_str()); if(creatureResult) { Field *creatureFields = creatureResult->Fetch(); giver_id = creatureFields[0].GetUInt32(); delete creatureResult; } else { my_query = "SELECT id FROM gameobject_quest_starter WHERE quest = " + MyConvertIntToString(quest_id); QueryResult *objectResult = WorldDatabase.Query(my_query.c_str()); if(objectResult) { Field *objectFields = objectResult->Fetch(); giver_id = objectFields[0].GetUInt32(); delete objectResult; } } if(giver_id == 0) SystemMessage(m_session, "Unable to find quest giver creature or object."); else { // I need some way to get the guid without targeting the creature or looking through all the spawns... Object *quest_giver = 0; for(uint32 guid=1; guid < plr->GetMapMgr()->CreatureStorage.size(); guid++) { Creature *pCreature = plr->GetMapMgr()->GetCreature(GET_LOWGUID_PART(guid)); if(pCreature) { if(pCreature->GetEntry() == giver_id) //found creature { quest_giver = (Object*)pCreature; guid = plr->GetMapMgr()->CreatureStorage.size(); } } } if(quest_giver) { GreenSystemMessage(m_session, "Found a quest_giver creature."); sQuestMgr.OnActivateQuestGiver(quest_giver, plr); sQuestMgr.GiveQuestRewardReputation(plr, qst, quest_giver); } else RedSystemMessage(m_session, "Unable to find quest_giver object."); } IsPlrOnQuest->Finish(); recout += "Player was on that quest, but has now completed it."; } else { recout += "The quest has now been completed for that player."; } sGMLog.writefromsession( m_session, "completed quest %u [%s] for player %s", quest_id, qst->title, plr->GetName() ); sQuestMgr.BuildQuestComplete(plr, qst); plr->AddToFinishedQuests(quest_id); // Quest Rewards : Copied from QuestMgr::OnQuestFinished() // Reputation reward for(int z = 0; z < 6; z++) { if( qst->reward_repfaction[z] ) { int32 amt = 0; uint32 fact = qst->reward_repfaction[z]; if( qst->reward_repvalue[z] ) { amt = qst->reward_repvalue[z]; } if( qst->reward_replimit && (plr->GetStanding(fact) >= (int32)qst->reward_replimit) ) { continue; } amt = float2int32( float( amt ) * sWorld.getRate( RATE_QUESTREPUTATION ) ); plr->ModStanding( fact, amt ); } } // Static Item reward for(uint32 i = 0; i < 4; ++i) { if(qst->reward_item[i]) { ItemPrototype *proto = ItemPrototypeStorage.LookupEntry(qst->reward_item[i]); if(!proto) { sLog.outError("Invalid item prototype in quest reward! ID %d, quest %d", qst->reward_item[i], qst->id); } else { Item *add; SlotResult slotresult; add = plr->GetItemInterface()->FindItemLessMax(qst->reward_item[i], qst->reward_itemcount[i], false); if (!add) { slotresult = plr->GetItemInterface()->FindFreeInventorySlot(proto); if(!slotresult.Result) { plr->GetItemInterface()->BuildInventoryChangeError(NULL, NULL, INV_ERR_INVENTORY_FULL); } else { Item *itm = objmgr.CreateItem(qst->reward_item[i], plr); if( itm ) { itm->SetStackCount( uint32(qst->reward_itemcount[i])); if( !plr->GetItemInterface()->SafeAddItem(itm,slotresult.ContainerSlot, slotresult.Slot) ) { itm->DeleteMe(); } } } } else { add->SetStackCount( add->GetStackCount() + qst->reward_itemcount[i]); add->m_isDirty = true; } } } } // Choice Rewards -- Defaulting to choice 0 for ".quest complete" command if(qst->reward_choiceitem[reward_slot]) { ItemPrototype *proto = ItemPrototypeStorage.LookupEntry(qst->reward_choiceitem[reward_slot]); if(!proto) { sLog.outError("Invalid item prototype in quest reward! ID %d, quest %d", qst->reward_choiceitem[reward_slot], qst->id); } else { Item *add; SlotResult slotresult; add = plr->GetItemInterface()->FindItemLessMax(qst->reward_choiceitem[reward_slot], qst->reward_choiceitemcount[reward_slot], false); if (!add) { slotresult = plr->GetItemInterface()->FindFreeInventorySlot(proto); if(!slotresult.Result) { plr->GetItemInterface()->BuildInventoryChangeError(NULL, NULL, INV_ERR_INVENTORY_FULL); } else { Item *itm = objmgr.CreateItem(qst->reward_choiceitem[reward_slot], plr); if( itm ) { itm->SetStackCount( uint32(qst->reward_choiceitemcount[reward_slot])); if( !plr->GetItemInterface()->SafeAddItem(itm,slotresult.ContainerSlot, slotresult.Slot) ) { itm->DeleteMe(); } } } } else { add->SetStackCount( add->GetStackCount() + qst->reward_choiceitemcount[reward_slot]); add->m_isDirty = true; } } } // if daily then append to finished dailies if ( qst->is_repeatable == arcemu_QUEST_REPEATABLE_DAILY ) plr->PushToFinishedDailies( qst->id ); // Remove quests that are listed to be removed on quest complete. set<uint32>::iterator iter = qst->remove_quest_list.begin(); for(; iter != qst->remove_quest_list.end(); ++iter) { if( !plr->HasFinishedQuest( (*iter ) )) plr->AddToFinishedQuests( (*iter ) ); } #ifdef ENABLE_ACHIEVEMENTS plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT, 1, 0, 0); if( qst->reward_money > 0 ) { // Money reward // Check they don't have more than the max gold if( sWorld.GoldCapEnabled && (plr->GetGold() + qst->reward_money) <= sWorld.GoldLimit ) { plr->ModGold( qst->reward_money ); } plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_QUEST_REWARD_GOLD, qst->reward_money, 0, 0); } plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE, qst->zone_id, 0, 0); plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST, qst->id, 0, 0); #endif } } else { recout += "Quest Id ["; recout += args; recout += "] was not found and unable to add it to the player's quest log."; } recout += "\n\n"; SendMultilineMessage(m_session, recout.c_str()); return true; }
void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ ) { DEBUG_LOG("WORLD: CMSG_LOOT_MONEY"); Player *player = GetPlayer(); ObjectGuid guid = player->GetLootGUID(); if (guid.IsEmpty()) return; Loot *pLoot = NULL; Item* pItem = NULL; switch(guid.GetHigh()) { case HIGHGUID_GAMEOBJECT: { GameObject *pGameObject = GetPlayer()->GetMap()->GetGameObject(guid); // not check distance for GO in case owned GO (fishing bobber case, for example) if( pGameObject && (pGameObject->GetOwnerGuid() == _player->GetObjectGuid() || pGameObject->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) ) pLoot = &pGameObject->loot; break; } case HIGHGUID_CORPSE: // remove insignia ONLY in BG { Corpse *bones = _player->GetMap()->GetCorpse(guid); if (bones && bones->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) pLoot = &bones->loot; break; } case HIGHGUID_ITEM: { pItem = GetPlayer()->GetItemByGuid(guid); if (!pItem || !pItem->HasGeneratedLoot()) return; pLoot = &pItem->loot; break; } case HIGHGUID_UNIT: case HIGHGUID_VEHICLE: { Creature* pCreature = GetPlayer()->GetMap()->GetCreature(guid); bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed); if ( ok_loot && pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) pLoot = &pCreature->loot ; break; } default: return; // unlootable type } if (pLoot) { if (!guid.IsItem() && player->GetGroup()) //item can be looted only single player { Group *group = player->GetGroup(); std::vector<Player*> playersNear; for(GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* playerGroup = itr->getSource(); if(!playerGroup) continue; if (player->IsWithinDistInMap(playerGroup,sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE),false)) playersNear.push_back(playerGroup); } uint32 money_per_player = uint32((pLoot->gold)/(playersNear.size())); for (std::vector<Player*>::const_iterator i = playersNear.begin(); i != playersNear.end(); ++i) { (*i)->ModifyMoney( money_per_player ); (*i)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, money_per_player); //Offset surely incorrect, but works WorldPacket data( SMSG_LOOT_MONEY_NOTIFY, 4 ); data << uint32(money_per_player); (*i)->GetSession()->SendPacket( &data ); } } else { player->ModifyMoney( pLoot->gold ); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, pLoot->gold); } pLoot->gold = 0; if (pItem) pItem->SetLootState(ITEM_LOOT_CHANGED); pLoot->NotifyMoneyRemoved(); } }
//does not clear ram void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction, SQLTransaction& trans) { Item *pItem = GetAItem(auction->item_guidlow); if (!pItem) return; uint32 bidder_accId = 0; uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER); Player *bidder = sObjectMgr->GetPlayer(bidder_guid); // data for gm.log if (sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE)) { uint32 bidder_security = 0; std::string bidder_name; if (bidder) { bidder_accId = bidder->GetSession()->GetAccountId(); bidder_security = bidder->GetSession()->GetSecurity(); bidder_name = bidder->GetName(); } else { bidder_accId = sObjectMgr->GetPlayerAccountIdByGUID(bidder_guid); bidder_security = sAccountMgr->GetSecurity(bidder_accId, realmID); if (bidder_security > SEC_PLAYER) // not do redundant DB requests { if (!sObjectMgr->GetPlayerNameByGUID(bidder_guid,bidder_name)) bidder_name = sObjectMgr->GetTrinityStringForDBCLocale(LANG_UNKNOWN); } } if (bidder_security > SEC_PLAYER) { std::string owner_name; if (!sObjectMgr->GetPlayerNameByGUID(auction->owner,owner_name)) owner_name = sObjectMgr->GetTrinityStringForDBCLocale(LANG_UNKNOWN); uint32 owner_accid = sObjectMgr->GetPlayerAccountIdByGUID(auction->owner); sLog->outCommand(bidder_accId,"GM %s (Account: %u) won item in auction: %s (Entry: %u Count: %u) and pay money: %u. Original owner %s (Account: %u)", bidder_name.c_str(),bidder_accId,pItem->GetProto()->Name1,pItem->GetEntry(),pItem->GetCount(),auction->bid,owner_name.c_str(),owner_accid); } } // receiver exist if (bidder || bidder_accId) { std::ostringstream msgAuctionWonSubject; msgAuctionWonSubject << auction->item_template << ":0:" << AUCTION_WON; std::ostringstream msgAuctionWonBody; msgAuctionWonBody.width(16); msgAuctionWonBody << std::right << std::hex << auction->owner; msgAuctionWonBody << std::dec << ":" << auction->bid << ":" << auction->buyout; sLog->outDebug(LOG_FILTER_AUCTIONHOUSE, "AuctionWon body string : %s", msgAuctionWonBody.str().c_str()); // set owner to bidder (to prevent delete item with sender char deleting) // owner in `data` will set at mail receive and item extracting PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SET_ITEM_OWNER); stmt->setUInt32(0, auction->bidder); stmt->setUInt32(1, pItem->GetGUIDLow()); trans->Append(stmt); if (bidder) { bidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, bidder_guid, 0, 0, auction->item_template); // FIXME: for offline player need also bidder->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS, 1); } MailDraft(msgAuctionWonSubject.str(), msgAuctionWonBody.str()) .AddItem(pItem) .SendMailTo(trans, MailReceiver(bidder,auction->bidder), auction, MAIL_CHECK_MASK_COPIED); } }
void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data ) { DEBUG_LOG("WORLD: CMSG_AUTOSTORE_LOOT_ITEM"); Player *player = GetPlayer(); ObjectGuid lguid = player->GetLootGUID(); Loot *loot; uint8 lootSlot; Item* pItem = NULL; recv_data >> lootSlot; switch( lguid.GetHigh()) { case HIGHGUID_GAMEOBJECT: { GameObject *go = player->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGuid() != _player->GetObjectGuid() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))) { player->SendLootRelease(lguid); return; } loot = &go->loot; break; } case HIGHGUID_ITEM: { pItem = player->GetItemByGuid( lguid ); if (!pItem || !pItem->HasGeneratedLoot()) { player->SendLootRelease(lguid); return; } loot = &pItem->loot; break; } case HIGHGUID_CORPSE: { Corpse *bones = player->GetMap()->GetCorpse(lguid); if (!bones) { player->SendLootRelease(lguid); return; } loot = &bones->loot; break; } case HIGHGUID_UNIT: case HIGHGUID_VEHICLE: { Creature* pCreature = GetPlayer()->GetMap()->GetCreature(lguid); bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed); if( !ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) { player->SendLootRelease(lguid); return; } loot = &pCreature->loot; break; } default: { sLog.outError("%s is unsupported for looting.",lguid.GetString().c_str()); return; } } QuestItem *qitem = NULL; QuestItem *ffaitem = NULL; QuestItem *conditem = NULL; LootItem *item = loot->LootItemInSlot(lootSlot,player,&qitem,&ffaitem,&conditem); if(!item) { player->SendEquipError( EQUIP_ERR_ALREADY_LOOTED, NULL, NULL ); return; } // questitems use the blocked field for other purposes if (!qitem && item->is_blocked) { player->SendLootRelease(lguid); return; } if (pItem) pItem->SetLootState(ITEM_LOOT_CHANGED); ItemPosCountVec dest; InventoryResult msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count ); if ( msg == EQUIP_ERR_OK ) { AllowedLooterSet* looters = item->GetAllowedLooters(); Item * newitem = player->StoreNewItem( dest, item->itemid, true, item->randomPropertyId, (looters->size() > 1) ? looters : NULL); if (qitem) { qitem->is_looted = true; //freeforall is 1 if everyone's supposed to get the quest item. if (item->freeforall || loot->GetPlayerQuestItems().size() == 1) player->SendNotifyLootItemRemoved(lootSlot); else loot->NotifyQuestItemRemoved(qitem->index); } else { if (ffaitem) { //freeforall case, notify only one player of the removal ffaitem->is_looted=true; player->SendNotifyLootItemRemoved(lootSlot); } else { //not freeforall, notify everyone if(conditem) conditem->is_looted=true; loot->NotifyItemRemoved(lootSlot); } } //if only one person is supposed to loot the item, then set it to looted if (!item->freeforall) item->is_looted = true; --loot->unlootedCount; player->SendNewItem(newitem, uint32(item->count), false, false, true); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type, item->count); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid, item->count); } else player->SendEquipError( msg, NULL, NULL, item->itemid ); }
void WorldSession::HandleSendMail(WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data,8+1+1+1+4+4+1+4+4+8+1); uint64 mailbox, unk3; std::string receiver, subject, body; uint32 unk1, unk2, money, COD; uint8 unk4; recv_data >> mailbox; recv_data >> receiver; if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; // recheck CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+1+1+4+4+1+4+4+8+1); recv_data >> subject; // recheck CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+1+4+4+1+4+4+8+1); recv_data >> body; // recheck CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+(body.size()+1)+4+4+1+4+4+8+1); recv_data >> unk1; // stationery? recv_data >> unk2; // 0x00000000 MailItemsInfo mi; uint8 items_count; recv_data >> items_count; // attached items count if(items_count > 12) // client limit return; // recheck CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+(body.size()+1)+4+4+1+items_count*(1+8)+4+4+8+1); if(items_count) { for(uint8 i = 0; i < items_count; ++i) { uint8 item_slot; uint64 item_guid; recv_data >> item_slot; recv_data >> item_guid; mi.AddItem(GUID_LOPART(item_guid), item_slot); } } recv_data >> money >> COD; // money and cod recv_data >> unk3; // const 0 recv_data >> unk4; // const 0 items_count = mi.size(); // this is the real size after the duplicates have been removed if (receiver.empty()) return; Player* pl = _player; uint64 rc = 0; if(normalizePlayerName(receiver)) rc = objmgr.GetPlayerGUIDByName(receiver); if (!rc) { sLog.outDetail("Player %u is sending mail to %s (GUID: not existed!) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); pl->SendMailResult(0, 0, MAIL_ERR_RECIPIENT_NOT_FOUND); return; } sLog.outDetail("Player %u is sending mail to %s (GUID: %u) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); if(pl->GetGUID() == rc) { pl->SendMailResult(0, 0, MAIL_ERR_CANNOT_SEND_TO_SELF); return; } uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client uint32 reqmoney = cost + money; if (pl->GetMoney() < reqmoney) { pl->SendMailResult(0, 0, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player *receive = objmgr.GetPlayer(rc); uint32 rc_team = 0; uint8 mails_count = 0; //do not allow to send to one player more than 100 mails if(receive) { rc_team = receive->GetTeam(); mails_count = receive->GetMailSize(); } else { rc_team = objmgr.GetPlayerTeamByGUID(rc); QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc)); if(result) { Field *fields = result->Fetch(); mails_count = fields[0].GetUInt32(); delete result; } } //do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mails_count > 100) { pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); return; } // test the receiver's Faction... if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER) { pl->SendMailResult(0, 0, MAIL_ERR_NOT_YOUR_TEAM); return; } if (items_count) { for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter) { MailItem& mailItem = mailItemIter->second; if(!mailItem.item_guidlow) { pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); return; } mailItem.item = pl->GetItemByGuid(MAKE_NEW_GUID(mailItem.item_guidlow, 0, HIGHGUID_ITEM)); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if(!mailItem.item || !mailItem.item->CanBeTraded()) { pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); return; } if (mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || mailItem.item->GetUInt32Value(ITEM_FIELD_DURATION)) { pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); return; } if(COD && mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED)) { pl->SendMailResult(0, 0, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } } } pl->SendMailResult(0, 0, MAIL_OK); uint32 itemTextId = 0; if (!body.empty()) { itemTextId = objmgr.CreateItemText( body ); } pl->ModifyMoney( -int32(reqmoney) ); pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; if(items_count > 0 || money > 0) { uint32 rc_account = 0; if(receive) rc_account = receive->GetSession()->GetAccountId(); else rc_account = objmgr.GetPlayerAccountIdByGUID(rc); if (items_count > 0) { for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter) { MailItem& mailItem = mailItemIter->second; if(!mailItem.item) continue; mailItem.item_template = mailItem.item ? mailItem.item->GetEntry() : 0; if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) { sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), mailItem.item->GetProto()->Name1, mailItem.item->GetEntry(), mailItem.item->GetCount(), receiver.c_str(), rc_account); } pl->MoveItemFromInventory(mailItem.item->GetBagSlot(), mailItem.item->GetSlot(), true); CharacterDatabase.BeginTransaction(); mailItem.item->DeleteFromInventoryDB(); //deletes item from character's inventory mailItem.item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone // owner in data will set at mail receive and item extracting CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", GUID_LOPART(rc), mailItem.item->GetGUIDLow()); CharacterDatabase.CommitTransaction(); } // if item send to character at another account, then apply item delivery delay needItemDelay = pl->GetSession()->GetAccountId() != rc_account; } if(money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) { sLog.outCommand(GetAccountId(),"GM %s (Account: %u) mail money: %u to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account); } } // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; // will delete item or place to receiver mail list WorldSession::SendMailTo(receive, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, pl->GetGUIDLow(), GUID_LOPART(rc), subject, itemTextId, &mi, money, COD, MAIL_CHECK_MASK_NONE, deliver_delay); CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); }