//============================================================================= // File : offline_shop.cpp // Use : General offline shop functions. // Author : Ken Kaneki // Date : 1/20/2016 - 1:31 AM //============================================================================= #include "stdafx.h" #include "../../libgame/include/grid.h" #include "../../common/tables.h" #include "constants.h" #include "utils.h" #include "config.h" #include "desc.h" #include "desc_manager.h" #include "char.h" #include "char_manager.h" #include "item.h" #include "item_manager.h" #include "buffer_manager.h" #include "packet.h" #include "log.h" #include "db.h" #include "questmanager.h" #include "monarch.h" #include "mob_manager.h" #include "locale_service.h" #include "offline_shop.h" #include "offlineshop_manager.h" #include "p2p.h" COfflineShop::COfflineShop(){} COfflineShop::~COfflineShop() { TPacketGCShop pack; pack.header = HEADER_GC_OFFLINE_SHOP; pack.subheader = SHOP_SUBHEADER_GC_END; pack.size = sizeof(TPacketGCShop); Broadcast(&pack, sizeof(pack)); for (GuestMapType::iterator it = m_map_guest.begin(); it != m_map_guest.end(); ++it) { LPCHARACTER ch = it->first; ch->SetOfflineShop(NULL); } } void COfflineShop::AddGuest(LPCHARACTER ch, LPCHARACTER npc) { if (!ch || ch->GetExchange() || ch->GetShop() || ch->GetMyShop() || ch->GetOfflineShop()) return; ch->SetOfflineShop(this); m_map_guest.insert(GuestMapType::value_type(ch, false)); TPacketGCShop pack; pack.header = HEADER_GC_OFFLINE_SHOP; pack.subheader = SHOP_SUBHEADER_GC_START; TPacketGCOfflineShopStart pack2; memset(&pack2, 0, sizeof(pack2)); pack2.owner_vid = npc->GetVID(); char szQuery[1024]; snprintf(szQuery, sizeof(szQuery), "SELECT pos, count, vnum, price, evolution, socket0, socket1, socket2, socket3, " "attrtype0, attrvalue0, " "attrtype1, attrvalue1, " "attrtype2, attrvalue2, " "attrtype3, attrvalue3, " "attrtype4, attrvalue4, " "attrtype5, attrvalue5, " "attrtype6, attrvalue6 " #ifdef USE_LENTS_SHOULDER_SASH ",applytype0, applyvalue0, " "applytype1, applyvalue1, " "applytype2, applyvalue2, " "applytype3, applyvalue3, " "applytype4, applyvalue4, " "applytype5, applyvalue5, " "applytype6, applyvalue6, " "applytype7, applyvalue7 " #endif "FROM %soffline_shop_item WHERE owner_id = %u and status = 0", get_table_postfix(), npc->GetOfflineShopRealOwner() ); std::auto_ptr pMsg(DBManager::Instance().DirectQuery(szQuery)); if (pMsg->Get()->uiNumRows == 0) { DBManager::instance().DirectQuery("DELETE FROM player.offline_shop_npc WHERE owner_id = %u", npc->GetOfflineShopRealOwner()); ch->SetOfflineShop(NULL); ch->SetOfflineShopOwner(NULL); M2_DESTROY_CHARACTER(npc); } for (int i = 0; i < mysql_num_rows(pMsg->Get()->pSQLResult); ++i) { MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); int cur = 0; BYTE bPos = 0; str_to_number(bPos, row[cur++]); str_to_number(pack2.items[bPos].count, row[cur++]); str_to_number(pack2.items[bPos].vnum, row[cur++]); str_to_number(pack2.items[bPos].price, row[cur++]); str_to_number(pack2.items[bPos].evolution, row[cur++]); DWORD alSockets[ITEM_SOCKET_MAX_NUM]; for (BYTE j = 0; j < ITEM_SOCKET_MAX_NUM; j++) str_to_number(pack2.items[bPos].alSockets[j], row[cur++]); for (BYTE j = 0; j < ITEM_ATTRIBUTE_MAX_NUM; j++) { str_to_number(pack2.items[bPos].aAttr[j].bType, row[cur++]); str_to_number(pack2.items[bPos].aAttr[j].sValue, row[cur++]); } } pack.size = sizeof(pack) + sizeof(pack2); if (ch->GetDesc()) { ch->GetDesc()->BufferedPacket(&pack, sizeof(TPacketGCShop)); ch->GetDesc()->Packet(&pack2, sizeof(TPacketGCOfflineShopStart)); } } void COfflineShop::RemoveGuest(LPCHARACTER ch) { // If this offline shop is not equal to this, break it if (ch->GetOfflineShop() != this) return; m_map_guest.erase(ch); ch->SetOfflineShop(NULL); TPacketGCShop pack; pack.header = HEADER_GC_OFFLINE_SHOP; pack.subheader = SHOP_SUBHEADER_GC_END; pack.size = sizeof(TPacketGCShop); if (ch->GetDesc()) ch->GetDesc()->Packet(&pack, sizeof(pack)); } void COfflineShop::RemoveAllGuest() { TPacketGCShop pack; pack.header = HEADER_GC_OFFLINE_SHOP; pack.subheader = SHOP_SUBHEADER_GC_END; pack.size = sizeof(TPacketGCShop); Broadcast(&pack, sizeof(pack)); for (GuestMapType::iterator it = m_map_guest.begin(); it != m_map_guest.end(); ++it) { LPCHARACTER ch = it->first; ch->SetOfflineShop(NULL); } } void COfflineShop::Destroy(LPCHARACTER npc) { DBManager::instance().DirectQuery("DELETE FROM %soffline_shop_npc WHERE owner_id = %u", get_table_postfix(), npc->GetOfflineShopRealOwner()); RemoveAllGuest(); M2_DESTROY_CHARACTER(npc); } int COfflineShop::Buy(LPCHARACTER ch, BYTE bPos) { if (ch->GetOfflineShopOwner()->GetOfflineShopRealOwner() == ch->GetPlayerID()) { ch->ChatPacket(CHAT_TYPE_INFO, "You can't buy anything from your offline shop."); return SHOP_SUBHEADER_GC_END; } if (bPos >= OFFLINE_SHOP_HOST_ITEM_MAX_NUM) return SHOP_SUBHEADER_GC_END; GuestMapType::iterator it = m_map_guest.find(ch); if (it == m_map_guest.end()) return SHOP_SUBHEADER_GC_END; char szQuery[1024]; snprintf(szQuery, sizeof(szQuery), "SELECT count, vnum, price, evolution, socket0, socket1, socket2, socket3, " "attrtype0, attrvalue0, " "attrtype1, attrvalue1, " "attrtype2, attrvalue2, " "attrtype3, attrvalue3, " "attrtype4, attrvalue4, " "attrtype5, attrvalue5, " "attrtype6, attrvalue6 " #ifdef USE_LENTS_SHOULDER_SASH ", applytype0, applyvalue0, " "applytype1, applyvalue1, " "applytype2, applyvalue2, " "applytype3, applyvalue3, " "applytype4, applyvalue4, " "applytype5, applyvalue5, " "applytype6, applyvalue6, " "applytype7, applyvalue7 " #endif "FROM %soffline_shop_item WHERE owner_id = %u and pos = %d and status = 0", get_table_postfix(), ch->GetOfflineShopOwner()->GetOfflineShopRealOwner(), bPos); std::auto_ptr pMsg(DBManager::Instance().DirectQuery(szQuery)); if (pMsg->Get()->uiNumRows == 0) return SHOP_SUBHEADER_GC_END; TPlayerItem item; #ifdef ENABLE_MAXIMUM_YANG_FOR_OFFLINE_SHOP long long llPrice = 0; #else DWORD dwPrice = 0; #endif // Load the item for (int i = 0; i < mysql_num_rows(pMsg->Get()->pSQLResult); ++i) { MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); int cur = 0; str_to_number(item.count, row[cur++]); str_to_number(item.vnum, row[cur++]); str_to_number(item.evolution, row[cur++]); #ifdef ENABLE_MAXIMUM_YANG_FOR_OFFLINE_SHOP str_to_number(llPrice,row[cur++]); #else str_to_number(dwPrice, row[cur++]); #endif for (BYTE j = 0; j < ITEM_SOCKET_MAX_NUM; j++) str_to_number(item.alSockets[j], row[cur++]); for (BYTE n = 0; n < ITEM_ATTRIBUTE_MAX_NUM; n++) { str_to_number(item.aAttr[n].bType, row[cur++]); str_to_number(item.aAttr[n].sValue, row[cur++]); } } #ifdef ENABLE_MAXIMUM_YANG_FOR_OFFLINE_SHOP if (ch->GetGold() < static_cast(llPrice)) { sys_log(1, "OfflineShop::Buy : Not enough money : %s has %u, price %lld", ch->GetName(), ch->GetGold(), llPrice); return SHOP_SUBHEADER_GC_NOT_ENOUGH_MONEY; } #else if (ch->GetGold() < static_cast(dwPrice)) { sys_log(1, "OfflineShop::Buy : Not enough money : %s has %u, price %u", ch->GetName(), ch->GetGold(), dwPrice); return SHOP_SUBHEADER_GC_NOT_ENOUGH_MONEY; } #endif LPITEM pItem = ITEM_MANAGER::Instance().CreateItem(item.vnum, item.count); if (!pItem) return SHOP_SUBHEADER_GC_SOLD_OUT; pItem->SetAttributes(item.aAttr); pItem->SetSockets(item.alSockets); // int iEmptyPos = pItem->IsDragonSoul() ? ch->GetEmptyDragonSoulInventory(pItem) : ch->GetEmptyInventory(pItem->GetSize()); int iEmptyPos = 0; if (pItem->IsDragonSoul()) iEmptyPos = ch->GetEmptyDragonSoulInventory(pItem); #ifdef WJ_SPLIT_INVENTORY_SYSTEM else if (pItem->IsSkillBook()) iEmptyPos = ch->GetEmptySkillBookInventory(pItem->GetSize()); else if (pItem->IsUpgradeItem()) iEmptyPos = ch->GetEmptyUpgradeItemsInventory(pItem->GetSize()); else if (pItem->IsStone()) iEmptyPos = ch->GetEmptyStoneInventory(pItem->GetSize()); #endif else iEmptyPos = ch->GetEmptyInventory(pItem->GetSize()); if (iEmptyPos < 0) return SHOP_SUBHEADER_GC_INVENTORY_FULL; // pItem->AddToCharacter(ch, TItemPos(pItem->IsDragonSoul() ? DRAGON_SOUL_INVENTORY : INVENTORY, iEmptyPos)); if (pItem->IsDragonSoul()) pItem->AddToCharacter(ch, TItemPos(DRAGON_SOUL_INVENTORY, iEmptyPos)); #ifdef WJ_SPLIT_INVENTORY_SYSTEM else if (pItem->IsSkillBook()) pItem->AddToCharacter(ch, TItemPos(INVENTORY, iEmptyPos)); else if (pItem->IsUpgradeItem()) pItem->AddToCharacter(ch, TItemPos(INVENTORY, iEmptyPos)); else if (pItem->IsStone()) pItem->AddToCharacter(ch, TItemPos(INVENTORY, iEmptyPos)); #endif else pItem->AddToCharacter(ch, TItemPos(INVENTORY,iEmptyPos)); // Find the owner of the offline shop. LPCHARACTER owner = CHARACTER_MANAGER::Instance().FindByPID(ch->GetOfflineShopOwner()->GetOfflineShopRealOwner()); if (owner) { #ifdef ENABLE_MAXIMUM_YANG_FOR_OFFLINE_SHOP bool bIsOverflow = owner->GetGold() + llPrice > GOLD_MAX - 1 ? true : false; if (bIsOverflow) DBManager::Instance().DirectQuery("UPDATE %splayer SET mymoney = mymoney + %lld WHERE id = %u", get_table_postfix(), llPrice, ch->GetOfflineShopOwner()->GetOfflineShopRealOwner()); else owner->PointChange(POINT_GOLD, llPrice, false); #else bool bIsOverflow = owner->GetGold() + static_cast(dwPrice) > GOLD_MAX - 1 ? true : false; if (bIsOverflow) DBManager::Instance().DirectQuery("UPDATE %splayer SET mymoney = mymoney + %u WHERE id = %u", get_table_postfix(), dwPrice, ch->GetOfflineShopOwner()->GetOfflineShopRealOwner()); else owner->PointChange(POINT_GOLD, dwPrice, false); #endif } else #ifdef ENABLE_MAXIMUM_YANG_FOR_OFFLINE_SHOP DBManager::Instance().DirectQuery("UPDATE %splayer SET mymoney = mymoney + %lld WHERE id = %u", get_table_postfix(), llPrice, ch->GetOfflineShopOwner()->GetOfflineShopRealOwner()); #else DBManager::Instance().DirectQuery("UPDATE %splayer SET mymoney = mymoney + %u WHERE id = %u", get_table_postfix(), dwPrice, ch->GetOfflineShopOwner()->GetOfflineShopRealOwner()); #endif ITEM_MANAGER::Instance().FlushDelayedSave(pItem); RemoveItem(ch->GetOfflineShopOwner()->GetOfflineShopRealOwner(), bPos); BroadcastUpdateItem(bPos, ch->GetOfflineShopOwner()->GetOfflineShopRealOwner(), true); #ifdef ENABLE_MAXIMUM_YANG_FOR_OFFLINE_SHOP ch->PointChange(POINT_GOLD, -llPrice, false); #else ch->PointChange(POINT_GOLD, -dwPrice, false); #endif ch->Save(); LogManager::Instance().ItemLog(ch, pItem, "BOUGHT ITEM FROM OFFLINE SHOP", ""); BYTE bLeftItemCount = GetLeftItemCount(ch->GetOfflineShopOwner()->GetOfflineShopRealOwner()); if (bLeftItemCount == 0) { // If the owner of offline shop is online, reset the status of offline shop. if (owner) { owner->SetOfflineShopStatus(OFFLINE_SHOP_AVAILABLE); owner->SetOfflineShopVID(0); } COfflineShopManager::Instance().RemoveFromList(ch->GetOfflineShopOwner()); Destroy(ch->GetOfflineShopOwner()); } return (SHOP_SUBHEADER_GC_OK); } void COfflineShop::BroadcastUpdateItem(BYTE bPos, DWORD dwPID, bool bDestroy) { TPacketGCShop pack; #ifdef ENABLE_MAXIMUM_YANG_FOR_OFFLINE_SHOP TPacketGCOfflineShopUpdateItem pack2; #else TPacketGCShopUpdateItem pack2; #endif TEMP_BUFFER buf; pack.header = HEADER_GC_OFFLINE_SHOP; pack.subheader = SHOP_SUBHEADER_GC_UPDATE_ITEM; if (bDestroy) memset(&pack2, 0, sizeof(pack2)); else { char szQuery[1024]; snprintf(szQuery, sizeof(szQuery), "SELECT pos, count, vnum, price, evolution, socket0, socket1, socket2, socket3, " "attrtype0, attrvalue0, " "attrtype1, attrvalue1, " "attrtype2, attrvalue2, " "attrtype3, attrvalue3, " "attrtype4, attrvalue4, " "attrtype5, attrvalue5, " "attrtype6, attrvalue6 " #ifdef USE_LENTS_SHOULDER_SASH ",applytype0, applyvalue0, " "applytype1, applyvalue1, " "applytype2, applyvalue2, " "applytype3, applyvalue3, " "applytype4, applyvalue4, " "applytype5, applyvalue5, " "applytype6, applyvalue6, " "applytype7, applyvalue7 " #endif "FROM %soffline_shop_item WHERE owner_id = %u and pos = %d and status = 0", get_table_postfix(), dwPID, bPos ); std::auto_ptr pMsg(DBManager::Instance().DirectQuery(szQuery)); if (pMsg->Get()->uiNumRows == 0) { memset(&pack2, 0, sizeof(pack2)); } else { for (int i = 0; i < mysql_num_rows(pMsg->Get()->pSQLResult); ++i) { MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); int cur = 0; str_to_number(pack2.item.count, row[cur++]); str_to_number(pack2.item.vnum, row[cur++]); str_to_number(pack2.item.price, row[cur++]); str_to_number(pack2.item.evolution, row[cur++]); // Set Sockets for (BYTE j = 0; j < ITEM_SOCKET_MAX_NUM; j++) str_to_number(pack2.item.alSockets[i], row[cur++]); // Set Attributes for (BYTE j = 0; j < ITEM_ATTRIBUTE_MAX_NUM; j++) { str_to_number(pack2.item.aAttr[i].bType, row[cur++]); str_to_number(pack2.item.aAttr[i].sValue, row[cur++]); } } } } pack2.pos = bPos; pack.size = sizeof(pack) + sizeof(pack2); buf.write(&pack, sizeof(pack)); buf.write(&pack2, sizeof(pack2)); Broadcast(buf.read_peek(), buf.size()); } void COfflineShop::BroadcastUpdatePrice(BYTE bPos, DWORD dwPrice) { TPacketGCShop pack; TPacketGCShopUpdatePrice pack2; TEMP_BUFFER buf; pack.header = HEADER_GC_OFFLINE_SHOP; pack.subheader = SHOP_SUBHEADER_GC_UPDATE_PRICE; pack.size = sizeof(pack) + sizeof(pack2); pack2.bPos = bPos; pack2.iPrice = dwPrice; buf.write(&pack, sizeof(pack)); buf.write(&pack2, sizeof(pack2)); Broadcast(buf.read_peek(), buf.size()); } void COfflineShop::Refresh(LPCHARACTER ch) { TPacketGCShop pack; pack.header = HEADER_GC_OFFLINE_SHOP; pack.subheader = SHOP_SUBHEADER_GC_UPDATE_ITEM2; TPacketGCOfflineShopStart pack2; memset(&pack2, 0, sizeof(pack2)); pack2.owner_vid = 0; char szQuery[1024]; snprintf(szQuery, sizeof(szQuery), "SELECT pos, count, vnum, price, evolution, socket0, socket1, socket2, socket3, " "attrtype0, attrvalue0, " "attrtype1, attrvalue1, " "attrtype2, attrvalue2, " "attrtype3, attrvalue3, " "attrtype4, attrvalue4, " "attrtype5, attrvalue5, " "attrtype6, attrvalue6 " #ifdef USE_LENTS_SHOULDER_SASH ",applytype0, applyvalue0, " "applytype1, applyvalue1, " "applytype2, applyvalue2, " "applytype3, applyvalue3, " "applytype4, applyvalue4, " "applytype5, applyvalue5, " "applytype6, applyvalue6, " "applytype7, applyvalue7 " #endif "FROM %soffline_shop_item WHERE owner_id = %u and status = 0", get_table_postfix(), ch->GetPlayerID() ); std::auto_ptr pMsg(DBManager::Instance().DirectQuery(szQuery)); for (int i = 0; i < mysql_num_rows(pMsg->Get()->pSQLResult); ++i) { MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); int cur = 0; BYTE bPos = 0; str_to_number(bPos, row[cur++]); str_to_number(pack2.items[bPos].count, row[cur++]); str_to_number(pack2.items[bPos].vnum, row[cur++]); str_to_number(pack2.items[bPos].price, row[cur++]); str_to_number(pack2.items[bPos].evolution, row[cur++]); for (BYTE j = 0; j < ITEM_SOCKET_MAX_NUM; j++) str_to_number(pack2.items[bPos].alSockets[j], row[cur++]); for (BYTE n = 0; n < ITEM_ATTRIBUTE_MAX_NUM; n++) { str_to_number(pack2.items[bPos].aAttr[n].bType, row[cur++]); str_to_number(pack2.items[bPos].aAttr[n].sValue, row[cur++]); } } pack.size = sizeof(pack) + sizeof(pack2); if (ch->GetDesc()) { ch->GetDesc()->BufferedPacket(&pack, sizeof(TPacketGCShop)); ch->GetDesc()->Packet(&pack2, sizeof(TPacketGCOfflineShopStart)); } } bool COfflineShop::RemoveItem(DWORD dwVID, BYTE bPos) { DBManager::instance().DirectQuery("DELETE FROM %soffline_shop_item WHERE owner_id = %u and pos = %d and status = 0", get_table_postfix(), dwVID, bPos); return true; } BYTE COfflineShop::GetLeftItemCount(DWORD dwPID) { std::auto_ptr pMsg(DBManager::instance().DirectQuery("SELECT COUNT(*) FROM %soffline_shop_item WHERE owner_id = %u and status = 0", get_table_postfix(), dwPID)); if (pMsg->Get()->uiNumRows == 0) return 0; MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); BYTE bCount = 0; str_to_number(bCount, row[0]); return bCount; } void COfflineShop::Broadcast(const void * data, int bytes) { sys_log(1, "OfflineShop::Broadcast %p %d", data, bytes); for (GuestMapType::iterator it = m_map_guest.begin(); it != m_map_guest.end(); ++it) { LPCHARACTER ch = it->first; if (ch && ch->GetDesc()) ch->GetDesc()->Packet(data, bytes); } }