1. #include "stdafx.h"
  2. #include "../../libgame/include/grid.h"
  3. #include "constants.h"
  4. #include "utils.h"
  5. #include "config.h"
  6. #include "shop.h"
  7. #include "desc.h"
  8. #include "desc_manager.h"
  9. #include "char.h"
  10. #include "char_manager.h"
  11. #include "item.h"
  12. #include "item_manager.h"
  13. #include "buffer_manager.h"
  14. #include "packet.h"
  15. #include "log.h"
  16. #include "db.h"
  17. #include "questmanager.h"
  18. #include "monarch.h"
  19. #include "mob_manager.h"
  20. #include "locale_service.h"
  21. /* ------------------------------------------------------------------------------------ */
  22. extern int taxes;
  23. extern bool raise_empire_prices;
  24. extern bool trade_effect;
  25. extern int trade_effect_shop_threshold;
  26. CShop::CShop()
  27. : m_dwVnum(0), m_dwNPCVnum(0), m_pkPC(NULL)
  28. {
  29. m_pGrid = M2_NEW CGrid(10, 9);
  30. }
  31. CShop::~CShop()
  32. {
  33. TPacketGCShop pack;
  34. pack.header = HEADER_GC_SHOP;
  35. pack.subheader = SHOP_SUBHEADER_GC_END;
  36. pack.size = sizeof(TPacketGCShop);
  37. Broadcast(&pack, sizeof(pack));
  38. GuestMapType::iterator it;
  39. it = m_map_guest.begin();
  40. while (it != m_map_guest.end())
  41. {
  42. LPCHARACTER ch = it->first;
  43. ch->SetShop(NULL);
  44. ++it;
  45. }
  46. M2_DELETE(m_pGrid);
  47. }
  48. void CShop::SetPCShop(LPCHARACTER ch)
  49. {
  50. m_pkPC = ch;
  51. }
  52. bool CShop::Create(DWORD dwVnum, DWORD dwNPCVnum, TShopItemTable * pTable)
  53. {
  54. /*
  55. if (NULL == CMobManager::instance().Get(dwNPCVnum))
  56. {
  57. sys_err("No such a npc by vnum %d", dwNPCVnum);
  58. return false;
  59. }
  60. */
  61. sys_log(0, "SHOP #%d (Shopkeeper %d)", dwVnum, dwNPCVnum);
  62. m_dwVnum = dwVnum;
  63. m_dwNPCVnum = dwNPCVnum;
  64. BYTE bItemCount;
  65. for (bItemCount = 0; bItemCount < SHOP_HOST_ITEM_MAX_NUM; ++bItemCount)
  66. if (0 == (pTable + bItemCount)->vnum)
  67. break;
  68. SetShopItems(pTable, bItemCount);
  69. return true;
  70. }
  71. void CShop::SetShopItems(TShopItemTable * pTable, BYTE bItemCount)
  72. {
  73. if (bItemCount > SHOP_HOST_ITEM_MAX_NUM)
  74. return;
  75. m_pGrid->Clear();
  76. m_itemVector.resize(SHOP_HOST_ITEM_MAX_NUM);
  77. memset(&m_itemVector[0], 0, sizeof(SHOP_ITEM) * m_itemVector.size());
  78. for (int i = 0; i < bItemCount; ++i)
  79. {
  80. LPITEM pkItem = NULL;
  81. const TItemTable * item_table;
  82. if (m_pkPC)
  83. {
  84. pkItem = m_pkPC->GetItem(pTable->pos);
  85. if (!pkItem)
  86. {
  87. sys_err("cannot find item on pos (%d, %d) (name: %s)", pTable->pos.window_type, pTable->pos.cell, m_pkPC->GetName());
  88. continue;
  89. }
  90. item_table = pkItem->GetProto();
  91. }
  92. else
  93. {
  94. if (!pTable->vnum)
  95. continue;
  96. item_table = ITEM_MANAGER::instance().GetTable(pTable->vnum);
  97. }
  98. if (!item_table)
  99. {
  100. sys_err("Shop: no item table by item vnum #%d", pTable->vnum);
  101. continue;
  102. }
  103. int iPos;
  104. if (IsPCShop())
  105. {
  106. sys_log(0, "MyShop: use position %d", pTable->display_pos);
  107. iPos = pTable->display_pos;
  108. }
  109. else
  110. iPos = m_pGrid->FindBlank(1, item_table->bSize);
  111. if (iPos < 0)
  112. {
  113. sys_err("not enough shop window");
  114. continue;
  115. }
  116. if (!m_pGrid->IsEmpty(iPos, 1, item_table->bSize))
  117. {
  118. if (IsPCShop())
  119. {
  120. sys_err("not empty position for pc shop %s[%d]", m_pkPC->GetName(), m_pkPC->GetPlayerID());
  121. }
  122. else
  123. {
  124. sys_err("not empty position for npc shop");
  125. }
  126. continue;
  127. }
  128. m_pGrid->Put(iPos, 1, item_table->bSize);
  129. SHOP_ITEM & item = m_itemVector[iPos];
  130. item.pkItem = pkItem;
  131. item.itemid = 0;
  132. if (item.pkItem)
  133. {
  134. item.vnum = pkItem->GetVnum();
  135. item.count = pkItem->GetCount(); // PC 샵의 경우 아이템 개수는 진짜 아이템의 개수여야 한다.
  136. item.price = pTable->price; // 가격도 사용자가 정한대로..
  137. item.itemid = pkItem->GetID();
  138. }
  139. else
  140. {
  141. item.vnum = pTable->vnum;
  142. item.count = pTable->count;
  143. if (IS_SET(item_table->dwFlags, ITEM_FLAG_COUNT_PER_1GOLD))
  144. {
  145. if (item_table->dwGold == 0)
  146. item.price = 0;
  147. else
  148. item.price = item.count / item_table->dwGold;
  149. }
  150. else
  151. item.price = item_table->dwGold * item.count;
  152. }
  153. char name[36];
  154. snprintf(name, sizeof(name), "%-20s(#%-5d) (x %d)", item_table->szName, (int) item.vnum, item.count);
  155. sys_log(0, "SHOP_ITEM: %-36s PRICE %-5d", name, item.price);
  156. ++pTable;
  157. }
  158. }
  159. int CShop::Buy(LPCHARACTER ch, BYTE pos)
  160. {
  161. if (pos >= m_itemVector.size())
  162. {
  163. sys_log(0, "Shop::Buy : invalid position %d : %s", pos, ch->GetName());
  164. return SHOP_SUBHEADER_GC_INVALID_POS;
  165. }
  166. if (ch->GetGMLevel() > GM_PLAYER && ch->GetGMLevel() < GM_IMPLEMENTOR)
  167. {
  168. ch->ChatPacket(CHAT_TYPE_INFO, "GameMaster item alamaz.");
  169. return false;
  170. }
  171. sys_log(0, "Shop::Buy : name %s pos %d", ch->GetName(), pos);
  172. GuestMapType::iterator it = m_map_guest.find(ch);
  173. if (it == m_map_guest.end())
  174. return SHOP_SUBHEADER_GC_END;
  175. SHOP_ITEM& r_item = m_itemVector[pos];
  176. if (r_item.price < 0)
  177. {
  178. LogManager::instance().HackLog("SHOP_BUY_GOLD_OVERFLOW", ch);
  179. return SHOP_SUBHEADER_GC_NOT_ENOUGH_MONEY;
  180. }
  181. LPITEM pkSelectedItem = ITEM_MANAGER::instance().Find(r_item.itemid);
  182. if(trade_effect)
  183. {
  184. if (m_pkPC) {
  185. for (int i = 1; i < r_item.price+1; i=i+trade_effect_shop_threshold)
  186. ch->CreateFly(4,m_pkPC); // 1 Fly pro 100kk , sonst laggt es O:
  187. m_pkPC->CreateFly(5,ch); // 1 Item
  188. m_pkPC->CreateFly(6,ch); // Allgemeines anzeigen
  189. }
  190. }
  191. if (IsPCShop())
  192. {
  193. if (!pkSelectedItem)
  194. {
  195. sys_log(0, "Shop::Buy : Critical: This user seems to be a hacker : invalid pcshop item : BuyerPID:%d SellerPID:%d",
  196. ch->GetPlayerID(),
  197. m_pkPC->GetPlayerID());
  198. return false;
  199. }
  200. if ((pkSelectedItem->GetOwner() != m_pkPC))
  201. {
  202. sys_log(0, "Shop::Buy : Critical: This user seems to be a hacker : invalid pcshop item : BuyerPID:%d SellerPID:%d",
  203. ch->GetPlayerID(),
  204. m_pkPC->GetPlayerID());
  205. return false;
  206. }
  207. }
  208. long long llPrice = static_cast<long long>(r_item.price);
  209. if (it->second && raise_empire_prices) // if other empire, price is triple
  210. llPrice *= 3;
  211. if (ch->GetGold() < llPrice)
  212. {
  213. sys_log(1, "Shop::Buy : Not enough money : %s has %lld, price %d", ch->GetName(), ch->GetGold(), llPrice);
  214. return SHOP_SUBHEADER_GC_NOT_ENOUGH_MONEY;
  215. }
  216. LPITEM item;
  217. if (m_pkPC) // 피씨가 운영하는 샵은 피씨가 실제 아이템을 가지고있어야 한다.
  218. item = r_item.pkItem;
  219. else
  220. item = ITEM_MANAGER::instance().CreateItem(r_item.vnum, r_item.count);
  221. if (!item)
  222. return SHOP_SUBHEADER_GC_SOLD_OUT;
  223. int suprice = 0;
  224. int wonprice = 0;
  225. if (ch->GetShopOwner()->IsPC() && ch->GetShopOwner())
  226. {
  227. //sys_err("267.satir");
  228. char bufff[256];
  229. char bufff2[256];
  230. snprintf(bufff, sizeof(bufff), "pazar.pazarsuslot%d", pos);
  231. snprintf(bufff2, sizeof(bufff2), "pazar.pazarbarslot%d", pos);
  232. //sys_err("272.satir");
  233. suprice = m_pkPC->GetQuestFlag(bufff);
  234. wonprice = m_pkPC->GetQuestFlag(bufff2);
  235. //sys_err("275.satir");
  236. if (ch->CountSpecifyItem(38053) < suprice && ch->GetShopOwner()->IsPC() == true)
  237. {
  238. ch->ChatPacket(CHAT_TYPE_COMMAND, "sutasiyok");
  239. return SHOP_SUBHEADER_GC_END;
  240. }
  241. //sys_err("281.satir");
  242. if (ch->CountSpecifyItem(38052) < wonprice && ch->GetShopOwner()->IsPC() == true)
  243. {
  244. ch->ChatPacket(CHAT_TYPE_COMMAND, "wonyok");
  245. return SHOP_SUBHEADER_GC_END;
  246. }
  247. }
  248. int iEmptyPos;
  249. if (item->IsDragonSoul())
  250. {
  251. iEmptyPos = ch->GetEmptyDragonSoulInventory(item);
  252. }
  253. else
  254. {
  255. iEmptyPos = ch->GetEmptyInventory(item->GetSize());
  256. }
  257. if (iEmptyPos < 0)
  258. {
  259. if (m_pkPC)
  260. {
  261. sys_log(1, "Shop::Buy at PC Shop : Inventory full : %s size %d", ch->GetName(), item->GetSize());
  262. return SHOP_SUBHEADER_GC_INVENTORY_FULL;
  263. }
  264. else
  265. {
  266. sys_log(1, "Shop::Buy : Inventory full : %s size %d", ch->GetName(), item->GetSize());
  267. M2_DESTROY_ITEM(item);
  268. return SHOP_SUBHEADER_GC_INVENTORY_FULL;
  269. }
  270. }
  271. ch->SetQuestNPCID(ch->GetShopOwner()->GetVID());
  272. quest::CQuestManager::instance().Buy(ch->GetPlayerID());
  273. ch->PointChange(POINT_GOLD, -llPrice, false);
  274. //세금 계산
  275. DWORD dwTax = 0;
  276. int iVal = 0;
  277. if(taxes!=0)
  278. dwTax = llPrice * taxes / 100;
  279. llPrice = llPrice - dwTax;
  280. // 상점에서 살‹š 세금 5%
  281. if (!m_pkPC)
  282. {
  283. CMonarch::instance().SendtoDBAddMoney(dwTax, ch->GetEmpire(), ch);
  284. }
  285. if (ch->GetShopOwner()->IsPC() == true && ch->GetShopOwner())
  286. {
  287. if (ch->IsPC() == true && ch != m_pkPC)
  288. {
  289. ch->RemoveSpecifyItem(38053,suprice);
  290. }
  291. if (ch->IsPC() == true && ch != m_pkPC)
  292. {
  293. ch->RemoveSpecifyItem(38052,wonprice);
  294. }
  295. int sumiktar = 0;
  296. int barmiktar = 0;
  297. //sys_err("364.satir");
  298. if (ch != m_pkPC)
  299. {
  300. while (sumiktar < suprice && ch->GetShopOwner()->IsPC() == true)
  301. {
  302. //m_pkPC->AutoGiveItem(38052,1);
  303. m_pkPC->AutoGiveItem(38053,1,0,false);
  304. sumiktar++;
  305. }
  306. }
  307. if (ch != m_pkPC)
  308. {
  309. while (barmiktar < wonprice && ch->GetShopOwner()->IsPC() == true)
  310. {
  311. //m_pkPC->AutoGiveItem(50513,1);
  312. m_pkPC->AutoGiveItem(38052,1,0,false);
  313. barmiktar++;
  314. }
  315. }
  316. }
  317. // 군주 시스템 : 세금 징수
  318. if (m_pkPC)
  319. {
  320. m_pkPC->SyncQuickslot(QUICKSLOT_TYPE_ITEM, item->GetCell(), 255);
  321. if (item->GetVnum() == 90008 || item->GetVnum() == 90009) // VCARD
  322. {
  323. VCardUse(m_pkPC, ch, item);
  324. item = NULL;
  325. }
  326. else
  327. {
  328. char buf[512];
  329. if (item->GetVnum() >= 80003 && item->GetVnum() <= 38052)
  330. {
  331. snprintf(buf, sizeof(buf), "%s FROM: %u TO: %u PRICE: %u", item->GetName(), ch->GetPlayerID(), m_pkPC->GetPlayerID(), llPrice);
  332. LogManager::instance().GoldBarLog(ch->GetPlayerID(), item->GetID(), SHOP_BUY, buf);
  333. LogManager::instance().GoldBarLog(m_pkPC->GetPlayerID(), item->GetID(), SHOP_SELL, buf);
  334. }
  335. item->RemoveFromCharacter();
  336. if (item->IsDragonSoul())
  337. item->AddToCharacter(ch, TItemPos(DRAGON_SOUL_INVENTORY, iEmptyPos));
  338. else
  339. item->AddToCharacter(ch, TItemPos(INVENTORY, iEmptyPos));
  340. ITEM_MANAGER::instance().FlushDelayedSave(item);
  341. snprintf(buf, sizeof(buf), "%s %u(%s) %u %u", item->GetName(), m_pkPC->GetPlayerID(), m_pkPC->GetName(), llPrice, item->GetCount());
  342. LogManager::instance().ItemLog(ch, item, "SHOP_BUY", buf);
  343. snprintf(buf, sizeof(buf), "%s %u(%s) %u %u", item->GetName(), ch->GetPlayerID(), ch->GetName(), llPrice, item->GetCount());
  344. LogManager::instance().ItemLog(m_pkPC, item, "SHOP_SELL", buf);
  345. }
  346. r_item.pkItem = NULL;
  347. BroadcastUpdateItem(pos);
  348. m_pkPC->PointChange(POINT_GOLD, llPrice, false);
  349. if (iVal > 0)
  350. m_pkPC->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("판매금액의 %d %% 가 세금으로 나가게됩니다"), iVal);
  351. CMonarch::instance().SendtoDBAddMoney(dwTax, m_pkPC->GetEmpire(), m_pkPC);
  352. }
  353. else
  354. {
  355. if (item->IsDragonSoul())
  356. item->AddToCharacter(ch, TItemPos(DRAGON_SOUL_INVENTORY, iEmptyPos));
  357. else
  358. item->AddToCharacter(ch, TItemPos(INVENTORY, iEmptyPos));
  359. ITEM_MANAGER::instance().FlushDelayedSave(item);
  360. LogManager::instance().ItemLog(ch, item, "BUY", item->GetName());
  361. if (item->GetVnum() >= 80003 && item->GetVnum() <= 38052)
  362. {
  363. LogManager::instance().GoldBarLog(ch->GetPlayerID(), item->GetID(), PERSONAL_SHOP_BUY, "");
  364. }
  365. DBManager::instance().SendMoneyLog(MONEY_LOG_SHOP, item->GetVnum(), -llPrice);
  366. }
  367. if (item)
  368. sys_log(0, "SHOP: BUY: name %s %s(x %d):%u price %u", ch->GetName(), item->GetName(), item->GetCount(), item->GetID(), llPrice);
  369. ch->Save();
  370. return (SHOP_SUBHEADER_GC_OK);
  371. }
  372. bool CShop::AddGuest(LPCHARACTER ch, DWORD owner_vid, bool bOtherEmpire)
  373. {
  374. if (!ch)
  375. return false;
  376. if (ch->GetExchange())
  377. return false;
  378. if (ch->GetShop())
  379. return false;
  380. ch->SetShop(this);
  381. m_map_guest.insert(GuestMapType::value_type(ch, bOtherEmpire));
  382. TPacketGCShop pack;
  383. pack.header = HEADER_GC_SHOP;
  384. pack.subheader = SHOP_SUBHEADER_GC_START;
  385. TPacketGCShopStart pack2;
  386. memset(&pack2, 0, sizeof(pack2));
  387. pack2.owner_vid = owner_vid;
  388. for (DWORD i = 0; i < m_itemVector.size() && i < SHOP_HOST_ITEM_MAX_NUM; ++i)
  389. {
  390. const SHOP_ITEM & item = m_itemVector[i];
  391. if (m_pkPC && !item.pkItem)
  392. continue;
  393. pack2.items[i].vnum = item.vnum;
  394. if (bOtherEmpire && raise_empire_prices) // no empire price penalty for pc shop
  395. pack2.items[i].price = item.price * 3;
  396. else
  397. pack2.items[i].price = item.price;
  398. pack2.items[i].count = item.count;
  399. if (item.pkItem)
  400. {
  401. thecore_memcpy(pack2.items[i].alSockets, item.pkItem->GetSockets(), sizeof(pack2.items[i].alSockets));
  402. thecore_memcpy(pack2.items[i].aAttr, item.pkItem->GetAttributes(), sizeof(pack2.items[i].aAttr));
  403. }
  404. }
  405. pack.size = sizeof(pack) + sizeof(pack2);
  406. ch->GetDesc()->BufferedPacket(&pack, sizeof(TPacketGCShop));
  407. ch->GetDesc()->Packet(&pack2, sizeof(TPacketGCShopStart));
  408. return true;
  409. }
  410. void CShop::RemoveGuest(LPCHARACTER ch)
  411. {
  412. if (ch->GetShop() != this)
  413. return;
  414. m_map_guest.erase(ch);
  415. ch->SetShop(NULL);
  416. TPacketGCShop pack;
  417. pack.header = HEADER_GC_SHOP;
  418. pack.subheader = SHOP_SUBHEADER_GC_END;
  419. pack.size = sizeof(TPacketGCShop);
  420. ch->GetDesc()->Packet(&pack, sizeof(pack));
  421. }
  422. void CShop::Broadcast(const void * data, int bytes)
  423. {
  424. sys_log(1, "Shop::Broadcast %p %d", data, bytes);
  425. GuestMapType::iterator it;
  426. it = m_map_guest.begin();
  427. while (it != m_map_guest.end())
  428. {
  429. LPCHARACTER ch = it->first;
  430. if (ch->GetDesc())
  431. ch->GetDesc()->Packet(data, bytes);
  432. ++it;
  433. }
  434. }
  435. void CShop::BroadcastUpdateItem(BYTE pos)
  436. {
  437. TPacketGCShop pack;
  438. TPacketGCShopUpdateItem pack2;
  439. TEMP_BUFFER buf;
  440. pack.header = HEADER_GC_SHOP;
  441. pack.subheader = SHOP_SUBHEADER_GC_UPDATE_ITEM;
  442. pack.size = sizeof(pack) + sizeof(pack2);
  443. pack2.pos = pos;
  444. if (m_pkPC && !m_itemVector[pos].pkItem)
  445. pack2.item.vnum = 0;
  446. else
  447. {
  448. pack2.item.vnum = m_itemVector[pos].vnum;
  449. if (m_itemVector[pos].pkItem)
  450. {
  451. thecore_memcpy(pack2.item.alSockets, m_itemVector[pos].pkItem->GetSockets(), sizeof(pack2.item.alSockets));
  452. thecore_memcpy(pack2.item.aAttr, m_itemVector[pos].pkItem->GetAttributes(), sizeof(pack2.item.aAttr));
  453. }
  454. else
  455. {
  456. memset(pack2.item.alSockets, 0, sizeof(pack2.item.alSockets));
  457. memset(pack2.item.aAttr, 0, sizeof(pack2.item.aAttr));
  458. }
  459. }
  460. pack2.item.price = m_itemVector[pos].price;
  461. pack2.item.count = m_itemVector[pos].count;
  462. buf.write(&pack, sizeof(pack));
  463. buf.write(&pack2, sizeof(pack2));
  464. Broadcast(buf.read_peek(), buf.size());
  465. }
  466. int CShop::GetNumberByVnum(DWORD dwVnum)
  467. {
  468. int itemNumber = 0;
  469. for (DWORD i = 0; i < m_itemVector.size() && i < SHOP_HOST_ITEM_MAX_NUM; ++i)
  470. {
  471. const SHOP_ITEM & item = m_itemVector[i];
  472. if (item.vnum == dwVnum)
  473. {
  474. itemNumber += item.count;
  475. }
  476. }
  477. return itemNumber;
  478. }
  479. bool CShop::IsSellingItem(DWORD itemID)
  480. {
  481. bool isSelling = false;
  482. for (DWORD i = 0; i < m_itemVector.size() && i < SHOP_HOST_ITEM_MAX_NUM; ++i)
  483. {
  484. if (static_cast<unsigned long>(m_itemVector[i].itemid) == itemID)
  485. {
  486. isSelling = true;
  487. break;
  488. }
  489. }
  490. return isSelling;
  491. }