Guest User

char_item.cpp

a guest
Oct 6th, 2024
109
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 212.68 KB | None | 0 0
  1. #include "stdafx.h"
  2.  
  3. #include <stack>
  4.  
  5. #include "utils.h"
  6. #include "config.h"
  7. #include "char.h"
  8. #include "char_manager.h"
  9. #include "item_manager.h"
  10. #include "desc.h"
  11. #include "desc_client.h"
  12. #include "desc_manager.h"
  13. #include "packet.h"
  14. #include "affect.h"
  15. #include "skill.h"
  16. #include "start_position.h"
  17. #include "mob_manager.h"
  18. #include "db.h"
  19. #include "log.h"
  20. #include "vector.h"
  21. #include "buffer_manager.h"
  22. #include "questmanager.h"
  23. #include "fishing.h"
  24. #include "party.h"
  25. #include "dungeon.h"
  26. #include "refine.h"
  27. #include "unique_item.h"
  28. #include "war_map.h"
  29. #include "xmas_event.h"
  30. #include "marriage.h"
  31. #include "monarch.h"
  32. #include "polymorph.h"
  33. #include "blend_item.h"
  34. #include "castle.h"
  35. #include "BattleArena.h"
  36. #include "arena.h"
  37. #include "dev_log.h"
  38. #include "pcbang.h"
  39. #include "threeway_war.h"
  40.  
  41. #include "safebox.h"
  42. #include "shop.h"
  43.  
  44. #include "../../common/VnumHelper.h"
  45. #include "DragonSoul.h"
  46. #include "buff_on_attributes.h"
  47. #include "belt_inventory_helper.h"
  48.  
  49. #ifdef ENABLE_SWITCHBOT
  50. #include "switchbot.h"
  51. #endif
  52.  
  53. //auction_temp
  54. #ifdef __AUCTION__
  55. #include "auction_manager.h"
  56. #endif
  57. const int ITEM_BROKEN_METIN_VNUM = 28960;
  58.  
  59. // CHANGE_ITEM_ATTRIBUTES
  60. const DWORD CHARACTER::msc_dwDefaultChangeItemAttrCycle = 10;
  61. const char CHARACTER::msc_szLastChangeItemAttrFlag[] = "Item.LastChangeItemAttr";
  62. const char CHARACTER::msc_szChangeItemAttrCycleFlag[] = "change_itemattr_cycle";
  63. // END_OF_CHANGE_ITEM_ATTRIBUTES
  64. const BYTE g_aBuffOnAttrPoints[] = { POINT_ENERGY, POINT_COSTUME_ATTR_BONUS };
  65.  
  66. struct FFindStone
  67. {
  68. std::map<DWORD, LPCHARACTER> m_mapStone;
  69.  
  70. void operator()(LPENTITY pEnt)
  71. {
  72. if (pEnt->IsType(ENTITY_CHARACTER) == true)
  73. {
  74. LPCHARACTER pChar = (LPCHARACTER)pEnt;
  75.  
  76. if (pChar->IsStone() == true)
  77. {
  78. m_mapStone[(DWORD)pChar->GetVID()] = pChar;
  79. }
  80. }
  81. }
  82. };
  83.  
  84.  
  85. //귀환부, 귀환기억부, 결혼반지
  86. static bool IS_SUMMON_ITEM(int vnum)
  87. {
  88. switch (vnum)
  89. {
  90. case 22000:
  91. case 22010:
  92. case 22011:
  93. case 22020:
  94. case ITEM_MARRIAGE_RING:
  95. return true;
  96. }
  97.  
  98. return false;
  99. }
  100.  
  101. static bool IS_MONKEY_DUNGEON(int map_index)
  102. {
  103. switch (map_index)
  104. {
  105. case 5:
  106. case 25:
  107. case 45:
  108. case 108:
  109. case 109:
  110. return true;;
  111. }
  112.  
  113. return false;
  114. }
  115.  
  116. bool IS_SUMMONABLE_ZONE(int map_index)
  117. {
  118. // 몽키던전
  119. if (IS_MONKEY_DUNGEON(map_index))
  120. return false;
  121. // 성
  122. if (IS_CASTLE_MAP(map_index))
  123. return false;
  124.  
  125. switch (map_index)
  126. {
  127. case 66 : // 사귀타워
  128. case 71 : // 거미 던전 2층
  129. case 72 : // 천의 동굴
  130. case 73 : // 천의 동굴 2층
  131. case 193 : // 거미 던전 2-1층
  132. #if 0
  133. case 184 : // 천의 동굴(신수)
  134. case 185 : // 천의 동굴 2층(신수)
  135. case 186 : // 천의 동굴(천조)
  136. case 187 : // 천의 동굴 2층(천조)
  137. case 188 : // 천의 동굴(진노)
  138. case 189 : // 천의 동굴 2층(진노)
  139. #endif
  140. // case 206 : // 아귀동굴
  141. case 216 : // 아귀동굴
  142. case 217 : // 거미 던전 3층
  143. case 208 : // 천의 동굴 (용방)
  144. return false;
  145. }
  146.  
  147. if (CBattleArena::IsBattleArenaMap(map_index)) return false;
  148.  
  149. // 모든 private 맵으론 워프 불가능
  150. if (map_index > 10000) return false;
  151.  
  152. return true;
  153. }
  154.  
  155. bool IS_BOTARYABLE_ZONE(int nMapIndex)
  156. {
  157. if (LC_IsYMIR() == false && LC_IsKorea() == false) return true;
  158.  
  159. switch (nMapIndex)
  160. {
  161. case 1 :
  162. case 3 :
  163. case 21 :
  164. case 23 :
  165. case 41 :
  166. case 43 :
  167. return true;
  168. }
  169.  
  170. return false;
  171. }
  172.  
  173. // item socket 이 프로토타입과 같은지 체크 -- by mhh
  174. static bool FN_check_item_socket(LPITEM item)
  175. {
  176. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  177. {
  178. if (item->GetSocket(i) != item->GetProto()->alSockets[i])
  179. return false;
  180. }
  181.  
  182. return true;
  183. }
  184.  
  185. // item socket 복사 -- by mhh
  186. static void FN_copy_item_socket(LPITEM dest, LPITEM src)
  187. {
  188. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  189. {
  190. dest->SetSocket(i, src->GetSocket(i));
  191. }
  192. }
  193. static bool FN_check_item_sex(LPCHARACTER ch, LPITEM item)
  194. {
  195. // 남자 금지
  196. if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_MALE))
  197. {
  198. if (SEX_MALE==GET_SEX(ch))
  199. return false;
  200. }
  201. // 여자금지
  202. if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_FEMALE))
  203. {
  204. if (SEX_FEMALE==GET_SEX(ch))
  205. return false;
  206. }
  207.  
  208. return true;
  209. }
  210.  
  211.  
  212. /////////////////////////////////////////////////////////////////////////////
  213. // ITEM HANDLING
  214. /////////////////////////////////////////////////////////////////////////////
  215. bool CHARACTER::CanHandleItem(bool bSkipCheckRefine, bool bSkipObserver)
  216. {
  217. if (!bSkipObserver)
  218. if (m_bIsObserver)
  219. return false;
  220.  
  221. if (GetMyShop())
  222. return false;
  223.  
  224. if (!bSkipCheckRefine)
  225. if (m_bUnderRefine)
  226. return false;
  227.  
  228. if (IsCubeOpen() || NULL != DragonSoul_RefineWindow_GetOpener())
  229. return false;
  230.  
  231. if (IsWarping())
  232. return false;
  233.  
  234. return true;
  235. }
  236.  
  237. LPITEM CHARACTER::GetInventoryItem(WORD wCell) const
  238. {
  239. return GetItem(TItemPos(INVENTORY, wCell));
  240. }
  241. LPITEM CHARACTER::GetItem(TItemPos Cell) const
  242. {
  243. if (!IsValidItemPosition(Cell))
  244. return NULL;
  245. WORD wCell = Cell.cell;
  246. BYTE window_type = Cell.window_type;
  247. switch (window_type)
  248. {
  249. case INVENTORY:
  250. case EQUIPMENT:
  251. if (wCell >= INVENTORY_AND_EQUIP_SLOT_MAX)
  252. {
  253. sys_err("CHARACTER::GetInventoryItem: invalid item cell %d", wCell);
  254. return NULL;
  255. }
  256. return m_pointsInstant.pItems[wCell];
  257. case DRAGON_SOUL_INVENTORY:
  258. if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM)
  259. {
  260. sys_err("CHARACTER::GetInventoryItem: invalid DS item cell %d", wCell);
  261. return NULL;
  262. }
  263. return m_pointsInstant.pDSItems[wCell];
  264.  
  265. #ifdef ENABLE_SWITCHBOT
  266. case SWITCHBOT:
  267. if (wCell >= SWITCHBOT_SLOT_COUNT)
  268. {
  269. sys_err("CHARACTER::GetInventoryItem: invalid switchbot item cell %d", wCell);
  270. return NULL;
  271. }
  272. return m_pointsInstant.pSwitchbotItems[wCell];
  273. #endif
  274.  
  275. default:
  276. return NULL;
  277. }
  278. return NULL;
  279. }
  280.  
  281. void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem)
  282. {
  283. WORD wCell = Cell.cell;
  284. BYTE window_type = Cell.window_type;
  285. if ((unsigned long)((CItem*)pItem) == 0xff || (unsigned long)((CItem*)pItem) == 0xffffffff)
  286. {
  287. sys_err("!!! FATAL ERROR !!! item == 0xff (char: %s cell: %u)", GetName(), wCell);
  288. core_dump();
  289. return;
  290. }
  291.  
  292. if (pItem && pItem->GetOwner())
  293. {
  294. assert(!"GetOwner exist");
  295. return;
  296. }
  297. // 기본 인벤토리
  298. switch(window_type)
  299. {
  300. case INVENTORY:
  301. case EQUIPMENT:
  302. {
  303. if (wCell >= INVENTORY_AND_EQUIP_SLOT_MAX)
  304. {
  305. sys_err("CHARACTER::SetItem: invalid item cell %d", wCell);
  306. return;
  307. }
  308.  
  309. LPITEM pOld = m_pointsInstant.pItems[wCell];
  310.  
  311. if (pOld)
  312. {
  313. if (wCell < INVENTORY_MAX_NUM)
  314. {
  315. for (int i = 0; i < pOld->GetSize(); ++i)
  316. {
  317. int p = wCell + (i * 5);
  318.  
  319. if (p >= INVENTORY_MAX_NUM)
  320. continue;
  321.  
  322. if (m_pointsInstant.pItems[p] && m_pointsInstant.pItems[p] != pOld)
  323. continue;
  324.  
  325. m_pointsInstant.bItemGrid[p] = 0;
  326. }
  327. }
  328. else
  329. m_pointsInstant.bItemGrid[wCell] = 0;
  330. }
  331.  
  332. if (pItem)
  333. {
  334. if (wCell < INVENTORY_MAX_NUM)
  335. {
  336. for (int i = 0; i < pItem->GetSize(); ++i)
  337. {
  338. int p = wCell + (i * 5);
  339.  
  340. if (p >= INVENTORY_MAX_NUM)
  341. continue;
  342.  
  343. // wCell + 1 로 하는 것은 빈곳을 체크할 때 같은
  344. // 아이템은 예외처리하기 위함
  345. m_pointsInstant.bItemGrid[p] = wCell + 1;
  346. }
  347. }
  348. else
  349. m_pointsInstant.bItemGrid[wCell] = wCell + 1;
  350. }
  351.  
  352. m_pointsInstant.pItems[wCell] = pItem;
  353. }
  354. break;
  355. // 용혼석 인벤토리
  356. case DRAGON_SOUL_INVENTORY:
  357. {
  358. LPITEM pOld = m_pointsInstant.pDSItems[wCell];
  359.  
  360. if (pOld)
  361. {
  362. if (wCell < DRAGON_SOUL_INVENTORY_MAX_NUM)
  363. {
  364. for (int i = 0; i < pOld->GetSize(); ++i)
  365. {
  366. int p = wCell + (i * DRAGON_SOUL_BOX_COLUMN_NUM);
  367.  
  368. if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM)
  369. continue;
  370.  
  371. if (m_pointsInstant.pDSItems[p] && m_pointsInstant.pDSItems[p] != pOld)
  372. continue;
  373.  
  374. m_pointsInstant.wDSItemGrid[p] = 0;
  375. }
  376. }
  377. else
  378. m_pointsInstant.wDSItemGrid[wCell] = 0;
  379. }
  380.  
  381. if (pItem)
  382. {
  383. if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM)
  384. {
  385. sys_err("CHARACTER::SetItem: invalid DS item cell %d", wCell);
  386. return;
  387. }
  388.  
  389. if (wCell < DRAGON_SOUL_INVENTORY_MAX_NUM)
  390. {
  391. for (int i = 0; i < pItem->GetSize(); ++i)
  392. {
  393. int p = wCell + (i * DRAGON_SOUL_BOX_COLUMN_NUM);
  394.  
  395. if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM)
  396. continue;
  397.  
  398. // wCell + 1 로 하는 것은 빈곳을 체크할 때 같은
  399. // 아이템은 예외처리하기 위함
  400. m_pointsInstant.wDSItemGrid[p] = wCell + 1;
  401. }
  402. }
  403. else
  404. m_pointsInstant.wDSItemGrid[wCell] = wCell + 1;
  405. }
  406.  
  407. m_pointsInstant.pDSItems[wCell] = pItem;
  408. }
  409. break;
  410.  
  411. #ifdef ENABLE_SWITCHBOT
  412. case SWITCHBOT:
  413. {
  414. LPITEM pOld = m_pointsInstant.pSwitchbotItems[wCell];
  415. if (pItem && pOld)
  416. {
  417. return;
  418. }
  419.  
  420. if (wCell >= SWITCHBOT_SLOT_COUNT)
  421. {
  422. sys_err("CHARACTER::SetItem: invalid switchbot item cell %d", wCell);
  423. return;
  424. }
  425.  
  426. if (pItem)
  427. {
  428. CSwitchbotManager::Instance().RegisterItem(GetPlayerID(), pItem->GetID(), wCell);
  429. }
  430. else
  431. {
  432. CSwitchbotManager::Instance().UnregisterItem(GetPlayerID(), wCell);
  433. }
  434.  
  435. m_pointsInstant.pSwitchbotItems[wCell] = pItem;
  436. }
  437. break;
  438. #endif
  439.  
  440.  
  441. default:
  442. sys_err ("Invalid Inventory type %d", window_type);
  443. return;
  444. }
  445.  
  446. if (GetDesc())
  447. {
  448. // 확장 아이템: 서버에서 아이템 플래그 정보를 보낸다
  449. if (pItem)
  450. {
  451. TPacketGCItemSet pack;
  452. pack.header = HEADER_GC_ITEM_SET;
  453. pack.Cell = Cell;
  454.  
  455. pack.count = pItem->GetCount();
  456. pack.vnum = pItem->GetVnum();
  457. pack.flags = pItem->GetFlag();
  458. pack.anti_flags = pItem->GetAntiFlag();
  459. pack.highlight = (Cell.window_type == DRAGON_SOUL_INVENTORY);
  460.  
  461.  
  462. thecore_memcpy(pack.alSockets, pItem->GetSockets(), sizeof(pack.alSockets));
  463. thecore_memcpy(pack.aAttr, pItem->GetAttributes(), sizeof(pack.aAttr));
  464.  
  465. GetDesc()->Packet(&pack, sizeof(TPacketGCItemSet));
  466. }
  467. else
  468. {
  469. TPacketGCItemDelDeprecated pack;
  470. pack.header = HEADER_GC_ITEM_DEL;
  471. pack.Cell = Cell;
  472. pack.count = 0;
  473. pack.vnum = 0;
  474. memset(pack.alSockets, 0, sizeof(pack.alSockets));
  475. memset(pack.aAttr, 0, sizeof(pack.aAttr));
  476.  
  477. GetDesc()->Packet(&pack, sizeof(TPacketGCItemDelDeprecated));
  478. }
  479. }
  480.  
  481. if (pItem)
  482. {
  483. pItem->SetCell(this, wCell);
  484. switch (window_type)
  485. {
  486. case INVENTORY:
  487. case EQUIPMENT:
  488. if ((wCell < INVENTORY_MAX_NUM) || (BELT_INVENTORY_SLOT_START <= wCell && BELT_INVENTORY_SLOT_END > wCell))
  489. pItem->SetWindow(INVENTORY);
  490. else
  491. pItem->SetWindow(EQUIPMENT);
  492. break;
  493. case DRAGON_SOUL_INVENTORY:
  494. pItem->SetWindow(DRAGON_SOUL_INVENTORY);
  495. break;
  496. #ifdef ENABLE_SWITCHBOT
  497. case SWITCHBOT:
  498. pItem->SetWindow(SWITCHBOT);
  499. break;
  500. #endif
  501. }
  502. }
  503. }
  504.  
  505. LPITEM CHARACTER::GetWear(BYTE bCell) const
  506. {
  507. // > WEAR_MAX_NUM : 용혼석 슬롯들.
  508. if (bCell >= WEAR_MAX_NUM + DRAGON_SOUL_DECK_MAX_NUM * DS_SLOT_MAX)
  509. {
  510. sys_err("CHARACTER::GetWear: invalid wear cell %d", bCell);
  511. return NULL;
  512. }
  513.  
  514. return m_pointsInstant.pItems[INVENTORY_MAX_NUM + bCell];
  515. }
  516.  
  517. void CHARACTER::SetWear(BYTE bCell, LPITEM item)
  518. {
  519. if (bCell >= WEAR_MAX_NUM + DRAGON_SOUL_DECK_MAX_NUM * DS_SLOT_MAX)
  520. {
  521. sys_err("CHARACTER::SetItem: invalid item cell %d", bCell);
  522. return;
  523. }
  524.  
  525. SetItem(TItemPos (INVENTORY, INVENTORY_MAX_NUM + bCell), item);
  526.  
  527. if (!item && bCell == WEAR_WEAPON)
  528. {
  529. if (IsAffectFlag(AFF_GWIGUM))
  530. RemoveAffect(SKILL_GWIGEOM);
  531.  
  532. if (IsAffectFlag(AFF_GEOMGYEONG))
  533. RemoveAffect(SKILL_GEOMKYUNG);
  534. }
  535. #ifdef ENABLE_MOUNT_LIKE_HORSE
  536. if (item)
  537. CalcMountBonusBySeal(item);
  538. #endif
  539. }
  540.  
  541. void CHARACTER::ClearItem()
  542. {
  543. int i;
  544. LPITEM item;
  545.  
  546. for (i = 0; i < INVENTORY_AND_EQUIP_SLOT_MAX; ++i)
  547. {
  548. if ((item = GetInventoryItem(i)))
  549. {
  550. item->SetSkipSave(true);
  551. ITEM_MANAGER::instance().FlushDelayedSave(item);
  552.  
  553. item->RemoveFromCharacter();
  554. M2_DESTROY_ITEM(item);
  555.  
  556. SyncQuickslot(QUICKSLOT_TYPE_ITEM, i, 255);
  557. }
  558. }
  559. for (i = 0; i < DRAGON_SOUL_INVENTORY_MAX_NUM; ++i)
  560. {
  561. if ((item = GetItem(TItemPos(DRAGON_SOUL_INVENTORY, i))))
  562. {
  563. item->SetSkipSave(true);
  564. ITEM_MANAGER::instance().FlushDelayedSave(item);
  565.  
  566. item->RemoveFromCharacter();
  567. M2_DESTROY_ITEM(item);
  568. }
  569. }
  570. #ifdef ENABLE_SWITCHBOT
  571. for (i = 0; i < SWITCHBOT_SLOT_COUNT; ++i)
  572. {
  573. if ((item = GetItem(TItemPos(SWITCHBOT, i))))
  574. {
  575. item->SetSkipSave(true);
  576. ITEM_MANAGER::instance().FlushDelayedSave(item);
  577.  
  578. item->RemoveFromCharacter();
  579. M2_DESTROY_ITEM(item);
  580. }
  581. }
  582. #endif
  583. }
  584.  
  585. bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, BYTE bSize, int iExceptionCell) const
  586. {
  587. switch (Cell.window_type)
  588. {
  589. case INVENTORY:
  590. {
  591. BYTE bCell = Cell.cell;
  592.  
  593. ++iExceptionCell;
  594.  
  595. if (Cell.IsBeltInventoryPosition())
  596. {
  597. LPITEM beltItem = GetWear(WEAR_BELT);
  598.  
  599. if (NULL == beltItem)
  600. return false;
  601.  
  602. if (false == CBeltInventoryHelper::IsAvailableCell(bCell - BELT_INVENTORY_SLOT_START, beltItem->GetValue(0)))
  603. return false;
  604.  
  605. if (m_pointsInstant.bItemGrid[bCell])
  606. {
  607. if (m_pointsInstant.bItemGrid[bCell] == iExceptionCell)
  608. return true;
  609.  
  610. return false;
  611. }
  612.  
  613. if (bSize == 1)
  614. return true;
  615.  
  616. }
  617. else if (bCell >= INVENTORY_MAX_NUM)
  618. return false;
  619.  
  620. if (m_pointsInstant.bItemGrid[bCell])
  621. {
  622. if (m_pointsInstant.bItemGrid[bCell] == iExceptionCell)
  623. {
  624. if (bSize == 1)
  625. return true;
  626.  
  627. int j = 1;
  628. BYTE bPage = bCell / (INVENTORY_MAX_NUM / 2);
  629.  
  630. do
  631. {
  632. BYTE p = bCell + (5 * j);
  633.  
  634. if (p >= INVENTORY_MAX_NUM)
  635. return false;
  636.  
  637. if (p / (INVENTORY_MAX_NUM / 2) != bPage)
  638. return false;
  639.  
  640. if (m_pointsInstant.bItemGrid[p])
  641. if (m_pointsInstant.bItemGrid[p] != iExceptionCell)
  642. return false;
  643. }
  644. while (++j < bSize);
  645.  
  646. return true;
  647. }
  648. else
  649. return false;
  650. }
  651.  
  652. // 크기가 1이면 한칸을 차지하는 것이므로 그냥 리턴
  653. if (1 == bSize)
  654. return true;
  655. else
  656. {
  657. int j = 1;
  658. BYTE bPage = bCell / (INVENTORY_MAX_NUM / 2);
  659.  
  660. do
  661. {
  662. BYTE p = bCell + (5 * j);
  663.  
  664. if (p >= INVENTORY_MAX_NUM)
  665. return false;
  666.  
  667. if (p / (INVENTORY_MAX_NUM / 2) != bPage)
  668. return false;
  669.  
  670. if (m_pointsInstant.bItemGrid[p])
  671. if (m_pointsInstant.bItemGrid[p] != iExceptionCell)
  672. return false;
  673. }
  674. while (++j < bSize);
  675.  
  676. return true;
  677. }
  678. }
  679. break;
  680. case DRAGON_SOUL_INVENTORY:
  681. {
  682. WORD wCell = Cell.cell;
  683. if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM)
  684. return false;
  685.  
  686. iExceptionCell++;
  687.  
  688. if (m_pointsInstant.wDSItemGrid[wCell])
  689. {
  690. if (m_pointsInstant.wDSItemGrid[wCell] == iExceptionCell)
  691. {
  692. if (bSize == 1)
  693. return true;
  694.  
  695. int j = 1;
  696.  
  697. do
  698. {
  699. BYTE p = wCell + (DRAGON_SOUL_BOX_COLUMN_NUM * j);
  700.  
  701. if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM)
  702. return false;
  703.  
  704. if (m_pointsInstant.wDSItemGrid[p])
  705. if (m_pointsInstant.wDSItemGrid[p] != iExceptionCell)
  706. return false;
  707. }
  708. while (++j < bSize);
  709.  
  710. return true;
  711. }
  712. else
  713. return false;
  714. }
  715.  
  716. if (1 == bSize)
  717. return true;
  718. else
  719. {
  720. int j = 1;
  721.  
  722. do
  723. {
  724. BYTE p = wCell + (DRAGON_SOUL_BOX_COLUMN_NUM * j);
  725.  
  726. if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM)
  727. return false;
  728.  
  729. if (m_pointsInstant.bItemGrid[p])
  730. if (m_pointsInstant.wDSItemGrid[p] != iExceptionCell)
  731. return false;
  732. }
  733. while (++j < bSize);
  734.  
  735. return true;
  736. }
  737. }
  738. #ifdef ENABLE_SWITCHBOT
  739. case SWITCHBOT:
  740. {
  741. WORD wCell = Cell.cell;
  742. if (wCell >= SWITCHBOT_SLOT_COUNT)
  743. {
  744. return false;
  745. }
  746.  
  747. if (m_pointsInstant.pSwitchbotItems[wCell])
  748. {
  749. return false;
  750. }
  751.  
  752. return true;
  753. }
  754. #endif
  755. }
  756. }
  757.  
  758. int CHARACTER::GetEmptyInventory(BYTE size) const
  759. {
  760. for ( int i = 0; i < INVENTORY_MAX_NUM; ++i)
  761. if (IsEmptyItemGrid(TItemPos (INVENTORY, i), size))
  762. return i;
  763. return -1;
  764. }
  765.  
  766. int CHARACTER::GetEmptyDragonSoulInventory(LPITEM pItem) const
  767. {
  768. if (NULL == pItem || !pItem->IsDragonSoul())
  769. return -1;
  770. if (!DragonSoul_IsQualified())
  771. {
  772. return -1;
  773. }
  774. BYTE bSize = pItem->GetSize();
  775. WORD wBaseCell = DSManager::instance().GetBasePosition(pItem);
  776.  
  777. if (WORD_MAX == wBaseCell)
  778. return -1;
  779.  
  780. for (int i = 0; i < DRAGON_SOUL_BOX_SIZE; ++i)
  781. if (IsEmptyItemGrid(TItemPos(DRAGON_SOUL_INVENTORY, i + wBaseCell), bSize))
  782. return i + wBaseCell;
  783.  
  784. return -1;
  785. }
  786.  
  787. void CHARACTER::CopyDragonSoulItemGrid(std::vector<WORD>& vDragonSoulItemGrid) const
  788. {
  789. vDragonSoulItemGrid.resize(DRAGON_SOUL_INVENTORY_MAX_NUM);
  790.  
  791. std::copy(m_pointsInstant.wDSItemGrid, m_pointsInstant.wDSItemGrid + DRAGON_SOUL_INVENTORY_MAX_NUM, vDragonSoulItemGrid.begin());
  792. }
  793.  
  794. int CHARACTER::CountEmptyInventory() const
  795. {
  796. int count = 0;
  797.  
  798. for (int i = 0; i < INVENTORY_MAX_NUM; ++i)
  799. if (GetInventoryItem(i))
  800. count += GetInventoryItem(i)->GetSize();
  801.  
  802. return (INVENTORY_MAX_NUM - count);
  803. }
  804.  
  805. void TransformRefineItem(LPITEM pkOldItem, LPITEM pkNewItem)
  806. {
  807. // ACCESSORY_REFINE
  808. if (pkOldItem->IsAccessoryForSocket())
  809. {
  810. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  811. {
  812. pkNewItem->SetSocket(i, pkOldItem->GetSocket(i));
  813. }
  814. //pkNewItem->StartAccessorySocketExpireEvent();
  815. }
  816. // END_OF_ACCESSORY_REFINE
  817. else
  818. {
  819. // 여기서 깨진석이 자동적으로 청소 됨
  820. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  821. {
  822. if (!pkOldItem->GetSocket(i))
  823. break;
  824. else
  825. pkNewItem->SetSocket(i, 1);
  826. }
  827.  
  828. // 소켓 설정
  829. int slot = 0;
  830.  
  831. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  832. {
  833. long socket = pkOldItem->GetSocket(i);
  834.  
  835. if (socket > 2 && socket != ITEM_BROKEN_METIN_VNUM)
  836. pkNewItem->SetSocket(slot++, socket);
  837. }
  838.  
  839. }
  840.  
  841. // 매직 아이템 설정
  842. pkOldItem->CopyAttributeTo(pkNewItem);
  843. }
  844.  
  845. void NotifyRefineSuccess(LPCHARACTER ch, LPITEM item, const char* way)
  846. {
  847. if (NULL != ch && item != NULL)
  848. {
  849. ch->ChatPacket(CHAT_TYPE_COMMAND, "RefineSuceeded");
  850.  
  851. LogManager::instance().RefineLog(ch->GetPlayerID(), item->GetName(), item->GetID(), item->GetRefineLevel(), 1, way);
  852. }
  853. }
  854.  
  855. void NotifyRefineFail(LPCHARACTER ch, LPITEM item, const char* way, int success = 0)
  856. {
  857. if (NULL != ch && NULL != item)
  858. {
  859. ch->ChatPacket(CHAT_TYPE_COMMAND, "RefineFailed");
  860.  
  861. LogManager::instance().RefineLog(ch->GetPlayerID(), item->GetName(), item->GetID(), item->GetRefineLevel(), success, way);
  862. }
  863. }
  864.  
  865. void CHARACTER::SetRefineNPC(LPCHARACTER ch)
  866. {
  867. if ( ch != NULL )
  868. {
  869. m_dwRefineNPCVID = ch->GetVID();
  870. }
  871. else
  872. {
  873. m_dwRefineNPCVID = 0;
  874. }
  875. }
  876.  
  877. bool CHARACTER::DoRefine(LPITEM item, bool bMoneyOnly)
  878. {
  879. if (!CanHandleItem(true))
  880. {
  881. ClearRefineMode();
  882. return false;
  883. }
  884.  
  885. //개량 시간제한 : upgrade_refine_scroll.quest 에서 개량후 5분이내에 일반 개량을
  886. //진행할수 없음
  887. if (quest::CQuestManager::instance().GetEventFlag("update_refine_time") != 0)
  888. {
  889. if (get_global_time() < quest::CQuestManager::instance().GetEventFlag("update_refine_time") + (60 * 5))
  890. {
  891. sys_log(0, "can't refine %d %s", GetPlayerID(), GetName());
  892. return false;
  893. }
  894. }
  895.  
  896. const TRefineTable * prt = CRefineManager::instance().GetRefineRecipe(item->GetRefineSet());
  897.  
  898. if (!prt)
  899. return false;
  900.  
  901. DWORD result_vnum = item->GetRefinedVnum();
  902.  
  903. // REFINE_COST
  904. int cost = ComputeRefineFee(prt->cost);
  905.  
  906. int RefineChance = GetQuestFlag("main_quest_lv7.refine_chance");
  907.  
  908. if (RefineChance > 0)
  909. {
  910. if (!item->CheckItemUseLevel(20) || item->GetType() != ITEM_WEAPON)
  911. {
  912. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("무료 개량 기회는 20 이하의 무기만 가능합니다"));
  913. return false;
  914. }
  915.  
  916. cost = 0;
  917. SetQuestFlag("main_quest_lv7.refine_chance", RefineChance - 1);
  918. }
  919. // END_OF_REFINE_COST
  920.  
  921. if (result_vnum == 0)
  922. {
  923. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("더 이상 개량할 수 없습니다."));
  924. return false;
  925. }
  926.  
  927. if (item->GetType() == ITEM_USE && item->GetSubType() == USE_TUNING)
  928. return false;
  929.  
  930. TItemTable * pProto = ITEM_MANAGER::instance().GetTable(item->GetRefinedVnum());
  931.  
  932. if (!pProto)
  933. {
  934. sys_err("DoRefine NOT GET ITEM PROTO %d", item->GetRefinedVnum());
  935. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템은 개량할 수 없습니다."));
  936. return false;
  937. }
  938.  
  939. // Check level limit in korea only
  940. if (!g_iUseLocale)
  941. {
  942. for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i)
  943. {
  944. long limit = pProto->aLimits[i].lValue;
  945.  
  946. switch (pProto->aLimits[i].bType)
  947. {
  948. case LIMIT_LEVEL:
  949. if (GetLevel() < limit)
  950. {
  951. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("개량된 후 아이템의 레벨 제한보다 레벨이 낮습니다."));
  952. return false;
  953. }
  954. break;
  955. }
  956. }
  957. }
  958.  
  959. // REFINE_COST
  960. if (GetGold() < cost)
  961. {
  962. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("개량을 하기 위한 돈이 부족합니다."));
  963. return false;
  964. }
  965.  
  966. if (!bMoneyOnly && !RefineChance)
  967. {
  968. for (int i = 0; i < prt->material_count; ++i)
  969. {
  970. if (CountSpecifyItem(prt->materials[i].vnum) < prt->materials[i].count)
  971. {
  972. if (test_server)
  973. {
  974. ChatPacket(CHAT_TYPE_INFO, "Find %d, count %d, require %d", prt->materials[i].vnum, CountSpecifyItem(prt->materials[i].vnum), prt->materials[i].count);
  975. }
  976. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("개량을 하기 위한 재료가 부족합니다."));
  977. return false;
  978. }
  979. }
  980.  
  981. for (int i = 0; i < prt->material_count; ++i)
  982. RemoveSpecifyItem(prt->materials[i].vnum, prt->materials[i].count);
  983. }
  984.  
  985. int prob = number(1, 100);
  986.  
  987. if (IsRefineThroughGuild() || bMoneyOnly)
  988. prob -= 10;
  989.  
  990. // END_OF_REFINE_COST
  991.  
  992. if (prob <= prt->prob)
  993. {
  994. // 성공! 모든 아이템이 사라지고, 같은 속성의 다른 아이템 획득
  995. LPITEM pkNewItem = ITEM_MANAGER::instance().CreateItem(result_vnum, 1, 0, false);
  996.  
  997. if (pkNewItem)
  998. {
  999. ITEM_MANAGER::CopyAllAttrTo(item, pkNewItem);
  1000. LogManager::instance().ItemLog(this, pkNewItem, "REFINE SUCCESS", pkNewItem->GetName());
  1001.  
  1002. BYTE bCell = item->GetCell();
  1003.  
  1004. // DETAIL_REFINE_LOG
  1005. NotifyRefineSuccess(this, item, IsRefineThroughGuild() ? "GUILD" : "POWER");
  1006. DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, item->GetVnum(), -cost);
  1007. ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (REFINE SUCCESS)");
  1008. // END_OF_DETAIL_REFINE_LOG
  1009.  
  1010. pkNewItem->AddToCharacter(this, TItemPos(INVENTORY, bCell));
  1011. ITEM_MANAGER::instance().FlushDelayedSave(pkNewItem);
  1012.  
  1013. sys_log(0, "Refine Success %d", cost);
  1014. pkNewItem->AttrLog();
  1015. //PointChange(POINT_GOLD, -cost);
  1016. sys_log(0, "PayPee %d", cost);
  1017. PayRefineFee(cost);
  1018. sys_log(0, "PayPee End %d", cost);
  1019. }
  1020. else
  1021. {
  1022. // DETAIL_REFINE_LOG
  1023. // 아이템 생성에 실패 -> 개량 실패로 간주
  1024. sys_err("cannot create item %u", result_vnum);
  1025. NotifyRefineFail(this, item, IsRefineThroughGuild() ? "GUILD" : "POWER");
  1026. // END_OF_DETAIL_REFINE_LOG
  1027. }
  1028. }
  1029. else
  1030. {
  1031. // 실패! 모든 아이템이 사라짐.
  1032. DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, item->GetVnum(), -cost);
  1033. NotifyRefineFail(this, item, IsRefineThroughGuild() ? "GUILD" : "POWER");
  1034. item->AttrLog();
  1035. ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (REFINE FAIL)");
  1036.  
  1037. //PointChange(POINT_GOLD, -cost);
  1038. PayRefineFee(cost);
  1039. }
  1040.  
  1041. return true;
  1042. }
  1043.  
  1044. enum enum_RefineScrolls
  1045. {
  1046. CHUKBOK_SCROLL = 0,
  1047. HYUNIRON_CHN = 1, // 중국에서만 사용
  1048. YONGSIN_SCROLL = 2,
  1049. MUSIN_SCROLL = 3,
  1050. YAGONG_SCROLL = 4,
  1051. MEMO_SCROLL = 5,
  1052. BDRAGON_SCROLL = 6,
  1053. };
  1054.  
  1055. bool CHARACTER::DoRefineWithScroll(LPITEM item)
  1056. {
  1057. if (!CanHandleItem(true))
  1058. {
  1059. ClearRefineMode();
  1060. return false;
  1061. }
  1062.  
  1063. ClearRefineMode();
  1064.  
  1065. //개량 시간제한 : upgrade_refine_scroll.quest 에서 개량후 5분이내에 일반 개량을
  1066. //진행할수 없음
  1067. if (quest::CQuestManager::instance().GetEventFlag("update_refine_time") != 0)
  1068. {
  1069. if (get_global_time() < quest::CQuestManager::instance().GetEventFlag("update_refine_time") + (60 * 5))
  1070. {
  1071. sys_log(0, "can't refine %d %s", GetPlayerID(), GetName());
  1072. return false;
  1073. }
  1074. }
  1075.  
  1076. const TRefineTable * prt = CRefineManager::instance().GetRefineRecipe(item->GetRefineSet());
  1077.  
  1078. if (!prt)
  1079. return false;
  1080.  
  1081. LPITEM pkItemScroll;
  1082.  
  1083. // 개량서 체크
  1084. if (m_iRefineAdditionalCell < 0)
  1085. return false;
  1086.  
  1087. pkItemScroll = GetInventoryItem(m_iRefineAdditionalCell);
  1088.  
  1089. if (!pkItemScroll)
  1090. return false;
  1091.  
  1092. if (!(pkItemScroll->GetType() == ITEM_USE && pkItemScroll->GetSubType() == USE_TUNING))
  1093. return false;
  1094.  
  1095. if (pkItemScroll->GetVnum() == item->GetVnum())
  1096. return false;
  1097.  
  1098. DWORD result_vnum = item->GetRefinedVnum();
  1099. DWORD result_fail_vnum = item->GetRefineFromVnum();
  1100.  
  1101. if (result_vnum == 0)
  1102. {
  1103. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("더 이상 개량할 수 없습니다."));
  1104. return false;
  1105. }
  1106.  
  1107. // MUSIN_SCROLL
  1108. if (pkItemScroll->GetValue(0) == MUSIN_SCROLL)
  1109. {
  1110. if (item->GetRefineLevel() >= 4)
  1111. {
  1112. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 개량서로 더 이상 개량할 수 없습니다."));
  1113. return false;
  1114. }
  1115. }
  1116. // END_OF_MUSIC_SCROLL
  1117.  
  1118. else if (pkItemScroll->GetValue(0) == MEMO_SCROLL)
  1119. {
  1120. if (item->GetRefineLevel() != pkItemScroll->GetValue(1))
  1121. {
  1122. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 개량서로 개량할 수 없습니다."));
  1123. return false;
  1124. }
  1125. }
  1126. else if (pkItemScroll->GetValue(0) == BDRAGON_SCROLL)
  1127. {
  1128. if (item->GetType() != ITEM_METIN || item->GetRefineLevel() != 4)
  1129. {
  1130. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템으로 개량할 수 없습니다."));
  1131. return false;
  1132. }
  1133. }
  1134.  
  1135. TItemTable * pProto = ITEM_MANAGER::instance().GetTable(item->GetRefinedVnum());
  1136.  
  1137. if (!pProto)
  1138. {
  1139. sys_err("DoRefineWithScroll NOT GET ITEM PROTO %d", item->GetRefinedVnum());
  1140. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템은 개량할 수 없습니다."));
  1141. return false;
  1142. }
  1143.  
  1144. // Check level limit in korea only
  1145. if (!g_iUseLocale)
  1146. {
  1147. for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i)
  1148. {
  1149. long limit = pProto->aLimits[i].lValue;
  1150.  
  1151. switch (pProto->aLimits[i].bType)
  1152. {
  1153. case LIMIT_LEVEL:
  1154. if (GetLevel() < limit)
  1155. {
  1156. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("개량된 후 아이템의 레벨 제한보다 레벨이 낮습니다."));
  1157. return false;
  1158. }
  1159. break;
  1160. }
  1161. }
  1162. }
  1163.  
  1164. if (GetGold() < prt->cost)
  1165. {
  1166. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("개량을 하기 위한 돈이 부족합니다."));
  1167. return false;
  1168. }
  1169.  
  1170. for (int i = 0; i < prt->material_count; ++i)
  1171. {
  1172. if (CountSpecifyItem(prt->materials[i].vnum) < prt->materials[i].count)
  1173. {
  1174. if (test_server)
  1175. {
  1176. ChatPacket(CHAT_TYPE_INFO, "Find %d, count %d, require %d", prt->materials[i].vnum, CountSpecifyItem(prt->materials[i].vnum), prt->materials[i].count);
  1177. }
  1178. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("개량을 하기 위한 재료가 부족합니다."));
  1179. return false;
  1180. }
  1181. }
  1182.  
  1183. for (int i = 0; i < prt->material_count; ++i)
  1184. RemoveSpecifyItem(prt->materials[i].vnum, prt->materials[i].count);
  1185.  
  1186. int prob = number(1, 100);
  1187. int success_prob = prt->prob;
  1188. bool bDestroyWhenFail = false;
  1189.  
  1190. const char* szRefineType = "SCROLL";
  1191.  
  1192. if (pkItemScroll->GetValue(0) == HYUNIRON_CHN ||
  1193. pkItemScroll->GetValue(0) == YONGSIN_SCROLL ||
  1194. pkItemScroll->GetValue(0) == YAGONG_SCROLL) // 현철, 용신의 축복서, 야공의 비전서 처리
  1195. {
  1196. const char hyuniron_prob[9] = { 100, 75, 65, 55, 45, 40, 35, 25, 20 };
  1197. const char hyuniron_prob_euckr[9] = { 100, 75, 65, 55, 45, 40, 35, 30, 25 };
  1198.  
  1199. const char yagong_prob[9] = { 100, 100, 90, 80, 70, 60, 50, 30, 20 };
  1200. const char yagong_prob_euckr[9] = { 100, 100, 90, 80, 70, 60, 50, 40, 30 };
  1201.  
  1202. if (pkItemScroll->GetValue(0) == YONGSIN_SCROLL)
  1203. {
  1204. if (LC_IsYMIR() == true || LC_IsKorea() == true)
  1205. success_prob = hyuniron_prob_euckr[MINMAX(0, item->GetRefineLevel(), 8)];
  1206. else
  1207. success_prob = hyuniron_prob[MINMAX(0, item->GetRefineLevel(), 8)];
  1208. }
  1209. else if (pkItemScroll->GetValue(0) == YAGONG_SCROLL)
  1210. {
  1211. if (LC_IsYMIR() == true || LC_IsKorea() == true)
  1212. success_prob = yagong_prob_euckr[MINMAX(0, item->GetRefineLevel(), 8)];
  1213. else
  1214. success_prob = yagong_prob[MINMAX(0, item->GetRefineLevel(), 8)];
  1215. }
  1216. else
  1217. {
  1218. sys_err("REFINE : Unknown refine scroll item. Value0: %d", pkItemScroll->GetValue(0));
  1219. }
  1220.  
  1221. if (test_server)
  1222. {
  1223. ChatPacket(CHAT_TYPE_INFO, "[Only Test] Success_Prob %d, RefineLevel %d ", success_prob, item->GetRefineLevel());
  1224. }
  1225. if (pkItemScroll->GetValue(0) == HYUNIRON_CHN) // 현철은 아이템이 부서져야 한다.
  1226. bDestroyWhenFail = true;
  1227.  
  1228. // DETAIL_REFINE_LOG
  1229. if (pkItemScroll->GetValue(0) == HYUNIRON_CHN)
  1230. {
  1231. szRefineType = "HYUNIRON";
  1232. }
  1233. else if (pkItemScroll->GetValue(0) == YONGSIN_SCROLL)
  1234. {
  1235. szRefineType = "GOD_SCROLL";
  1236. }
  1237. else if (pkItemScroll->GetValue(0) == YAGONG_SCROLL)
  1238. {
  1239. szRefineType = "YAGONG_SCROLL";
  1240. }
  1241. // END_OF_DETAIL_REFINE_LOG
  1242. }
  1243.  
  1244. // DETAIL_REFINE_LOG
  1245. if (pkItemScroll->GetValue(0) == MUSIN_SCROLL) // 무신의 축복서는 100% 성공 (+4까지만)
  1246. {
  1247. success_prob = 100;
  1248.  
  1249. szRefineType = "MUSIN_SCROLL";
  1250. }
  1251. // END_OF_DETAIL_REFINE_LOG
  1252. else if (pkItemScroll->GetValue(0) == MEMO_SCROLL)
  1253. {
  1254. success_prob = 100;
  1255. szRefineType = "MEMO_SCROLL";
  1256. }
  1257. else if (pkItemScroll->GetValue(0) == BDRAGON_SCROLL)
  1258. {
  1259. success_prob = 80;
  1260. szRefineType = "BDRAGON_SCROLL";
  1261. }
  1262.  
  1263. pkItemScroll->SetCount(pkItemScroll->GetCount() - 1);
  1264.  
  1265. if (prob <= success_prob)
  1266. {
  1267. // 성공! 모든 아이템이 사라지고, 같은 속성의 다른 아이템 획득
  1268. LPITEM pkNewItem = ITEM_MANAGER::instance().CreateItem(result_vnum, 1, 0, false);
  1269.  
  1270. if (pkNewItem)
  1271. {
  1272. ITEM_MANAGER::CopyAllAttrTo(item, pkNewItem);
  1273. LogManager::instance().ItemLog(this, pkNewItem, "REFINE SUCCESS", pkNewItem->GetName());
  1274.  
  1275. BYTE bCell = item->GetCell();
  1276.  
  1277. NotifyRefineSuccess(this, item, szRefineType);
  1278. DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, item->GetVnum(), -prt->cost);
  1279. ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (REFINE SUCCESS)");
  1280.  
  1281. pkNewItem->AddToCharacter(this, TItemPos(INVENTORY, bCell));
  1282. ITEM_MANAGER::instance().FlushDelayedSave(pkNewItem);
  1283. pkNewItem->AttrLog();
  1284. //PointChange(POINT_GOLD, -prt->cost);
  1285. PayRefineFee(prt->cost);
  1286. }
  1287. else
  1288. {
  1289. // 아이템 생성에 실패 -> 개량 실패로 간주
  1290. sys_err("cannot create item %u", result_vnum);
  1291. NotifyRefineFail(this, item, szRefineType);
  1292. }
  1293. }
  1294. else if (!bDestroyWhenFail && result_fail_vnum)
  1295. {
  1296. // 실패! 모든 아이템이 사라지고, 같은 속성의 낮은 등급의 아이템 획득
  1297. LPITEM pkNewItem = ITEM_MANAGER::instance().CreateItem(result_fail_vnum, 1, 0, false);
  1298.  
  1299. if (pkNewItem)
  1300. {
  1301. ITEM_MANAGER::CopyAllAttrTo(item, pkNewItem);
  1302. LogManager::instance().ItemLog(this, pkNewItem, "REFINE FAIL", pkNewItem->GetName());
  1303.  
  1304. BYTE bCell = item->GetCell();
  1305.  
  1306. DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, item->GetVnum(), -prt->cost);
  1307. NotifyRefineFail(this, item, szRefineType, -1);
  1308. ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (REFINE FAIL)");
  1309.  
  1310. pkNewItem->AddToCharacter(this, TItemPos(INVENTORY, bCell));
  1311. ITEM_MANAGER::instance().FlushDelayedSave(pkNewItem);
  1312.  
  1313. pkNewItem->AttrLog();
  1314.  
  1315. //PointChange(POINT_GOLD, -prt->cost);
  1316. PayRefineFee(prt->cost);
  1317. }
  1318. else
  1319. {
  1320. // 아이템 생성에 실패 -> 개량 실패로 간주
  1321. sys_err("cannot create item %u", result_fail_vnum);
  1322. NotifyRefineFail(this, item, szRefineType);
  1323. }
  1324. }
  1325. else
  1326. {
  1327. NotifyRefineFail(this, item, szRefineType); // 개량시 아이템 사라지지 않음
  1328.  
  1329. PayRefineFee(prt->cost);
  1330. }
  1331.  
  1332. return true;
  1333. }
  1334.  
  1335. bool CHARACTER::RefineInformation(BYTE bCell, BYTE bType, int iAdditionalCell)
  1336. {
  1337. if (bCell > INVENTORY_MAX_NUM)
  1338. return false;
  1339.  
  1340. LPITEM item = GetInventoryItem(bCell);
  1341.  
  1342. if (!item)
  1343. return false;
  1344.  
  1345. // REFINE_COST
  1346. if (bType == REFINE_TYPE_MONEY_ONLY && !GetQuestFlag("deviltower_zone.can_refine"))
  1347. {
  1348. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("사귀 타워 완료 보상은 한번까지 사용가능합니다."));
  1349. return false;
  1350. }
  1351. // END_OF_REFINE_COST
  1352.  
  1353. TPacketGCRefineInformation p;
  1354.  
  1355. p.header = HEADER_GC_REFINE_INFORMATION;
  1356. p.pos = bCell;
  1357. p.src_vnum = item->GetVnum();
  1358. p.result_vnum = item->GetRefinedVnum();
  1359. p.type = bType;
  1360.  
  1361. if (p.result_vnum == 0)
  1362. {
  1363. sys_err("RefineInformation p.result_vnum == 0");
  1364. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템은 개량할 수 없습니다."));
  1365. return false;
  1366. }
  1367.  
  1368. if (item->GetType() == ITEM_USE && item->GetSubType() == USE_TUNING)
  1369. {
  1370. if (bType == 0)
  1371. {
  1372. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템은 이 방식으로는 개량할 수 없습니다."));
  1373. return false;
  1374. }
  1375. else
  1376. {
  1377. LPITEM itemScroll = GetInventoryItem(iAdditionalCell);
  1378. if (!itemScroll || item->GetVnum() == itemScroll->GetVnum())
  1379. {
  1380. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("같은 개량서를 합칠 수는 없습니다."));
  1381. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("축복의 서와 현철을 합칠 수 있습니다."));
  1382. return false;
  1383. }
  1384. }
  1385. }
  1386.  
  1387. CRefineManager & rm = CRefineManager::instance();
  1388.  
  1389. const TRefineTable* prt = rm.GetRefineRecipe(item->GetRefineSet());
  1390.  
  1391. if (!prt)
  1392. {
  1393. sys_err("RefineInformation NOT GET REFINE SET %d", item->GetRefineSet());
  1394. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템은 개량할 수 없습니다."));
  1395. return false;
  1396. }
  1397.  
  1398. // REFINE_COST
  1399.  
  1400. //MAIN_QUEST_LV7
  1401. if (GetQuestFlag("main_quest_lv7.refine_chance") > 0)
  1402. {
  1403. // 일본은 제외
  1404. if (!item->CheckItemUseLevel(20) || item->GetType() != ITEM_WEAPON)
  1405. {
  1406. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("무료 개량 기회는 20 이하의 무기만 가능합니다"));
  1407. return false;
  1408. }
  1409. p.cost = 0;
  1410. }
  1411. else
  1412. p.cost = ComputeRefineFee(prt->cost);
  1413.  
  1414. //END_MAIN_QUEST_LV7
  1415. p.prob = prt->prob;
  1416. if (bType == REFINE_TYPE_MONEY_ONLY)
  1417. {
  1418. p.material_count = 0;
  1419. memset(p.materials, 0, sizeof(p.materials));
  1420. }
  1421. else
  1422. {
  1423. p.material_count = prt->material_count;
  1424. thecore_memcpy(&p.materials, prt->materials, sizeof(prt->materials));
  1425. }
  1426. // END_OF_REFINE_COST
  1427.  
  1428. GetDesc()->Packet(&p, sizeof(TPacketGCRefineInformation));
  1429.  
  1430. SetRefineMode(iAdditionalCell);
  1431. return true;
  1432. }
  1433.  
  1434. bool CHARACTER::RefineItem(LPITEM pkItem, LPITEM pkTarget)
  1435. {
  1436. if (!CanHandleItem())
  1437. return false;
  1438.  
  1439. if (pkItem->GetSubType() == USE_TUNING)
  1440. {
  1441. // XXX 성능, 소켓 개량서는 사라졌습니다...
  1442. // XXX 성능개량서는 축복의 서가 되었다!
  1443. // MUSIN_SCROLL
  1444. if (pkItem->GetValue(0) == MUSIN_SCROLL)
  1445. RefineInformation(pkTarget->GetCell(), REFINE_TYPE_MUSIN, pkItem->GetCell());
  1446. // END_OF_MUSIN_SCROLL
  1447. else if (pkItem->GetValue(0) == HYUNIRON_CHN)
  1448. RefineInformation(pkTarget->GetCell(), REFINE_TYPE_HYUNIRON, pkItem->GetCell());
  1449. else if (pkItem->GetValue(0) == BDRAGON_SCROLL)
  1450. {
  1451. if (pkTarget->GetRefineSet() != 702) return false;
  1452. RefineInformation(pkTarget->GetCell(), REFINE_TYPE_BDRAGON, pkItem->GetCell());
  1453. }
  1454. else
  1455. {
  1456. if (pkTarget->GetRefineSet() == 501) return false;
  1457. RefineInformation(pkTarget->GetCell(), REFINE_TYPE_SCROLL, pkItem->GetCell());
  1458. }
  1459. }
  1460. else if (pkItem->GetSubType() == USE_DETACHMENT && IS_SET(pkTarget->GetFlag(), ITEM_FLAG_REFINEABLE))
  1461. {
  1462. LogManager::instance().ItemLog(this, pkTarget, "USE_DETACHMENT", pkTarget->GetName());
  1463.  
  1464. bool bHasMetinStone = false;
  1465.  
  1466. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++)
  1467. {
  1468. long socket = pkTarget->GetSocket(i);
  1469. if (socket > 2 && socket != ITEM_BROKEN_METIN_VNUM)
  1470. {
  1471. bHasMetinStone = true;
  1472. break;
  1473. }
  1474. }
  1475.  
  1476. if (bHasMetinStone)
  1477. {
  1478. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  1479. {
  1480. long socket = pkTarget->GetSocket(i);
  1481. if (socket > 2 && socket != ITEM_BROKEN_METIN_VNUM)
  1482. {
  1483. AutoGiveItem(socket);
  1484. //TItemTable* pTable = ITEM_MANAGER::instance().GetTable(pkTarget->GetSocket(i));
  1485. //pkTarget->SetSocket(i, pTable->alValues[2]);
  1486. // 깨진돌로 대체해준다
  1487. pkTarget->SetSocket(i, ITEM_BROKEN_METIN_VNUM);
  1488. }
  1489. }
  1490. pkItem->SetCount(pkItem->GetCount() - 1);
  1491. return true;
  1492. }
  1493. else
  1494. {
  1495. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("빼낼 수 있는 메틴석이 없습니다."));
  1496. return false;
  1497. }
  1498. }
  1499.  
  1500. return false;
  1501. }
  1502.  
  1503. EVENTFUNC(kill_campfire_event)
  1504. {
  1505. char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  1506.  
  1507. if ( info == NULL )
  1508. {
  1509. sys_err( "kill_campfire_event> <Factor> Null pointer" );
  1510. return 0;
  1511. }
  1512.  
  1513. LPCHARACTER ch = info->ch;
  1514.  
  1515. if (ch == NULL) { // <Factor>
  1516. return 0;
  1517. }
  1518. ch->m_pkMiningEvent = NULL;
  1519. M2_DESTROY_CHARACTER(ch);
  1520. return 0;
  1521. }
  1522.  
  1523. bool CHARACTER::GiveRecallItem(LPITEM item)
  1524. {
  1525. int idx = GetMapIndex();
  1526. int iEmpireByMapIndex = -1;
  1527.  
  1528. if (idx < 20)
  1529. iEmpireByMapIndex = 1;
  1530. else if (idx < 40)
  1531. iEmpireByMapIndex = 2;
  1532. else if (idx < 60)
  1533. iEmpireByMapIndex = 3;
  1534. else if (idx < 10000)
  1535. iEmpireByMapIndex = 0;
  1536.  
  1537. switch (idx)
  1538. {
  1539. case 66:
  1540. case 216:
  1541. iEmpireByMapIndex = -1;
  1542. break;
  1543. }
  1544.  
  1545. if (iEmpireByMapIndex && GetEmpire() != iEmpireByMapIndex)
  1546. {
  1547. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("기억해 둘 수 없는 위치 입니다."));
  1548. return false;
  1549. }
  1550.  
  1551. int pos;
  1552.  
  1553. if (item->GetCount() == 1) // 아이템이 하나라면 그냥 셋팅.
  1554. {
  1555. item->SetSocket(0, GetX());
  1556. item->SetSocket(1, GetY());
  1557. }
  1558. else if ((pos = GetEmptyInventory(item->GetSize())) != -1) // 그렇지 않다면 다른 인벤토리 슬롯을 찾는다.
  1559. {
  1560. LPITEM item2 = ITEM_MANAGER::instance().CreateItem(item->GetVnum(), 1);
  1561.  
  1562. if (NULL != item2)
  1563. {
  1564. item2->SetSocket(0, GetX());
  1565. item2->SetSocket(1, GetY());
  1566. item2->AddToCharacter(this, TItemPos(INVENTORY, pos));
  1567.  
  1568. item->SetCount(item->GetCount() - 1);
  1569. }
  1570. }
  1571. else
  1572. {
  1573. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("소지품에 빈 공간이 없습니다."));
  1574. return false;
  1575. }
  1576.  
  1577. return true;
  1578. }
  1579.  
  1580. void CHARACTER::ProcessRecallItem(LPITEM item)
  1581. {
  1582. int idx;
  1583.  
  1584. if ((idx = SECTREE_MANAGER::instance().GetMapIndex(item->GetSocket(0), item->GetSocket(1))) == 0)
  1585. return;
  1586.  
  1587. int iEmpireByMapIndex = -1;
  1588.  
  1589. if (idx < 20)
  1590. iEmpireByMapIndex = 1;
  1591. else if (idx < 40)
  1592. iEmpireByMapIndex = 2;
  1593. else if (idx < 60)
  1594. iEmpireByMapIndex = 3;
  1595. else if (idx < 10000)
  1596. iEmpireByMapIndex = 0;
  1597.  
  1598. switch (idx)
  1599. {
  1600. case 66:
  1601. case 216:
  1602. iEmpireByMapIndex = -1;
  1603. break;
  1604. // 악룡군도 일때
  1605. case 301:
  1606. case 302:
  1607. case 303:
  1608. case 304:
  1609. if( GetLevel() < 90 )
  1610. {
  1611. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템의 레벨 제한보다 레벨이 낮습니다."));
  1612. return;
  1613. }
  1614. else
  1615. break;
  1616. }
  1617.  
  1618. if (iEmpireByMapIndex && GetEmpire() != iEmpireByMapIndex)
  1619. {
  1620. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("기억된 위치가 타제국에 속해 있어서 귀환할 수 없습니다."));
  1621. item->SetSocket(0, 0);
  1622. item->SetSocket(1, 0);
  1623. }
  1624. else
  1625. {
  1626. sys_log(1, "Recall: %s %d %d -> %d %d", GetName(), GetX(), GetY(), item->GetSocket(0), item->GetSocket(1));
  1627. WarpSet(item->GetSocket(0), item->GetSocket(1));
  1628. item->SetCount(item->GetCount() - 1);
  1629. }
  1630. }
  1631.  
  1632. void CHARACTER::__OpenPrivateShop()
  1633. {
  1634. unsigned bodyPart = GetPart(PART_MAIN);
  1635. switch (bodyPart)
  1636. {
  1637. case 0:
  1638. case 1:
  1639. case 2:
  1640. ChatPacket(CHAT_TYPE_COMMAND, "OpenPrivateShop");
  1641. break;
  1642. default:
  1643. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("갑옷을 벗어야 개인 상점을 열 수 있습니다."));
  1644. break;
  1645. }
  1646. }
  1647.  
  1648. // MYSHOP_PRICE_LIST
  1649. void CHARACTER::SendMyShopPriceListCmd(DWORD dwItemVnum, DWORD dwItemPrice)
  1650. {
  1651. char szLine[256];
  1652. snprintf(szLine, sizeof(szLine), "MyShopPriceList %u %u", dwItemVnum, dwItemPrice);
  1653. ChatPacket(CHAT_TYPE_COMMAND, szLine);
  1654. sys_log(0, szLine);
  1655. }
  1656.  
  1657. //
  1658. // DB 캐시로 부터 받은 리스트를 User 에게 전송하고 상점을 열라는 커맨드를 보낸다.
  1659. //
  1660. void CHARACTER::UseSilkBotaryReal(const TPacketMyshopPricelistHeader* p)
  1661. {
  1662. const TItemPriceInfo* pInfo = (const TItemPriceInfo*)(p + 1);
  1663.  
  1664. if (!p->byCount)
  1665. // 가격 리스트가 없다. dummy 데이터를 넣은 커맨드를 보내준다.
  1666. SendMyShopPriceListCmd(1, 0);
  1667. else {
  1668. for (int idx = 0; idx < p->byCount; idx++)
  1669. SendMyShopPriceListCmd(pInfo[ idx ].dwVnum, pInfo[ idx ].dwPrice);
  1670. }
  1671.  
  1672. __OpenPrivateShop();
  1673. }
  1674.  
  1675. //
  1676. // 이번 접속 후 처음 상점을 Open 하는 경우 리스트를 Load 하기 위해 DB 캐시에 가격정보 리스트 요청 패킷을 보낸다.
  1677. // 이후부터는 바로 상점을 열라는 응답을 보낸다.
  1678. //
  1679. void CHARACTER::UseSilkBotary(void)
  1680. {
  1681. if (m_bNoOpenedShop) {
  1682. DWORD dwPlayerID = GetPlayerID();
  1683. db_clientdesc->DBPacket(HEADER_GD_MYSHOP_PRICELIST_REQ, GetDesc()->GetHandle(), &dwPlayerID, sizeof(DWORD));
  1684. m_bNoOpenedShop = false;
  1685. } else {
  1686. __OpenPrivateShop();
  1687. }
  1688. }
  1689. // END_OF_MYSHOP_PRICE_LIST
  1690.  
  1691.  
  1692. int CalculateConsume(LPCHARACTER ch)
  1693. {
  1694. static const int WARP_NEED_LIFE_PERCENT = 30;
  1695. static const int WARP_MIN_LIFE_PERCENT = 10;
  1696. // CONSUME_LIFE_WHEN_USE_WARP_ITEM
  1697. int consumeLife = 0;
  1698. {
  1699. // CheckNeedLifeForWarp
  1700. const int curLife = ch->GetHP();
  1701. const int needPercent = WARP_NEED_LIFE_PERCENT;
  1702. const int needLife = ch->GetMaxHP() * needPercent / 100;
  1703. if (curLife < needLife)
  1704. {
  1705. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("남은 생명력 양이 모자라 사용할 수 없습니다."));
  1706. return -1;
  1707. }
  1708.  
  1709. consumeLife = needLife;
  1710.  
  1711.  
  1712. // CheckMinLifeForWarp: 독에 의해서 죽으면 안되므로 생명력 최소량는 남겨준다
  1713. const int minPercent = WARP_MIN_LIFE_PERCENT;
  1714. const int minLife = ch->GetMaxHP() * minPercent / 100;
  1715. if (curLife - needLife < minLife)
  1716. consumeLife = curLife - minLife;
  1717.  
  1718. if (consumeLife < 0)
  1719. consumeLife = 0;
  1720. }
  1721. // END_OF_CONSUME_LIFE_WHEN_USE_WARP_ITEM
  1722. return consumeLife;
  1723. }
  1724.  
  1725. int CalculateConsumeSP(LPCHARACTER lpChar)
  1726. {
  1727. static const int NEED_WARP_SP_PERCENT = 30;
  1728.  
  1729. const int curSP = lpChar->GetSP();
  1730. const int needSP = lpChar->GetMaxSP() * NEED_WARP_SP_PERCENT / 100;
  1731.  
  1732. if (curSP < needSP)
  1733. {
  1734. lpChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("남은 정신력 양이 모자라 사용할 수 없습니다."));
  1735. return -1;
  1736. }
  1737.  
  1738. return needSP;
  1739. }
  1740.  
  1741. bool CHARACTER::UseItemEx(LPITEM item, TItemPos DestCell)
  1742. {
  1743. int iLimitRealtimeStartFirstUseFlagIndex = -1;
  1744. int iLimitTimerBasedOnWearFlagIndex = -1;
  1745.  
  1746. WORD wDestCell = DestCell.cell;
  1747. BYTE bDestInven = DestCell.window_type;
  1748. for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i)
  1749. {
  1750. long limitValue = item->GetProto()->aLimits[i].lValue;
  1751.  
  1752. switch (item->GetProto()->aLimits[i].bType)
  1753. {
  1754. case LIMIT_LEVEL:
  1755. if (GetLevel() < limitValue)
  1756. {
  1757. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템의 레벨 제한보다 레벨이 낮습니다."));
  1758. return false;
  1759. }
  1760. break;
  1761.  
  1762. case LIMIT_REAL_TIME_START_FIRST_USE:
  1763. iLimitRealtimeStartFirstUseFlagIndex = i;
  1764. break;
  1765.  
  1766. case LIMIT_TIMER_BASED_ON_WEAR:
  1767. iLimitTimerBasedOnWearFlagIndex = i;
  1768. break;
  1769. }
  1770. }
  1771.  
  1772. if (test_server)
  1773. {
  1774. sys_log(0, "USE_ITEM %s, Inven %d, Cell %d, ItemType %d, SubType %d", item->GetName(), bDestInven, wDestCell, item->GetType(), item->GetSubType());
  1775. }
  1776.  
  1777. if ( CArenaManager::instance().IsLimitedItem( GetMapIndex(), item->GetVnum() ) == true )
  1778. {
  1779. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 중에는 이용할 수 없는 물품입니다."));
  1780. return false;
  1781. }
  1782.  
  1783. // 아이템 최초 사용 이후부터는 사용하지 않아도 시간이 차감되는 방식 처리.
  1784. if (-1 != iLimitRealtimeStartFirstUseFlagIndex)
  1785. {
  1786. // 한 번이라도 사용한 아이템인지 여부는 Socket1을 보고 판단한다. (Socket1에 사용횟수 기록)
  1787. if (0 == item->GetSocket(1))
  1788. {
  1789. // 사용가능시간은 Default 값으로 Limit Value 값을 사용하되, Socket0에 값이 있으면 그 값을 사용하도록 한다. (단위는 초)
  1790. long duration = (0 != item->GetSocket(0)) ? item->GetSocket(0) : item->GetProto()->aLimits[iLimitRealtimeStartFirstUseFlagIndex].lValue;
  1791.  
  1792. if (0 == duration)
  1793. duration = 60 * 60 * 24 * 7;
  1794.  
  1795. item->SetSocket(0, time(0) + duration);
  1796. item->StartRealTimeExpireEvent();
  1797. }
  1798.  
  1799. if (false == item->IsEquipped())
  1800. item->SetSocket(1, item->GetSocket(1) + 1);
  1801. }
  1802.  
  1803. switch (item->GetType())
  1804. {
  1805. case ITEM_HAIR:
  1806. return ItemProcess_Hair(item, wDestCell);
  1807.  
  1808. case ITEM_POLYMORPH:
  1809. return ItemProcess_Polymorph(item);
  1810.  
  1811. case ITEM_QUEST:
  1812. if (GetArena() != NULL || IsObserverMode() == true)
  1813. {
  1814. if (item->GetVnum() == 50051 || item->GetVnum() == 50052 || item->GetVnum() == 50053)
  1815. {
  1816. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 중에는 이용할 수 없는 물품입니다."));
  1817. return false;
  1818. }
  1819. }
  1820.  
  1821. if (!IS_SET(item->GetFlag(), ITEM_FLAG_QUEST_USE | ITEM_FLAG_QUEST_USE_MULTIPLE))
  1822. {
  1823. if (item->GetSIGVnum() == 0)
  1824. {
  1825. quest::CQuestManager::instance().UseItem(GetPlayerID(), item, false);
  1826. }
  1827. else
  1828. {
  1829. quest::CQuestManager::instance().SIGUse(GetPlayerID(), item->GetSIGVnum(), item, false);
  1830. }
  1831. }
  1832. break;
  1833.  
  1834. case ITEM_CAMPFIRE:
  1835. {
  1836. float fx, fy;
  1837. GetDeltaByDegree(GetRotation(), 100.0f, &fx, &fy);
  1838.  
  1839. LPSECTREE tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), (long)(GetX()+fx), (long)(GetY()+fy));
  1840.  
  1841. if (!tree)
  1842. {
  1843. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("모닥불을 피울 수 없는 지점입니다."));
  1844. return false;
  1845. }
  1846.  
  1847. if (tree->IsAttr((long)(GetX()+fx), (long)(GetY()+fy), ATTR_WATER))
  1848. {
  1849. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("물 속에 모닥불을 피울 수 없습니다."));
  1850. return false;
  1851. }
  1852.  
  1853. LPCHARACTER campfire = CHARACTER_MANAGER::instance().SpawnMob(fishing::CAMPFIRE_MOB, GetMapIndex(), (long)(GetX()+fx), (long)(GetY()+fy), 0, false, number(0, 359));
  1854.  
  1855. char_event_info* info = AllocEventInfo<char_event_info>();
  1856.  
  1857. info->ch = campfire;
  1858.  
  1859. campfire->m_pkMiningEvent = event_create(kill_campfire_event, info, PASSES_PER_SEC(40));
  1860.  
  1861. item->SetCount(item->GetCount() - 1);
  1862. }
  1863. break;
  1864.  
  1865. case ITEM_UNIQUE:
  1866. {
  1867. switch (item->GetSubType())
  1868. {
  1869. case USE_ABILITY_UP:
  1870. {
  1871. switch (item->GetValue(0))
  1872. {
  1873. case APPLY_MOV_SPEED:
  1874. AddAffect(AFFECT_UNIQUE_ABILITY, POINT_MOV_SPEED, item->GetValue(2), AFF_MOV_SPEED_POTION, item->GetValue(1), 0, true, true);
  1875. break;
  1876.  
  1877. case APPLY_ATT_SPEED:
  1878. AddAffect(AFFECT_UNIQUE_ABILITY, POINT_ATT_SPEED, item->GetValue(2), AFF_ATT_SPEED_POTION, item->GetValue(1), 0, true, true);
  1879. break;
  1880.  
  1881. case APPLY_STR:
  1882. AddAffect(AFFECT_UNIQUE_ABILITY, POINT_ST, item->GetValue(2), 0, item->GetValue(1), 0, true, true);
  1883. break;
  1884.  
  1885. case APPLY_DEX:
  1886. AddAffect(AFFECT_UNIQUE_ABILITY, POINT_DX, item->GetValue(2), 0, item->GetValue(1), 0, true, true);
  1887. break;
  1888.  
  1889. case APPLY_CON:
  1890. AddAffect(AFFECT_UNIQUE_ABILITY, POINT_HT, item->GetValue(2), 0, item->GetValue(1), 0, true, true);
  1891. break;
  1892.  
  1893. case APPLY_INT:
  1894. AddAffect(AFFECT_UNIQUE_ABILITY, POINT_IQ, item->GetValue(2), 0, item->GetValue(1), 0, true, true);
  1895. break;
  1896.  
  1897. case APPLY_CAST_SPEED:
  1898. AddAffect(AFFECT_UNIQUE_ABILITY, POINT_CASTING_SPEED, item->GetValue(2), 0, item->GetValue(1), 0, true, true);
  1899. break;
  1900.  
  1901. case APPLY_RESIST_MAGIC:
  1902. AddAffect(AFFECT_UNIQUE_ABILITY, POINT_RESIST_MAGIC, item->GetValue(2), 0, item->GetValue(1), 0, true, true);
  1903. break;
  1904.  
  1905. case APPLY_ATT_GRADE_BONUS:
  1906. AddAffect(AFFECT_UNIQUE_ABILITY, POINT_ATT_GRADE_BONUS,
  1907. item->GetValue(2), 0, item->GetValue(1), 0, true, true);
  1908. break;
  1909.  
  1910. case APPLY_DEF_GRADE_BONUS:
  1911. AddAffect(AFFECT_UNIQUE_ABILITY, POINT_DEF_GRADE_BONUS,
  1912. item->GetValue(2), 0, item->GetValue(1), 0, true, true);
  1913. break;
  1914. }
  1915. }
  1916.  
  1917. if (GetDungeon())
  1918. GetDungeon()->UsePotion(this);
  1919.  
  1920. if (GetWarMap())
  1921. GetWarMap()->UsePotion(this, item);
  1922.  
  1923. item->SetCount(item->GetCount() - 1);
  1924. break;
  1925.  
  1926. default:
  1927. {
  1928. if (item->GetSubType() == USE_SPECIAL)
  1929. {
  1930. sys_log(0, "ITEM_UNIQUE: USE_SPECIAL %u", item->GetVnum());
  1931.  
  1932. switch (item->GetVnum())
  1933. {
  1934. case 71049: // 비단보따리
  1935. if (LC_IsYMIR() == true || LC_IsKorea() == true)
  1936. {
  1937. if (IS_BOTARYABLE_ZONE(GetMapIndex()) == true)
  1938. {
  1939. UseSilkBotary();
  1940. }
  1941. else
  1942. {
  1943. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("개인 상점을 열 수 없는 지역입니다"));
  1944. }
  1945. }
  1946. else
  1947. {
  1948. UseSilkBotary();
  1949. }
  1950. break;
  1951. }
  1952. }
  1953. else
  1954. {
  1955. if (!item->IsEquipped())
  1956. EquipItem(item);
  1957. else
  1958. UnequipItem(item);
  1959. }
  1960. }
  1961. break;
  1962. }
  1963. }
  1964. break;
  1965.  
  1966. case ITEM_COSTUME:
  1967. case ITEM_WEAPON:
  1968. case ITEM_ARMOR:
  1969. case ITEM_ROD:
  1970. case ITEM_RING: // 신규 반지 아이템
  1971. case ITEM_BELT: // 신규 벨트 아이템
  1972. // MINING
  1973. case ITEM_PICK:
  1974. // END_OF_MINING
  1975. if (!item->IsEquipped())
  1976. EquipItem(item);
  1977. else
  1978. UnequipItem(item);
  1979. break;
  1980. // 착용하지 않은 용혼석은 사용할 수 없다.
  1981. // 정상적인 클라라면, 용혼석에 관하여 item use 패킷을 보낼 수 없다.
  1982. // 용혼석 착용은 item move 패킷으로 한다.
  1983. // 착용한 용혼석은 추출한다.
  1984. case ITEM_DS:
  1985. {
  1986. if (!item->IsEquipped())
  1987. return false;
  1988. return DSManager::instance().PullOut(this, NPOS, item);
  1989. break;
  1990. }
  1991. case ITEM_SPECIAL_DS:
  1992. if (!item->IsEquipped())
  1993. EquipItem(item);
  1994. else
  1995. UnequipItem(item);
  1996. break;
  1997.  
  1998. case ITEM_FISH:
  1999. {
  2000. if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true)
  2001. {
  2002. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 중에는 이용할 수 없는 물품입니다."));
  2003. return false;
  2004. }
  2005.  
  2006. if (item->GetSubType() == FISH_ALIVE)
  2007. fishing::UseFish(this, item);
  2008. }
  2009. break;
  2010.  
  2011. case ITEM_TREASURE_BOX:
  2012. {
  2013. return false;
  2014. //ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("열쇠로 잠겨 있어서 열리지 않는것 같다. 열쇠를 구해보자."));
  2015. }
  2016. break;
  2017.  
  2018. case ITEM_TREASURE_KEY:
  2019. {
  2020. LPITEM item2;
  2021.  
  2022. if (!GetItem(DestCell) || !(item2 = GetItem(DestCell)))
  2023. return false;
  2024.  
  2025. if (item2->IsExchanging())
  2026. return false;
  2027.  
  2028. if (item2->GetType() != ITEM_TREASURE_BOX)
  2029. {
  2030. ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("열쇠로 여는 물건이 아닌것 같다."));
  2031. return false;
  2032. }
  2033.  
  2034. if (item->GetValue(0) == item2->GetValue(0))
  2035. {
  2036. //ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("열쇠는 맞으나 아이템 주는 부분 구현이 안되었습니다."));
  2037. DWORD dwBoxVnum = item2->GetVnum();
  2038. std::vector <DWORD> dwVnums;
  2039. std::vector <DWORD> dwCounts;
  2040. std::vector <LPITEM> item_gets;
  2041. int count = 0;
  2042.  
  2043. if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count))
  2044. {
  2045. ITEM_MANAGER::instance().RemoveItem(item);
  2046. ITEM_MANAGER::instance().RemoveItem(item2);
  2047.  
  2048. for (int i = 0; i < count; i++){
  2049. switch (dwVnums[i])
  2050. {
  2051. case CSpecialItemGroup::GOLD:
  2052. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("돈 %d 냥을 획득했습니다."), dwCounts[i]);
  2053. break;
  2054. case CSpecialItemGroup::EXP:
  2055. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 부터 신비한 빛이 나옵니다."));
  2056. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d의 경험치를 획득했습니다."), dwCounts[i]);
  2057. break;
  2058. case CSpecialItemGroup::MOB:
  2059. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 몬스터가 나타났습니다!"));
  2060. break;
  2061. case CSpecialItemGroup::SLOW:
  2062. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 나온 빨간 연기를 들이마시자 움직이는 속도가 느려졌습니다!"));
  2063. break;
  2064. case CSpecialItemGroup::DRAIN_HP:
  2065. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자가 갑자기 폭발하였습니다! 생명력이 감소했습니다."));
  2066. break;
  2067. case CSpecialItemGroup::POISON:
  2068. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 나온 녹색 연기를 들이마시자 독이 온몸으로 퍼집니다!"));
  2069. break;
  2070. case CSpecialItemGroup::MOB_GROUP:
  2071. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 몬스터가 나타났습니다!"));
  2072. break;
  2073. default:
  2074. if (item_gets[i])
  2075. {
  2076. if (dwCounts[i] > 1)
  2077. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 %s 가 %d 개 나왔습니다."), item_gets[i]->GetName(), dwCounts[i]);
  2078. else
  2079. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 %s 가 나왔습니다."), item_gets[i]->GetName());
  2080.  
  2081. }
  2082. }
  2083. }
  2084. }
  2085. else
  2086. {
  2087. ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("열쇠가 맞지 않는 것 같다."));
  2088. return false;
  2089. }
  2090. }
  2091. else
  2092. {
  2093. ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("열쇠가 맞지 않는 것 같다."));
  2094. return false;
  2095. }
  2096. }
  2097. break;
  2098.  
  2099. case ITEM_GIFTBOX:
  2100. {
  2101. DWORD dwBoxVnum = item->GetVnum();
  2102. std::vector <DWORD> dwVnums;
  2103. std::vector <DWORD> dwCounts;
  2104. std::vector <LPITEM> item_gets;
  2105. int count = 0;
  2106.  
  2107. if (dwBoxVnum == 50033 && LC_IsYMIR()) // 알수없는 상자
  2108. {
  2109. if (GetLevel() < 15)
  2110. {
  2111. ChatPacket(CHAT_TYPE_INFO, "15레벨 이하에서는 사용할 수 없습니다.");
  2112. return false;
  2113. }
  2114. }
  2115.  
  2116. if( (dwBoxVnum > 51500 && dwBoxVnum < 52000) || (dwBoxVnum >= 50255 && dwBoxVnum <= 50260) ) // 용혼원석들
  2117. {
  2118. if( !(this->DragonSoul_IsQualified()) )
  2119. {
  2120. ChatPacket(CHAT_TYPE_INFO,LC_TEXT("먼저 용혼석 퀘스트를 완료하셔야 합니다."));
  2121. return false;
  2122. }
  2123. }
  2124.  
  2125. if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count))
  2126. {
  2127. item->SetCount(item->GetCount()-1);
  2128.  
  2129. for (int i = 0; i < count; i++){
  2130. switch (dwVnums[i])
  2131. {
  2132. case CSpecialItemGroup::GOLD:
  2133. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("돈 %d 냥을 획득했습니다."), dwCounts[i]);
  2134. break;
  2135. case CSpecialItemGroup::EXP:
  2136. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 부터 신비한 빛이 나옵니다."));
  2137. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d의 경험치를 획득했습니다."), dwCounts[i]);
  2138. break;
  2139. case CSpecialItemGroup::MOB:
  2140. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 몬스터가 나타났습니다!"));
  2141. break;
  2142. case CSpecialItemGroup::SLOW:
  2143. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 나온 빨간 연기를 들이마시자 움직이는 속도가 느려졌습니다!"));
  2144. break;
  2145. case CSpecialItemGroup::DRAIN_HP:
  2146. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자가 갑자기 폭발하였습니다! 생명력이 감소했습니다."));
  2147. break;
  2148. case CSpecialItemGroup::POISON:
  2149. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 나온 녹색 연기를 들이마시자 독이 온몸으로 퍼집니다!"));
  2150. break;
  2151. case CSpecialItemGroup::MOB_GROUP:
  2152. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 몬스터가 나타났습니다!"));
  2153. break;
  2154. default:
  2155. if (item_gets[i])
  2156. {
  2157. if (dwCounts[i] > 1)
  2158. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 %s 가 %d 개 나왔습니다."), item_gets[i]->GetName(), dwCounts[i]);
  2159. else
  2160. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 %s 가 나왔습니다."), item_gets[i]->GetName());
  2161. }
  2162. }
  2163. }
  2164. }
  2165. else
  2166. {
  2167. ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("아무것도 얻을 수 없었습니다."));
  2168. return false;
  2169. }
  2170. }
  2171. break;
  2172.  
  2173. case ITEM_SKILLFORGET:
  2174. {
  2175. if (!item->GetSocket(0))
  2176. {
  2177. ITEM_MANAGER::instance().RemoveItem(item);
  2178. return false;
  2179. }
  2180.  
  2181. DWORD dwVnum = item->GetSocket(0);
  2182.  
  2183. if (SkillLevelDown(dwVnum))
  2184. {
  2185. ITEM_MANAGER::instance().RemoveItem(item);
  2186. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("스킬 레벨을 내리는데 성공하였습니다."));
  2187. }
  2188. else
  2189. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("스킬 레벨을 내릴 수 없습니다."));
  2190. }
  2191. break;
  2192.  
  2193. case ITEM_SKILLBOOK:
  2194. {
  2195. if (IsPolymorphed())
  2196. {
  2197. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("변신중에는 책을 읽을수 없습니다."));
  2198. return false;
  2199. }
  2200.  
  2201. DWORD dwVnum = 0;
  2202.  
  2203. if (item->GetVnum() == 50300)
  2204. {
  2205. dwVnum = item->GetSocket(0);
  2206. }
  2207. else
  2208. {
  2209. // 새로운 수련서는 value 0 에 스킬 번호가 있으므로 그것을 사용.
  2210. dwVnum = item->GetValue(0);
  2211. }
  2212.  
  2213. if (0 == dwVnum)
  2214. {
  2215. ITEM_MANAGER::instance().RemoveItem(item);
  2216.  
  2217. return false;
  2218. }
  2219.  
  2220. if (true == LearnSkillByBook(dwVnum))
  2221. {
  2222. ITEM_MANAGER::instance().RemoveItem(item);
  2223.  
  2224. int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX);
  2225.  
  2226. if (distribution_test_server)
  2227. iReadDelay /= 3;
  2228.  
  2229. //한국 본섭의 경우에는 시간을 24시간 고정
  2230. if (LC_IsKorea())
  2231. iReadDelay = 86400;
  2232.  
  2233. SetSkillNextReadTime(dwVnum, get_global_time() + iReadDelay);
  2234. }
  2235. }
  2236. break;
  2237.  
  2238. case ITEM_USE:
  2239. {
  2240. if (item->GetVnum() > 50800 && item->GetVnum() <= 50820)
  2241. {
  2242. if (test_server)
  2243. sys_log (0, "ADD addtional effect : vnum(%d) subtype(%d)", item->GetOriginalVnum(), item->GetSubType());
  2244.  
  2245. int affect_type = AFFECT_EXP_BONUS_EURO_FREE;
  2246. int apply_type = aApplyInfo[item->GetValue(0)].bPointType;
  2247. int apply_value = item->GetValue(2);
  2248. int apply_duration = item->GetValue(1);
  2249.  
  2250. switch (item->GetSubType())
  2251. {
  2252. case USE_ABILITY_UP:
  2253. if (FindAffect(affect_type, apply_type))
  2254. {
  2255. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이미 효과가 걸려 있습니다."));
  2256. return false;
  2257. }
  2258.  
  2259. {
  2260. switch (item->GetValue(0))
  2261. {
  2262. case APPLY_MOV_SPEED:
  2263. AddAffect(affect_type, apply_type, apply_value, AFF_MOV_SPEED_POTION, apply_duration, 0, true, true);
  2264. break;
  2265.  
  2266. case APPLY_ATT_SPEED:
  2267. AddAffect(affect_type, apply_type, apply_value, AFF_ATT_SPEED_POTION, apply_duration, 0, true, true);
  2268. break;
  2269.  
  2270. case APPLY_STR:
  2271. case APPLY_DEX:
  2272. case APPLY_CON:
  2273. case APPLY_INT:
  2274. case APPLY_CAST_SPEED:
  2275. case APPLY_RESIST_MAGIC:
  2276. case APPLY_ATT_GRADE_BONUS:
  2277. case APPLY_DEF_GRADE_BONUS:
  2278. AddAffect(affect_type, apply_type, apply_value, 0, apply_duration, 0, true, true);
  2279. break;
  2280. }
  2281. }
  2282.  
  2283. if (GetDungeon())
  2284. GetDungeon()->UsePotion(this);
  2285.  
  2286. if (GetWarMap())
  2287. GetWarMap()->UsePotion(this, item);
  2288.  
  2289. item->SetCount(item->GetCount() - 1);
  2290. break;
  2291.  
  2292. case USE_AFFECT :
  2293. {
  2294. if (FindAffect(AFFECT_EXP_BONUS_EURO_FREE, aApplyInfo[item->GetValue(1)].bPointType))
  2295. {
  2296. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이미 효과가 걸려 있습니다."));
  2297. }
  2298. else
  2299. {
  2300. // PC_BANG_ITEM_ADD
  2301. if (item->IsPCBangItem() == true)
  2302. {
  2303. // PC방인지 체크해서 처리
  2304. if (CPCBangManager::instance().IsPCBangIP(GetDesc()->GetHostName()) == false)
  2305. {
  2306. // PC방이 아님!
  2307. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템은 PC방에서만 사용할 수 있습니다."));
  2308. return false;
  2309. }
  2310. }
  2311. // END_PC_BANG_ITEM_ADD
  2312.  
  2313. AddAffect(AFFECT_EXP_BONUS_EURO_FREE, aApplyInfo[item->GetValue(1)].bPointType, item->GetValue(2), 0, item->GetValue(3), 0, false, true);
  2314. item->SetCount(item->GetCount() - 1);
  2315. }
  2316. }
  2317. break;
  2318.  
  2319. case USE_POTION_NODELAY:
  2320. {
  2321. if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true)
  2322. {
  2323. if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit") > 0)
  2324. {
  2325. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장에서 사용하실 수 없습니다."));
  2326. return false;
  2327. }
  2328.  
  2329. switch (item->GetVnum())
  2330. {
  2331. case 70020 :
  2332. case 71018 :
  2333. case 71019 :
  2334. case 71020 :
  2335. if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count") < 10000)
  2336. {
  2337. if (m_nPotionLimit <= 0)
  2338. {
  2339. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("사용 제한량을 초과하였습니다."));
  2340. return false;
  2341. }
  2342. }
  2343. break;
  2344.  
  2345. default :
  2346. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장에서 사용하실 수 없습니다."));
  2347. return false;
  2348. break;
  2349. }
  2350. }
  2351.  
  2352. bool used = false;
  2353.  
  2354. if (item->GetValue(0) != 0) // HP 절대값 회복
  2355. {
  2356. if (GetHP() < GetMaxHP())
  2357. {
  2358. PointChange(POINT_HP, item->GetValue(0) * (100 + GetPoint(POINT_POTION_BONUS)) / 100);
  2359. EffectPacket(SE_HPUP_RED);
  2360. used = TRUE;
  2361. }
  2362. }
  2363.  
  2364. if (item->GetValue(1) != 0) // SP 절대값 회복
  2365. {
  2366. if (GetSP() < GetMaxSP())
  2367. {
  2368. PointChange(POINT_SP, item->GetValue(1) * (100 + GetPoint(POINT_POTION_BONUS)) / 100);
  2369. EffectPacket(SE_SPUP_BLUE);
  2370. used = TRUE;
  2371. }
  2372. }
  2373.  
  2374. if (item->GetValue(3) != 0) // HP % 회복
  2375. {
  2376. if (GetHP() < GetMaxHP())
  2377. {
  2378. PointChange(POINT_HP, item->GetValue(3) * GetMaxHP() / 100);
  2379. EffectPacket(SE_HPUP_RED);
  2380. used = TRUE;
  2381. }
  2382. }
  2383.  
  2384. if (item->GetValue(4) != 0) // SP % 회복
  2385. {
  2386. if (GetSP() < GetMaxSP())
  2387. {
  2388. PointChange(POINT_SP, item->GetValue(4) * GetMaxSP() / 100);
  2389. EffectPacket(SE_SPUP_BLUE);
  2390. used = TRUE;
  2391. }
  2392. }
  2393.  
  2394. if (used)
  2395. {
  2396. if (item->GetVnum() == 50085 || item->GetVnum() == 50086)
  2397. {
  2398. if (test_server)
  2399. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("월병 또는 종자 를 사용하였습니다"));
  2400. SetUseSeedOrMoonBottleTime();
  2401. }
  2402. if (GetDungeon())
  2403. GetDungeon()->UsePotion(this);
  2404.  
  2405. if (GetWarMap())
  2406. GetWarMap()->UsePotion(this, item);
  2407.  
  2408. m_nPotionLimit--;
  2409.  
  2410. //RESTRICT_USE_SEED_OR_MOONBOTTLE
  2411. item->SetCount(item->GetCount() - 1);
  2412. //END_RESTRICT_USE_SEED_OR_MOONBOTTLE
  2413. }
  2414. }
  2415. break;
  2416. }
  2417.  
  2418. return true;
  2419. }
  2420.  
  2421.  
  2422. if (item->GetVnum() >= 27863 && item->GetVnum() <= 27883)
  2423. {
  2424. if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true)
  2425. {
  2426. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 중에는 이용할 수 없는 물품입니다."));
  2427. return false;
  2428. }
  2429. }
  2430.  
  2431. if (test_server)
  2432. {
  2433. sys_log (0, "USE_ITEM %s Type %d SubType %d vnum %d", item->GetName(), item->GetType(), item->GetSubType(), item->GetOriginalVnum());
  2434. }
  2435.  
  2436. switch (item->GetSubType())
  2437. {
  2438. case USE_TIME_CHARGE_PER:
  2439. {
  2440. LPITEM pDestItem = GetItem(DestCell);
  2441. if (NULL == pDestItem)
  2442. {
  2443. return false;
  2444. }
  2445. // 우선 용혼석에 관해서만 하도록 한다.
  2446. if (pDestItem->IsDragonSoul())
  2447. {
  2448. int ret;
  2449. char buf[128];
  2450. if (item->GetVnum() == DRAGON_HEART_VNUM)
  2451. {
  2452. ret = pDestItem->GiveMoreTime_Per((float)item->GetSocket(ITEM_SOCKET_CHARGING_AMOUNT_IDX));
  2453. }
  2454. else
  2455. {
  2456. ret = pDestItem->GiveMoreTime_Per((float)item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX));
  2457. }
  2458. if (ret > 0)
  2459. {
  2460. if (item->GetVnum() == DRAGON_HEART_VNUM)
  2461. {
  2462. sprintf(buf, "Inc %ds by item{VN:%d SOC%d:%d}", ret, item->GetVnum(), ITEM_SOCKET_CHARGING_AMOUNT_IDX, item->GetSocket(ITEM_SOCKET_CHARGING_AMOUNT_IDX));
  2463. }
  2464. else
  2465. {
  2466. sprintf(buf, "Inc %ds by item{VN:%d VAL%d:%d}", ret, item->GetVnum(), ITEM_VALUE_CHARGING_AMOUNT_IDX, item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX));
  2467. }
  2468.  
  2469. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d초 만큼 충전되었습니다."), ret);
  2470. item->SetCount(item->GetCount() - 1);
  2471. LogManager::instance().ItemLog(this, item, "DS_CHARGING_SUCCESS", buf);
  2472. return true;
  2473. }
  2474. else
  2475. {
  2476. if (item->GetVnum() == DRAGON_HEART_VNUM)
  2477. {
  2478. sprintf(buf, "No change by item{VN:%d SOC%d:%d}", item->GetVnum(), ITEM_SOCKET_CHARGING_AMOUNT_IDX, item->GetSocket(ITEM_SOCKET_CHARGING_AMOUNT_IDX));
  2479. }
  2480. else
  2481. {
  2482. sprintf(buf, "No change by item{VN:%d VAL%d:%d}", item->GetVnum(), ITEM_VALUE_CHARGING_AMOUNT_IDX, item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX));
  2483. }
  2484.  
  2485. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("충전할 수 없습니다."));
  2486. LogManager::instance().ItemLog(this, item, "DS_CHARGING_FAILED", buf);
  2487. return false;
  2488. }
  2489. }
  2490. else
  2491. return false;
  2492. }
  2493. break;
  2494. case USE_TIME_CHARGE_FIX:
  2495. {
  2496. LPITEM pDestItem = GetItem(DestCell);
  2497. if (NULL == pDestItem)
  2498. {
  2499. return false;
  2500. }
  2501.  
  2502. if (pDestItem->IsDragonSoul())
  2503. {
  2504. int ret = pDestItem->GiveMoreTime_Fix(item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX));
  2505. char buf[128];
  2506. if (ret)
  2507. {
  2508. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d초 만큼 충전되었습니다."), ret);
  2509. sprintf(buf, "Increase %ds by item{VN:%d VAL%d:%d}", ret, item->GetVnum(), ITEM_VALUE_CHARGING_AMOUNT_IDX, item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX));
  2510. LogManager::instance().ItemLog(this, item, "DS_CHARGING_SUCCESS", buf);
  2511. item->SetCount(item->GetCount() - 1);
  2512. return true;
  2513. }
  2514. else
  2515. {
  2516. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("충전할 수 없습니다."));
  2517. sprintf(buf, "No change by item{VN:%d VAL%d:%d}", item->GetVnum(), ITEM_VALUE_CHARGING_AMOUNT_IDX, item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX));
  2518. LogManager::instance().ItemLog(this, item, "DS_CHARGING_FAILED", buf);
  2519. return false;
  2520. }
  2521. }
  2522.  
  2523.  
  2524. #ifdef ENABLE_PET_COSTUME_SYSTEM
  2525. if (pDestItem->GetSubType() == COSTUME_PET)
  2526. return false;
  2527. #endif
  2528.  
  2529. else
  2530. return false;
  2531. }
  2532. break;
  2533. case USE_SPECIAL:
  2534.  
  2535. switch (item->GetVnum())
  2536. {
  2537. //크리스마스 란주
  2538. case ITEM_NOG_POCKET:
  2539. {
  2540. /*
  2541. 란주능력치 : item_proto value 의미
  2542. 이동속도 value 1
  2543. 공격력 value 2
  2544. 경험치 value 3
  2545. 지속시간 value 0 (단위 초)
  2546.  
  2547. */
  2548. if (FindAffect(AFFECT_NOG_ABILITY))
  2549. {
  2550. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이미 효과가 걸려 있습니다."));
  2551. return false;
  2552. }
  2553. long time = item->GetValue(0);
  2554. long moveSpeedPer = item->GetValue(1);
  2555. long attPer = item->GetValue(2);
  2556. long expPer = item->GetValue(3);
  2557. AddAffect(AFFECT_NOG_ABILITY, POINT_MOV_SPEED, moveSpeedPer, AFF_MOV_SPEED_POTION, time, 0, true, true);
  2558. AddAffect(AFFECT_NOG_ABILITY, POINT_MALL_ATTBONUS, attPer, AFF_NONE, time, 0, true, true);
  2559. AddAffect(AFFECT_NOG_ABILITY, POINT_MALL_EXPBONUS, expPer, AFF_NONE, time, 0, true, true);
  2560. item->SetCount(item->GetCount() - 1);
  2561. }
  2562. break;
  2563.  
  2564. //라마단용 사탕
  2565. case ITEM_RAMADAN_CANDY:
  2566. {
  2567. /*
  2568. 사탕능력치 : item_proto value 의미
  2569. 이동속도 value 1
  2570. 공격력 value 2
  2571. 경험치 value 3
  2572. 지속시간 value 0 (단위 초)
  2573.  
  2574. */
  2575. long time = item->GetValue(0);
  2576. long moveSpeedPer = item->GetValue(1);
  2577. long attPer = item->GetValue(2);
  2578. long expPer = item->GetValue(3);
  2579. AddAffect(AFFECT_RAMADAN_ABILITY, POINT_MOV_SPEED, moveSpeedPer, AFF_MOV_SPEED_POTION, time, 0, true, true);
  2580. AddAffect(AFFECT_RAMADAN_ABILITY, POINT_MALL_ATTBONUS, attPer, AFF_NONE, time, 0, true, true);
  2581. AddAffect(AFFECT_RAMADAN_ABILITY, POINT_MALL_EXPBONUS, expPer, AFF_NONE, time, 0, true, true);
  2582. item->SetCount(item->GetCount() - 1);
  2583. }
  2584. break;
  2585. case ITEM_MARRIAGE_RING:
  2586. {
  2587. marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID());
  2588. if (pMarriage)
  2589. {
  2590. if (pMarriage->ch1 != NULL)
  2591. {
  2592. if (CArenaManager::instance().IsArenaMap(pMarriage->ch1->GetMapIndex()) == true)
  2593. {
  2594. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 중에는 이용할 수 없는 물품입니다."));
  2595. break;
  2596. }
  2597. }
  2598.  
  2599. if (pMarriage->ch2 != NULL)
  2600. {
  2601. if (CArenaManager::instance().IsArenaMap(pMarriage->ch2->GetMapIndex()) == true)
  2602. {
  2603. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 중에는 이용할 수 없는 물품입니다."));
  2604. break;
  2605. }
  2606. }
  2607.  
  2608. int consumeSP = CalculateConsumeSP(this);
  2609.  
  2610. if (consumeSP < 0)
  2611. return false;
  2612.  
  2613. PointChange(POINT_SP, -consumeSP, false);
  2614.  
  2615. WarpToPID(pMarriage->GetOther(GetPlayerID()));
  2616. }
  2617. else
  2618. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("결혼 상태가 아니면 결혼반지를 사용할 수 없습니다."));
  2619. }
  2620. break;
  2621.  
  2622. //기존 용기의 망토
  2623. case UNIQUE_ITEM_CAPE_OF_COURAGE:
  2624. //라마단 보상용 용기의 망토
  2625. case 70057:
  2626. case REWARD_BOX_UNIQUE_ITEM_CAPE_OF_COURAGE:
  2627. AggregateMonster();
  2628. item->SetCount(item->GetCount()-1);
  2629. break;
  2630.  
  2631. case UNIQUE_ITEM_WHITE_FLAG:
  2632. ForgetMyAttacker();
  2633. item->SetCount(item->GetCount()-1);
  2634. break;
  2635.  
  2636. case UNIQUE_ITEM_TREASURE_BOX:
  2637. break;
  2638.  
  2639. case 30093:
  2640. case 30094:
  2641. case 30095:
  2642. case 30096:
  2643. // 복주머니
  2644. {
  2645. const int MAX_BAG_INFO = 26;
  2646. static struct LuckyBagInfo
  2647. {
  2648. DWORD count;
  2649. int prob;
  2650. DWORD vnum;
  2651. } b1[MAX_BAG_INFO] =
  2652. {
  2653. { 1000, 302, 1 },
  2654. { 10, 150, 27002 },
  2655. { 10, 75, 27003 },
  2656. { 10, 100, 27005 },
  2657. { 10, 50, 27006 },
  2658. { 10, 80, 27001 },
  2659. { 10, 50, 27002 },
  2660. { 10, 80, 27004 },
  2661. { 10, 50, 27005 },
  2662. { 1, 10, 50300 },
  2663. { 1, 6, 92 },
  2664. { 1, 2, 132 },
  2665. { 1, 6, 1052 },
  2666. { 1, 2, 1092 },
  2667. { 1, 6, 2082 },
  2668. { 1, 2, 2122 },
  2669. { 1, 6, 3082 },
  2670. { 1, 2, 3122 },
  2671. { 1, 6, 5052 },
  2672. { 1, 2, 5082 },
  2673. { 1, 6, 7082 },
  2674. { 1, 2, 7122 },
  2675. { 1, 1, 11282 },
  2676. { 1, 1, 11482 },
  2677. { 1, 1, 11682 },
  2678. { 1, 1, 11882 },
  2679. };
  2680.  
  2681. struct LuckyBagInfo b2[MAX_BAG_INFO] =
  2682. {
  2683. { 1000, 302, 1 },
  2684. { 10, 150, 27002 },
  2685. { 10, 75, 27002 },
  2686. { 10, 100, 27005 },
  2687. { 10, 50, 27005 },
  2688. { 10, 80, 27001 },
  2689. { 10, 50, 27002 },
  2690. { 10, 80, 27004 },
  2691. { 10, 50, 27005 },
  2692. { 1, 10, 50300 },
  2693. { 1, 6, 92 },
  2694. { 1, 2, 132 },
  2695. { 1, 6, 1052 },
  2696. { 1, 2, 1092 },
  2697. { 1, 6, 2082 },
  2698. { 1, 2, 2122 },
  2699. { 1, 6, 3082 },
  2700. { 1, 2, 3122 },
  2701. { 1, 6, 5052 },
  2702. { 1, 2, 5082 },
  2703. { 1, 6, 7082 },
  2704. { 1, 2, 7122 },
  2705. { 1, 1, 11282 },
  2706. { 1, 1, 11482 },
  2707. { 1, 1, 11682 },
  2708. { 1, 1, 11882 },
  2709. };
  2710.  
  2711. LuckyBagInfo * bi = NULL;
  2712. if (LC_IsHongKong())
  2713. bi = b2;
  2714. else
  2715. bi = b1;
  2716.  
  2717. int pct = number(1, 1000);
  2718.  
  2719. int i;
  2720. for (i=0;i<MAX_BAG_INFO;i++)
  2721. {
  2722. if (pct <= bi[i].prob)
  2723. break;
  2724. pct -= bi[i].prob;
  2725. }
  2726. if (i>=MAX_BAG_INFO)
  2727. return false;
  2728.  
  2729. if (bi[i].vnum == 50300)
  2730. {
  2731. // 스킬수련서는 특수하게 준다.
  2732. GiveRandomSkillBook();
  2733. }
  2734. else if (bi[i].vnum == 1)
  2735. {
  2736. PointChange(POINT_GOLD, 1000, true);
  2737. }
  2738. else
  2739. {
  2740. AutoGiveItem(bi[i].vnum, bi[i].count);
  2741. }
  2742. ITEM_MANAGER::instance().RemoveItem(item);
  2743. }
  2744. break;
  2745.  
  2746. case 50004: // 이벤트용 감지기
  2747. {
  2748. if (item->GetSocket(0))
  2749. {
  2750. item->SetSocket(0, item->GetSocket(0) + 1);
  2751. }
  2752. else
  2753. {
  2754. // 처음 사용시
  2755. int iMapIndex = GetMapIndex();
  2756.  
  2757. PIXEL_POSITION pos;
  2758.  
  2759. if (SECTREE_MANAGER::instance().GetRandomLocation(iMapIndex, pos, 700))
  2760. {
  2761. item->SetSocket(0, 1);
  2762. item->SetSocket(1, pos.x);
  2763. item->SetSocket(2, pos.y);
  2764. }
  2765. else
  2766. {
  2767. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 곳에선 이벤트용 감지기가 동작하지 않는것 같습니다."));
  2768. return false;
  2769. }
  2770. }
  2771.  
  2772. int dist = 0;
  2773. float distance = (DISTANCE_SQRT(GetX()-item->GetSocket(1), GetY()-item->GetSocket(2)));
  2774.  
  2775. if (distance < 1000.0f)
  2776. {
  2777. // 발견!
  2778. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이벤트용 감지기가 신비로운 빛을 내며 사라집니다."));
  2779.  
  2780. // 사용횟수에 따라 주는 아이템을 다르게 한다.
  2781. struct TEventStoneInfo
  2782. {
  2783. DWORD dwVnum;
  2784. int count;
  2785. int prob;
  2786. };
  2787. const int EVENT_STONE_MAX_INFO = 15;
  2788. TEventStoneInfo info_10[EVENT_STONE_MAX_INFO] =
  2789. {
  2790. { 27001, 10, 8 },
  2791. { 27004, 10, 6 },
  2792. { 27002, 10, 12 },
  2793. { 27005, 10, 12 },
  2794. { 27100, 1, 9 },
  2795. { 27103, 1, 9 },
  2796. { 27101, 1, 10 },
  2797. { 27104, 1, 10 },
  2798. { 27999, 1, 12 },
  2799.  
  2800. { 25040, 1, 4 },
  2801.  
  2802. { 27410, 1, 0 },
  2803. { 27600, 1, 0 },
  2804. { 25100, 1, 0 },
  2805.  
  2806. { 50001, 1, 0 },
  2807. { 50003, 1, 1 },
  2808. };
  2809. TEventStoneInfo info_7[EVENT_STONE_MAX_INFO] =
  2810. {
  2811. { 27001, 10, 1 },
  2812. { 27004, 10, 1 },
  2813. { 27004, 10, 9 },
  2814. { 27005, 10, 9 },
  2815. { 27100, 1, 5 },
  2816. { 27103, 1, 5 },
  2817. { 27101, 1, 10 },
  2818. { 27104, 1, 10 },
  2819. { 27999, 1, 14 },
  2820.  
  2821. { 25040, 1, 5 },
  2822.  
  2823. { 27410, 1, 5 },
  2824. { 27600, 1, 5 },
  2825. { 25100, 1, 5 },
  2826.  
  2827. { 50001, 1, 0 },
  2828. { 50003, 1, 5 },
  2829.  
  2830. };
  2831. TEventStoneInfo info_4[EVENT_STONE_MAX_INFO] =
  2832. {
  2833. { 27001, 10, 0 },
  2834. { 27004, 10, 0 },
  2835. { 27002, 10, 0 },
  2836. { 27005, 10, 0 },
  2837. { 27100, 1, 0 },
  2838. { 27103, 1, 0 },
  2839. { 27101, 1, 0 },
  2840. { 27104, 1, 0 },
  2841. { 27999, 1, 25 },
  2842.  
  2843. { 25040, 1, 0 },
  2844.  
  2845. { 27410, 1, 0 },
  2846. { 27600, 1, 0 },
  2847. { 25100, 1, 15 },
  2848.  
  2849. { 50001, 1, 10 },
  2850. { 50003, 1, 50 },
  2851.  
  2852. };
  2853.  
  2854. {
  2855. TEventStoneInfo* info;
  2856. if (item->GetSocket(0) <= 4)
  2857. info = info_4;
  2858. else if (item->GetSocket(0) <= 7)
  2859. info = info_7;
  2860. else
  2861. info = info_10;
  2862.  
  2863. int prob = number(1, 100);
  2864.  
  2865. for (int i = 0; i < EVENT_STONE_MAX_INFO; ++i)
  2866. {
  2867. if (!info[i].prob)
  2868. continue;
  2869.  
  2870. if (prob <= info[i].prob)
  2871. {
  2872. if (info[i].dwVnum == 50001)
  2873. {
  2874. DWORD * pdw = M2_NEW DWORD[2];
  2875.  
  2876. pdw[0] = info[i].dwVnum;
  2877. pdw[1] = info[i].count;
  2878.  
  2879. // 추첨서는 소켓을 설정한다
  2880. DBManager::instance().ReturnQuery(QID_LOTTO, GetPlayerID(), pdw,
  2881. "INSERT INTO lotto_list VALUES(0, 'server%s', %u, NOW())",
  2882. get_table_postfix(), GetPlayerID());
  2883. }
  2884. else
  2885. AutoGiveItem(info[i].dwVnum, info[i].count);
  2886.  
  2887. break;
  2888. }
  2889. prob -= info[i].prob;
  2890. }
  2891. }
  2892.  
  2893. char chatbuf[CHAT_MAX_LEN + 1];
  2894. int len = snprintf(chatbuf, sizeof(chatbuf), "StoneDetect %u 0 0", (DWORD)GetVID());
  2895.  
  2896. if (len < 0 || len >= (int) sizeof(chatbuf))
  2897. len = sizeof(chatbuf) - 1;
  2898.  
  2899. ++len; // \0 문자까지 보내기
  2900.  
  2901. TPacketGCChat pack_chat;
  2902. pack_chat.header = HEADER_GC_CHAT;
  2903. pack_chat.size = sizeof(TPacketGCChat) + len;
  2904. pack_chat.type = CHAT_TYPE_COMMAND;
  2905. pack_chat.id = 0;
  2906. pack_chat.bEmpire = GetDesc()->GetEmpire();
  2907. //pack_chat.id = vid;
  2908.  
  2909. TEMP_BUFFER buf;
  2910. buf.write(&pack_chat, sizeof(TPacketGCChat));
  2911. buf.write(chatbuf, len);
  2912.  
  2913. PacketAround(buf.read_peek(), buf.size());
  2914.  
  2915. ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (DETECT_EVENT_STONE) 1");
  2916. return true;
  2917. }
  2918. else if (distance < 20000)
  2919. dist = 1;
  2920. else if (distance < 70000)
  2921. dist = 2;
  2922. else
  2923. dist = 3;
  2924.  
  2925. // 많이 사용했으면 사라진다.
  2926. const int STONE_DETECT_MAX_TRY = 10;
  2927. if (item->GetSocket(0) >= STONE_DETECT_MAX_TRY)
  2928. {
  2929. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이벤트용 감지기가 흔적도 없이 사라집니다."));
  2930. ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (DETECT_EVENT_STONE) 0");
  2931. AutoGiveItem(27002);
  2932. return true;
  2933. }
  2934.  
  2935. if (dist)
  2936. {
  2937. char chatbuf[CHAT_MAX_LEN + 1];
  2938. int len = snprintf(chatbuf, sizeof(chatbuf),
  2939. "StoneDetect %u %d %d",
  2940. (DWORD)GetVID(), dist, (int)GetDegreeFromPositionXY(GetX(), item->GetSocket(2), item->GetSocket(1), GetY()));
  2941.  
  2942. if (len < 0 || len >= (int) sizeof(chatbuf))
  2943. len = sizeof(chatbuf) - 1;
  2944.  
  2945. ++len; // \0 문자까지 보내기
  2946.  
  2947. TPacketGCChat pack_chat;
  2948. pack_chat.header = HEADER_GC_CHAT;
  2949. pack_chat.size = sizeof(TPacketGCChat) + len;
  2950. pack_chat.type = CHAT_TYPE_COMMAND;
  2951. pack_chat.id = 0;
  2952. pack_chat.bEmpire = GetDesc()->GetEmpire();
  2953. //pack_chat.id = vid;
  2954.  
  2955. TEMP_BUFFER buf;
  2956. buf.write(&pack_chat, sizeof(TPacketGCChat));
  2957. buf.write(chatbuf, len);
  2958.  
  2959. PacketAround(buf.read_peek(), buf.size());
  2960. }
  2961.  
  2962. }
  2963. break;
  2964.  
  2965. case 27989: // 영석감지기
  2966. case 76006: // 선물용 영석감지기
  2967. {
  2968. LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap(GetMapIndex());
  2969.  
  2970. if (pMap != NULL)
  2971. {
  2972. item->SetSocket(0, item->GetSocket(0) + 1);
  2973.  
  2974. FFindStone f;
  2975.  
  2976. // <Factor> SECTREE::for_each -> SECTREE::for_each_entity
  2977. pMap->for_each(f);
  2978.  
  2979. if (f.m_mapStone.size() > 0)
  2980. {
  2981. std::map<DWORD, LPCHARACTER>::iterator stone = f.m_mapStone.begin();
  2982.  
  2983. DWORD max = UINT_MAX;
  2984. LPCHARACTER pTarget = stone->second;
  2985.  
  2986. while (stone != f.m_mapStone.end())
  2987. {
  2988. DWORD dist = (DWORD)DISTANCE_SQRT(GetX()-stone->second->GetX(), GetY()-stone->second->GetY());
  2989.  
  2990. if (dist != 0 && max > dist)
  2991. {
  2992. max = dist;
  2993. pTarget = stone->second;
  2994. }
  2995. stone++;
  2996. }
  2997.  
  2998. if (pTarget != NULL)
  2999. {
  3000. int val = 3;
  3001.  
  3002. if (max < 10000) val = 2;
  3003. else if (max < 70000) val = 1;
  3004.  
  3005. ChatPacket(CHAT_TYPE_COMMAND, "StoneDetect %u %d %d", (DWORD)GetVID(), val,
  3006. (int)GetDegreeFromPositionXY(GetX(), pTarget->GetY(), pTarget->GetX(), GetY()));
  3007. }
  3008. else
  3009. {
  3010. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("감지기를 작용하였으나 감지되는 영석이 없습니다."));
  3011. }
  3012. }
  3013. else
  3014. {
  3015. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("감지기를 작용하였으나 감지되는 영석이 없습니다."));
  3016. }
  3017.  
  3018. if (item->GetSocket(0) >= 6)
  3019. {
  3020. ChatPacket(CHAT_TYPE_COMMAND, "StoneDetect %u 0 0", (DWORD)GetVID());
  3021. ITEM_MANAGER::instance().RemoveItem(item);
  3022. }
  3023. }
  3024. break;
  3025. }
  3026. break;
  3027.  
  3028. case 27996: // 독병
  3029. item->SetCount(item->GetCount() - 1);
  3030. /*if (GetSkillLevel(SKILL_CREATE_POISON))
  3031. AddAffect(AFFECT_ATT_GRADE, POINT_ATT_GRADE, 3, AFF_DRINK_POISON, 15*60, 0, true);
  3032. else
  3033. {
  3034. // 독다루기가 없으면 50% 즉사 50% 공격력 +2
  3035. if (number(0, 1))
  3036. {
  3037. if (GetHP() > 100)
  3038. PointChange(POINT_HP, -(GetHP() - 1));
  3039. else
  3040. Dead();
  3041. }
  3042. else
  3043. AddAffect(AFFECT_ATT_GRADE, POINT_ATT_GRADE, 2, AFF_DRINK_POISON, 15*60, 0, true);
  3044. }*/
  3045. break;
  3046.  
  3047. case 27987: // 조개
  3048. // 50 돌조각 47990
  3049. // 30 꽝
  3050. // 10 백진주 47992
  3051. // 7 청진주 47993
  3052. // 3 피진주 47994
  3053. {
  3054. item->SetCount(item->GetCount() - 1);
  3055.  
  3056. int r = number(1, 100);
  3057.  
  3058. if (r <= 50)
  3059. {
  3060. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("조개에서 돌조각이 나왔습니다."));
  3061. AutoGiveItem(27990);
  3062. }
  3063. else
  3064. {
  3065. const int prob_table_euckr[] =
  3066. {
  3067. 80, 90, 97
  3068. };
  3069.  
  3070. const int prob_table_gb2312[] =
  3071. {
  3072. 95, 97, 99
  3073. };
  3074.  
  3075. const int * prob_table = !g_iUseLocale ? prob_table_euckr : prob_table_gb2312;
  3076.  
  3077. if (r <= prob_table[0])
  3078. {
  3079. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("조개가 흔적도 없이 사라집니다."));
  3080. }
  3081. else if (r <= prob_table[1])
  3082. {
  3083. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("조개에서 백진주가 나왔습니다."));
  3084. AutoGiveItem(27992);
  3085. }
  3086. else if (r <= prob_table[2])
  3087. {
  3088. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("조개에서 청진주가 나왔습니다."));
  3089. AutoGiveItem(27993);
  3090. }
  3091. else
  3092. {
  3093. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("조개에서 피진주가 나왔습니다."));
  3094. AutoGiveItem(27994);
  3095. }
  3096. }
  3097. }
  3098. break;
  3099.  
  3100. case 71013: // 축제용폭죽
  3101. CreateFly(number(FLY_FIREWORK1, FLY_FIREWORK6), this);
  3102. item->SetCount(item->GetCount() - 1);
  3103. break;
  3104.  
  3105. case 50100: // 폭죽
  3106. case 50101:
  3107. case 50102:
  3108. case 50103:
  3109. case 50104:
  3110. case 50105:
  3111. case 50106:
  3112. CreateFly(item->GetVnum() - 50100 + FLY_FIREWORK1, this);
  3113. item->SetCount(item->GetCount() - 1);
  3114. break;
  3115.  
  3116. case 50200: // 보따리
  3117. if (LC_IsYMIR() == true || LC_IsKorea() == true)
  3118. {
  3119. if (IS_BOTARYABLE_ZONE(GetMapIndex()) == true)
  3120. {
  3121. __OpenPrivateShop();
  3122. }
  3123. else
  3124. {
  3125. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("개인 상점을 열 수 없는 지역입니다"));
  3126. }
  3127. }
  3128. else
  3129. {
  3130. __OpenPrivateShop();
  3131. }
  3132. break;
  3133.  
  3134. case fishing::FISH_MIND_PILL_VNUM:
  3135. AddAffect(AFFECT_FISH_MIND_PILL, POINT_NONE, 0, AFF_FISH_MIND, 20*60, 0, true);
  3136. item->SetCount(item->GetCount() - 1);
  3137. break;
  3138.  
  3139. case 50301: // 통솔력 수련서
  3140. case 50302:
  3141. case 50303:
  3142. {
  3143. if (IsPolymorphed() == true)
  3144. {
  3145. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("둔갑 중에는 능력을 올릴 수 없습니다."));
  3146. return false;
  3147. }
  3148.  
  3149. int lv = GetSkillLevel(SKILL_LEADERSHIP);
  3150.  
  3151. if (lv < item->GetValue(0))
  3152. {
  3153. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 책은 너무 어려워 이해하기가 힘듭니다."));
  3154. return false;
  3155. }
  3156.  
  3157. if (lv >= item->GetValue(1))
  3158. {
  3159. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 책은 아무리 봐도 도움이 될 것 같지 않습니다."));
  3160. return false;
  3161. }
  3162.  
  3163. if (LearnSkillByBook(SKILL_LEADERSHIP))
  3164. {
  3165. ITEM_MANAGER::instance().RemoveItem(item);
  3166.  
  3167. int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX);
  3168. if (distribution_test_server) iReadDelay /= 3;
  3169.  
  3170. SetSkillNextReadTime(SKILL_LEADERSHIP, get_global_time() + iReadDelay);
  3171. }
  3172. }
  3173. break;
  3174.  
  3175. case 50304: // 연계기 수련서
  3176. case 50305:
  3177. case 50306:
  3178. {
  3179. if (IsPolymorphed())
  3180. {
  3181. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("변신중에는 책을 읽을수 없습니다."));
  3182. return false;
  3183.  
  3184. }
  3185. if (GetSkillLevel(SKILL_COMBO) == 0 && GetLevel() < 30)
  3186. {
  3187. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("레벨 30이 되기 전에는 습득할 수 있을 것 같지 않습니다."));
  3188. return false;
  3189. }
  3190.  
  3191. if (GetSkillLevel(SKILL_COMBO) == 1 && GetLevel() < 50)
  3192. {
  3193. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("레벨 50이 되기 전에는 습득할 수 있을 것 같지 않습니다."));
  3194. return false;
  3195. }
  3196.  
  3197. if (GetSkillLevel(SKILL_COMBO) >= 2)
  3198. {
  3199. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("연계기는 더이상 수련할 수 없습니다."));
  3200. return false;
  3201. }
  3202.  
  3203. int iPct = item->GetValue(0);
  3204.  
  3205. if (LearnSkillByBook(SKILL_COMBO, iPct))
  3206. {
  3207. ITEM_MANAGER::instance().RemoveItem(item);
  3208.  
  3209. int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX);
  3210. if (distribution_test_server) iReadDelay /= 3;
  3211.  
  3212. SetSkillNextReadTime(SKILL_COMBO, get_global_time() + iReadDelay);
  3213. }
  3214. }
  3215. break;
  3216. case 50311: // 언어 수련서
  3217. case 50312:
  3218. case 50313:
  3219. {
  3220. if (IsPolymorphed())
  3221. {
  3222. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("변신중에는 책을 읽을수 없습니다."));
  3223. return false;
  3224.  
  3225. }
  3226. DWORD dwSkillVnum = item->GetValue(0);
  3227. int iPct = MINMAX(0, item->GetValue(1), 100);
  3228. if (GetSkillLevel(dwSkillVnum)>=20 || dwSkillVnum-SKILL_LANGUAGE1+1 == GetEmpire())
  3229. {
  3230. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이미 완벽하게 알아들을 수 있는 언어이다."));
  3231. return false;
  3232. }
  3233.  
  3234. if (LearnSkillByBook(dwSkillVnum, iPct))
  3235. {
  3236. ITEM_MANAGER::instance().RemoveItem(item);
  3237.  
  3238. int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX);
  3239. if (distribution_test_server) iReadDelay /= 3;
  3240.  
  3241. SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay);
  3242. }
  3243. }
  3244. break;
  3245.  
  3246. case 50061 : // 일본 말 소환 스킬 수련서
  3247. {
  3248. if (IsPolymorphed())
  3249. {
  3250. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("변신중에는 책을 읽을수 없습니다."));
  3251. return false;
  3252.  
  3253. }
  3254. DWORD dwSkillVnum = item->GetValue(0);
  3255. int iPct = MINMAX(0, item->GetValue(1), 100);
  3256.  
  3257. if (GetSkillLevel(dwSkillVnum) >= 10)
  3258. {
  3259. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("더 이상 수련할 수 없습니다."));
  3260. return false;
  3261. }
  3262.  
  3263. if (LearnSkillByBook(dwSkillVnum, iPct))
  3264. {
  3265. ITEM_MANAGER::instance().RemoveItem(item);
  3266.  
  3267. int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX);
  3268. if (distribution_test_server) iReadDelay /= 3;
  3269.  
  3270. SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay);
  3271. }
  3272. }
  3273. break;
  3274.  
  3275. case 50314: case 50315: case 50316: // 변신 수련서
  3276. case 50323: case 50324: // 증혈 수련서
  3277. case 50325: case 50326: // 철통 수련서
  3278. {
  3279. if (IsPolymorphed() == true)
  3280. {
  3281. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("둔갑 중에는 능력을 올릴 수 없습니다."));
  3282. return false;
  3283. }
  3284.  
  3285. int iSkillLevelLowLimit = item->GetValue(0);
  3286. int iSkillLevelHighLimit = item->GetValue(1);
  3287. int iPct = MINMAX(0, item->GetValue(2), 100);
  3288. int iLevelLimit = item->GetValue(3);
  3289. DWORD dwSkillVnum = 0;
  3290.  
  3291. switch (item->GetVnum())
  3292. {
  3293. case 50314: case 50315: case 50316:
  3294. dwSkillVnum = SKILL_POLYMORPH;
  3295. break;
  3296.  
  3297. case 50323: case 50324:
  3298. dwSkillVnum = SKILL_ADD_HP;
  3299. break;
  3300.  
  3301. case 50325: case 50326:
  3302. dwSkillVnum = SKILL_RESIST_PENETRATE;
  3303. break;
  3304.  
  3305. default:
  3306. return false;
  3307. }
  3308.  
  3309. if (0 == dwSkillVnum)
  3310. return false;
  3311.  
  3312. if (GetLevel() < iLevelLimit)
  3313. {
  3314. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 책을 읽으려면 레벨을 더 올려야 합니다."));
  3315. return false;
  3316. }
  3317.  
  3318. if (GetSkillLevel(dwSkillVnum) >= 40)
  3319. {
  3320. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("더 이상 수련할 수 없습니다."));
  3321. return false;
  3322. }
  3323.  
  3324. if (GetSkillLevel(dwSkillVnum) < iSkillLevelLowLimit)
  3325. {
  3326. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 책은 너무 어려워 이해하기가 힘듭니다."));
  3327. return false;
  3328. }
  3329.  
  3330. if (GetSkillLevel(dwSkillVnum) >= iSkillLevelHighLimit)
  3331. {
  3332. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 책으로는 더 이상 수련할 수 없습니다."));
  3333. return false;
  3334. }
  3335.  
  3336. if (LearnSkillByBook(dwSkillVnum, iPct))
  3337. {
  3338. ITEM_MANAGER::instance().RemoveItem(item);
  3339.  
  3340. int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX);
  3341. if (distribution_test_server) iReadDelay /= 3;
  3342.  
  3343. SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay);
  3344. }
  3345. }
  3346. break;
  3347.  
  3348. case 50902:
  3349. case 50903:
  3350. case 50904:
  3351. {
  3352. if (IsPolymorphed())
  3353. {
  3354. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("변신중에는 책을 읽을수 없습니다."));
  3355. return false;
  3356.  
  3357. }
  3358. DWORD dwSkillVnum = SKILL_CREATE;
  3359. int iPct = MINMAX(0, item->GetValue(1), 100);
  3360.  
  3361. if (GetSkillLevel(dwSkillVnum)>=40)
  3362. {
  3363. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("더 이상 수련할 수 없습니다."));
  3364. return false;
  3365. }
  3366.  
  3367. if (LearnSkillByBook(dwSkillVnum, iPct))
  3368. {
  3369. ITEM_MANAGER::instance().RemoveItem(item);
  3370.  
  3371. int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX);
  3372. if (distribution_test_server) iReadDelay /= 3;
  3373.  
  3374. SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay);
  3375.  
  3376. if (test_server)
  3377. {
  3378. ChatPacket(CHAT_TYPE_INFO, "[TEST_SERVER] Success to learn skill ");
  3379. }
  3380. }
  3381. else
  3382. {
  3383. if (test_server)
  3384. {
  3385. ChatPacket(CHAT_TYPE_INFO, "[TEST_SERVER] Failed to learn skill ");
  3386. }
  3387. }
  3388. }
  3389. break;
  3390.  
  3391. // MINING
  3392. case ITEM_MINING_SKILL_TRAIN_BOOK:
  3393. {
  3394. if (IsPolymorphed())
  3395. {
  3396. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("변신중에는 책을 읽을수 없습니다."));
  3397. return false;
  3398.  
  3399. }
  3400. DWORD dwSkillVnum = SKILL_MINING;
  3401. int iPct = MINMAX(0, item->GetValue(1), 100);
  3402.  
  3403. if (GetSkillLevel(dwSkillVnum)>=40)
  3404. {
  3405. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("더 이상 수련할 수 없습니다."));
  3406. return false;
  3407. }
  3408.  
  3409. if (LearnSkillByBook(dwSkillVnum, iPct))
  3410. {
  3411. ITEM_MANAGER::instance().RemoveItem(item);
  3412.  
  3413. int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX);
  3414. if (distribution_test_server) iReadDelay /= 3;
  3415.  
  3416. SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay);
  3417. }
  3418. }
  3419. break;
  3420. // END_OF_MINING
  3421.  
  3422. case ITEM_HORSE_SKILL_TRAIN_BOOK:
  3423. {
  3424. if (IsPolymorphed())
  3425. {
  3426. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("변신중에는 책을 읽을수 없습니다."));
  3427. return false;
  3428.  
  3429. }
  3430. DWORD dwSkillVnum = SKILL_HORSE;
  3431. int iPct = MINMAX(0, item->GetValue(1), 100);
  3432.  
  3433. if (GetLevel() < 50)
  3434. {
  3435. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아직 승마 스킬을 수련할 수 있는 레벨이 아닙니다."));
  3436. return false;
  3437. }
  3438.  
  3439. if (!test_server && get_global_time() < GetSkillNextReadTime(dwSkillVnum))
  3440. {
  3441. if (FindAffect(AFFECT_SKILL_NO_BOOK_DELAY))
  3442. {
  3443. // 주안술서 사용중에는 시간 제한 무시
  3444. RemoveAffect(AFFECT_SKILL_NO_BOOK_DELAY);
  3445. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("주안술서를 통해 주화입마에서 빠져나왔습니다."));
  3446. }
  3447. else
  3448. {
  3449. SkillLearnWaitMoreTimeMessage(GetSkillNextReadTime(dwSkillVnum) - get_global_time());
  3450. return false;
  3451. }
  3452. }
  3453.  
  3454. if (GetPoint(POINT_HORSE_SKILL) >= 20 ||
  3455. GetSkillLevel(SKILL_HORSE_WILDATTACK) + GetSkillLevel(SKILL_HORSE_CHARGE) + GetSkillLevel(SKILL_HORSE_ESCAPE) >= 60 ||
  3456. GetSkillLevel(SKILL_HORSE_WILDATTACK_RANGE) + GetSkillLevel(SKILL_HORSE_CHARGE) + GetSkillLevel(SKILL_HORSE_ESCAPE) >= 60)
  3457. {
  3458. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("더 이상 승마 수련서를 읽을 수 없습니다."));
  3459. return false;
  3460. }
  3461.  
  3462. if (number(1, 100) <= iPct)
  3463. {
  3464. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("승마 수련서를 읽어 승마 스킬 포인트를 얻었습니다."));
  3465. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("얻은 포인트로는 승마 스킬의 레벨을 올릴 수 있습니다."));
  3466. PointChange(POINT_HORSE_SKILL, 1);
  3467.  
  3468. int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX);
  3469. if (distribution_test_server) iReadDelay /= 3;
  3470.  
  3471. if (!test_server)
  3472. SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay);
  3473. }
  3474. else
  3475. {
  3476. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("승마 수련서 이해에 실패하였습니다."));
  3477. }
  3478.  
  3479. ITEM_MANAGER::instance().RemoveItem(item);
  3480. }
  3481. break;
  3482.  
  3483. case 70102: // 선두
  3484. case 70103: // 선두
  3485. {
  3486. if (GetAlignment() >= 0)
  3487. return false;
  3488.  
  3489. int delta = MIN(-GetAlignment(), item->GetValue(0));
  3490.  
  3491. sys_log(0, "%s ALIGNMENT ITEM %d", GetName(), delta);
  3492.  
  3493. UpdateAlignment(delta);
  3494. item->SetCount(item->GetCount() - 1);
  3495.  
  3496. if (delta / 10 > 0)
  3497. {
  3498. ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("마음이 맑아지는군. 가슴을 짓누르던 무언가가 좀 가벼워진 느낌이야."));
  3499. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("선악치가 %d 증가하였습니다."), delta/10);
  3500. }
  3501. }
  3502. break;
  3503.  
  3504. case 71107: // 천도복숭아
  3505. {
  3506. int val = item->GetValue(0);
  3507. int interval = item->GetValue(1);
  3508. quest::PC* pPC = quest::CQuestManager::instance().GetPC(GetPlayerID());
  3509. int last_use_time = pPC->GetFlag("mythical_peach.last_use_time");
  3510.  
  3511. if (get_global_time() - last_use_time < interval * 60 * 60)
  3512. {
  3513. if (test_server == false)
  3514. {
  3515. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아직 사용할 수 없습니다."));
  3516. return false;
  3517. }
  3518. else
  3519. {
  3520. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("테스트 서버 시간제한 통과"));
  3521. }
  3522. }
  3523.  
  3524. if (GetAlignment() == 200000)
  3525. {
  3526. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("선악치를 더 이상 올릴 수 없습니다."));
  3527. return false;
  3528. }
  3529.  
  3530. if (200000 - GetAlignment() < val * 10)
  3531. {
  3532. val = (200000 - GetAlignment()) / 10;
  3533. }
  3534.  
  3535. int old_alignment = GetAlignment() / 10;
  3536.  
  3537. UpdateAlignment(val*10);
  3538.  
  3539. item->SetCount(item->GetCount()-1);
  3540. pPC->SetFlag("mythical_peach.last_use_time", get_global_time());
  3541.  
  3542. ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("마음이 맑아지는군. 가슴을 짓누르던 무언가가 좀 가벼워진 느낌이야."));
  3543. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("선악치가 %d 증가하였습니다."), val);
  3544.  
  3545. char buf[256 + 1];
  3546. snprintf(buf, sizeof(buf), "%d %d", old_alignment, GetAlignment() / 10);
  3547. LogManager::instance().CharLog(this, val, "MYTHICAL_PEACH", buf);
  3548. }
  3549. break;
  3550.  
  3551. case 71109: // 탈석서
  3552. case 72719:
  3553. {
  3554. LPITEM item2;
  3555.  
  3556. if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell)))
  3557. return false;
  3558.  
  3559. if (item2->IsExchanging() == true)
  3560. return false;
  3561.  
  3562. if (item2->GetSocketCount() == 0)
  3563. return false;
  3564.  
  3565. switch( item2->GetType() )
  3566. {
  3567. case ITEM_WEAPON:
  3568. break;
  3569. case ITEM_ARMOR:
  3570. switch (item2->GetSubType())
  3571. {
  3572. case ARMOR_EAR:
  3573. case ARMOR_WRIST:
  3574. case ARMOR_NECK:
  3575. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("빼낼 영석이 없습니다"));
  3576. return false;
  3577. }
  3578. break;
  3579.  
  3580. default:
  3581. return false;
  3582. }
  3583.  
  3584. std::stack<long> socket;
  3585.  
  3586. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  3587. socket.push(item2->GetSocket(i));
  3588.  
  3589. int idx = ITEM_SOCKET_MAX_NUM - 1;
  3590.  
  3591. while (socket.size() > 0)
  3592. {
  3593. if (socket.top() > 2 && socket.top() != ITEM_BROKEN_METIN_VNUM)
  3594. break;
  3595.  
  3596. idx--;
  3597. socket.pop();
  3598. }
  3599.  
  3600. if (socket.size() == 0)
  3601. {
  3602. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("빼낼 영석이 없습니다"));
  3603. return false;
  3604. }
  3605.  
  3606. LPITEM pItemReward = AutoGiveItem(socket.top());
  3607.  
  3608. if (pItemReward != NULL)
  3609. {
  3610. item2->SetSocket(idx, 1);
  3611.  
  3612. char buf[256+1];
  3613. snprintf(buf, sizeof(buf), "%s(%u) %s(%u)",
  3614. item2->GetName(), item2->GetID(), pItemReward->GetName(), pItemReward->GetID());
  3615. LogManager::instance().ItemLog(this, item, "USE_DETACHMENT_ONE", buf);
  3616.  
  3617. item->SetCount(item->GetCount() - 1);
  3618. }
  3619. }
  3620. break;
  3621.  
  3622. case 70201: // 탈색제
  3623. case 70202: // 염색약(흰색)
  3624. case 70203: // 염색약(금색)
  3625. case 70204: // 염색약(빨간색)
  3626. case 70205: // 염색약(갈색)
  3627. case 70206: // 염색약(검은색)
  3628. {
  3629. // NEW_HAIR_STYLE_ADD
  3630. if (GetPart(PART_HAIR) >= 1001)
  3631. {
  3632. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("현재 헤어스타일에서는 염색과 탈색이 불가능합니다."));
  3633. }
  3634. // END_NEW_HAIR_STYLE_ADD
  3635. else
  3636. {
  3637. quest::CQuestManager& q = quest::CQuestManager::instance();
  3638. quest::PC* pPC = q.GetPC(GetPlayerID());
  3639.  
  3640. if (pPC)
  3641. {
  3642. int last_dye_level = pPC->GetFlag("dyeing_hair.last_dye_level");
  3643.  
  3644. if (last_dye_level == 0 ||
  3645. last_dye_level+3 <= GetLevel() ||
  3646. item->GetVnum() == 70201)
  3647. {
  3648. SetPart(PART_HAIR, item->GetVnum() - 70201);
  3649.  
  3650. if (item->GetVnum() == 70201)
  3651. pPC->SetFlag("dyeing_hair.last_dye_level", 0);
  3652. else
  3653. pPC->SetFlag("dyeing_hair.last_dye_level", GetLevel());
  3654.  
  3655. item->SetCount(item->GetCount() - 1);
  3656. UpdatePacket();
  3657. }
  3658. else
  3659. {
  3660. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d 레벨이 되어야 다시 염색하실 수 있습니다."), last_dye_level+3);
  3661. }
  3662. }
  3663. }
  3664. }
  3665. break;
  3666.  
  3667. case ITEM_NEW_YEAR_GREETING_VNUM:
  3668. {
  3669. DWORD dwBoxVnum = ITEM_NEW_YEAR_GREETING_VNUM;
  3670. std::vector <DWORD> dwVnums;
  3671. std::vector <DWORD> dwCounts;
  3672. std::vector <LPITEM> item_gets;
  3673. int count = 0;
  3674.  
  3675. if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count))
  3676. {
  3677. for (int i = 0; i < count; i++)
  3678. {
  3679. if (dwVnums[i] == CSpecialItemGroup::GOLD)
  3680. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("돈 %d 냥을 획득했습니다."), dwCounts[i]);
  3681. }
  3682.  
  3683. item->SetCount(item->GetCount() - 1);
  3684. }
  3685. }
  3686. break;
  3687.  
  3688. case ITEM_VALENTINE_ROSE:
  3689. case ITEM_VALENTINE_CHOCOLATE:
  3690. {
  3691. DWORD dwBoxVnum = item->GetVnum();
  3692. std::vector <DWORD> dwVnums;
  3693. std::vector <DWORD> dwCounts;
  3694. std::vector <LPITEM> item_gets;
  3695. int count = 0;
  3696.  
  3697.  
  3698. if (item->GetVnum() == ITEM_VALENTINE_ROSE && SEX_MALE==GET_SEX(this) ||
  3699. item->GetVnum() == ITEM_VALENTINE_CHOCOLATE && SEX_FEMALE==GET_SEX(this))
  3700. {
  3701. // 성별이 맞지않아 쓸 수 없다.
  3702. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("성별이 맞지않아 이 아이템을 열 수 없습니다."));
  3703. return false;
  3704. }
  3705.  
  3706.  
  3707. if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count))
  3708. item->SetCount(item->GetCount()-1);
  3709. }
  3710. break;
  3711.  
  3712. case ITEM_WHITEDAY_CANDY:
  3713. case ITEM_WHITEDAY_ROSE:
  3714. {
  3715. DWORD dwBoxVnum = item->GetVnum();
  3716. std::vector <DWORD> dwVnums;
  3717. std::vector <DWORD> dwCounts;
  3718. std::vector <LPITEM> item_gets;
  3719. int count = 0;
  3720.  
  3721.  
  3722. if (item->GetVnum() == ITEM_WHITEDAY_CANDY && SEX_MALE==GET_SEX(this) ||
  3723. item->GetVnum() == ITEM_WHITEDAY_ROSE && SEX_FEMALE==GET_SEX(this))
  3724. {
  3725. // 성별이 맞지않아 쓸 수 없다.
  3726. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("성별이 맞지않아 이 아이템을 열 수 없습니다."));
  3727. return false;
  3728. }
  3729.  
  3730.  
  3731. if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count))
  3732. item->SetCount(item->GetCount()-1);
  3733. }
  3734. break;
  3735.  
  3736. case 50011: // 월광보합
  3737. {
  3738. DWORD dwBoxVnum = 50011;
  3739. std::vector <DWORD> dwVnums;
  3740. std::vector <DWORD> dwCounts;
  3741. std::vector <LPITEM> item_gets;
  3742. int count = 0;
  3743.  
  3744. if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count))
  3745. {
  3746. for (int i = 0; i < count; i++)
  3747. {
  3748. char buf[50 + 1];
  3749. snprintf(buf, sizeof(buf), "%u %u", dwVnums[i], dwCounts[i]);
  3750. LogManager::instance().ItemLog(this, item, "MOONLIGHT_GET", buf);
  3751.  
  3752. //ITEM_MANAGER::instance().RemoveItem(item);
  3753. item->SetCount(item->GetCount() - 1);
  3754.  
  3755. switch (dwVnums[i])
  3756. {
  3757. case CSpecialItemGroup::GOLD:
  3758. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("돈 %d 냥을 획득했습니다."), dwCounts[i]);
  3759. break;
  3760.  
  3761. case CSpecialItemGroup::EXP:
  3762. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 부터 신비한 빛이 나옵니다."));
  3763. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d의 경험치를 획득했습니다."), dwCounts[i]);
  3764. break;
  3765.  
  3766. case CSpecialItemGroup::MOB:
  3767. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 몬스터가 나타났습니다!"));
  3768. break;
  3769.  
  3770. case CSpecialItemGroup::SLOW:
  3771. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 나온 빨간 연기를 들이마시자 움직이는 속도가 느려졌습니다!"));
  3772. break;
  3773.  
  3774. case CSpecialItemGroup::DRAIN_HP:
  3775. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자가 갑자기 폭발하였습니다! 생명력이 감소했습니다."));
  3776. break;
  3777.  
  3778. case CSpecialItemGroup::POISON:
  3779. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 나온 녹색 연기를 들이마시자 독이 온몸으로 퍼집니다!"));
  3780. break;
  3781.  
  3782. case CSpecialItemGroup::MOB_GROUP:
  3783. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 몬스터가 나타났습니다!"));
  3784. break;
  3785.  
  3786. default:
  3787. if (item_gets[i])
  3788. {
  3789. if (dwCounts[i] > 1)
  3790. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 %s 가 %d 개 나왔습니다."), item_gets[i]->GetName(), dwCounts[i]);
  3791. else
  3792. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상자에서 %s 가 나왔습니다."), item_gets[i]->GetName());
  3793. }
  3794. break;
  3795. }
  3796. }
  3797. }
  3798. else
  3799. {
  3800. ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("아무것도 얻을 수 없었습니다."));
  3801. return false;
  3802. }
  3803. }
  3804. break;
  3805.  
  3806. case ITEM_GIVE_STAT_RESET_COUNT_VNUM:
  3807. {
  3808. //PointChange(POINT_GOLD, -iCost);
  3809. PointChange(POINT_STAT_RESET_COUNT, 1);
  3810. item->SetCount(item->GetCount()-1);
  3811. }
  3812. break;
  3813.  
  3814. case 50107:
  3815. {
  3816. EffectPacket(SE_CHINA_FIREWORK);
  3817. // 스턴 공격을 올려준다
  3818. AddAffect(AFFECT_CHINA_FIREWORK, POINT_STUN_PCT, 30, AFF_CHINA_FIREWORK, 5*60, 0, true);
  3819. item->SetCount(item->GetCount()-1);
  3820. }
  3821. break;
  3822.  
  3823. case 50108:
  3824. {
  3825. if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true)
  3826. {
  3827. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 중에는 이용할 수 없는 물품입니다."));
  3828. return false;
  3829. }
  3830.  
  3831. EffectPacket(SE_SPIN_TOP);
  3832. // 스턴 공격을 올려준다
  3833. AddAffect(AFFECT_CHINA_FIREWORK, POINT_STUN_PCT, 30, AFF_CHINA_FIREWORK, 5*60, 0, true);
  3834. item->SetCount(item->GetCount()-1);
  3835. }
  3836. break;
  3837.  
  3838. case ITEM_WONSO_BEAN_VNUM:
  3839. PointChange(POINT_HP, GetMaxHP() - GetHP());
  3840. item->SetCount(item->GetCount()-1);
  3841. break;
  3842.  
  3843. case ITEM_WONSO_SUGAR_VNUM:
  3844. PointChange(POINT_SP, GetMaxSP() - GetSP());
  3845. item->SetCount(item->GetCount()-1);
  3846. break;
  3847.  
  3848. case ITEM_WONSO_FRUIT_VNUM:
  3849. PointChange(POINT_STAMINA, GetMaxStamina()-GetStamina());
  3850. item->SetCount(item->GetCount()-1);
  3851. break;
  3852.  
  3853. case 90008: // VCARD
  3854. case 90009: // VCARD
  3855. VCardUse(this, this, item);
  3856. break;
  3857.  
  3858. case ITEM_ELK_VNUM: // 돈꾸러미
  3859. {
  3860. int iGold = item->GetSocket(0);
  3861. ITEM_MANAGER::instance().RemoveItem(item);
  3862. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("돈 %d 냥을 획득했습니다."), iGold);
  3863. PointChange(POINT_GOLD, iGold);
  3864. }
  3865. break;
  3866.  
  3867. //군주의 증표
  3868. case 70021:
  3869. {
  3870. int HealPrice = quest::CQuestManager::instance().GetEventFlag("MonarchHealGold");
  3871. if (HealPrice == 0)
  3872. HealPrice = 2000000;
  3873.  
  3874. if (CMonarch::instance().HealMyEmpire(this, HealPrice))
  3875. {
  3876. char szNotice[256];
  3877. snprintf(szNotice, sizeof(szNotice), LC_TEXT("군주의 축복으로 이지역 %s 유저는 HP,SP가 모두 채워집니다."), EMPIRE_NAME(GetEmpire()));
  3878. SendNoticeMap(szNotice, GetMapIndex(), false);
  3879.  
  3880. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("군주의 축복을 사용하였습니다."));
  3881. }
  3882. }
  3883. break;
  3884.  
  3885. case 27995:
  3886. {
  3887. }
  3888. break;
  3889.  
  3890. case 71092 : // 변신 해체부 임시
  3891. {
  3892. if (m_pkChrTarget != NULL)
  3893. {
  3894. if (m_pkChrTarget->IsPolymorphed())
  3895. {
  3896. m_pkChrTarget->SetPolymorph(0);
  3897. m_pkChrTarget->RemoveAffect(AFFECT_POLYMORPH);
  3898. }
  3899. }
  3900. else
  3901. {
  3902. if (IsPolymorphed())
  3903. {
  3904. SetPolymorph(0);
  3905. RemoveAffect(AFFECT_POLYMORPH);
  3906. }
  3907. }
  3908. }
  3909. break;
  3910.  
  3911. case 71051 : // 진재가
  3912. {
  3913. // 유럽, 싱가폴, 베트남 진재가 사용금지
  3914. // if (LC_IsEurope() || LC_IsSingapore() || LC_IsVietnam())
  3915. // return false;
  3916.  
  3917. LPITEM item2;
  3918.  
  3919. if (!IsValidItemPosition(DestCell) || !(item2 = GetInventoryItem(wDestCell)))
  3920. return false;
  3921.  
  3922. if (item2->IsExchanging() == true)
  3923. return false;
  3924.  
  3925. if (item2->IsEquipped() == true) // Fix
  3926. return false;
  3927.  
  3928. if (item2->GetType() == ITEM_COSTUME) // Fix
  3929. return false;
  3930.  
  3931. if (item2->GetAttributeSetIndex() == -1)
  3932. {
  3933. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("속성을 변경할 수 없는 아이템입니다."));
  3934. return false;
  3935. }
  3936.  
  3937. if (item2->AddRareAttribute() == true)
  3938. {
  3939. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("성공적으로 속성이 추가 되었습니다"));
  3940.  
  3941. int iAddedIdx = item2->GetRareAttrCount() + 4;
  3942. char buf[21];
  3943. snprintf(buf, sizeof(buf), "%u", item2->GetID());
  3944.  
  3945. LogManager::instance().ItemLog(
  3946. GetPlayerID(),
  3947. item2->GetAttributeType(iAddedIdx),
  3948. item2->GetAttributeValue(iAddedIdx),
  3949. item->GetID(),
  3950. "ADD_RARE_ATTR",
  3951. buf,
  3952. GetDesc()->GetHostName(),
  3953. item->GetOriginalVnum());
  3954.  
  3955. item->SetCount(item->GetCount() - 1);
  3956. }
  3957. else
  3958. {
  3959. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("더 이상 이 아이템으로 속성을 추가할 수 없습니다"));
  3960. }
  3961. }
  3962. break;
  3963.  
  3964. case 71052 : // 진재경
  3965. {
  3966. // 유럽, 싱가폴, 베트남 진재가 사용금지
  3967. // if (LC_IsEurope() || LC_IsSingapore() || LC_IsVietnam())
  3968. // return false;
  3969.  
  3970. LPITEM item2;
  3971.  
  3972. if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell)))
  3973. return false;
  3974.  
  3975. if (item2->IsExchanging() == true)
  3976. return false;
  3977.  
  3978. if (item2->IsEquipped() == true) // Fix
  3979. return false;
  3980.  
  3981. if (item2->GetType() == ITEM_COSTUME) // Fix
  3982. return false;
  3983.  
  3984. if (item2->GetAttributeSetIndex() == -1)
  3985. {
  3986. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("속성을 변경할 수 없는 아이템입니다."));
  3987. return false;
  3988. }
  3989.  
  3990. if (item2->ChangeRareAttribute() == true)
  3991. {
  3992. char buf[21];
  3993. snprintf(buf, sizeof(buf), "%u", item2->GetID());
  3994. LogManager::instance().ItemLog(this, item, "CHANGE_RARE_ATTR", buf);
  3995.  
  3996. item->SetCount(item->GetCount() - 1);
  3997. }
  3998. else
  3999. {
  4000. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("변경 시킬 속성이 없습니다"));
  4001. }
  4002. }
  4003. break;
  4004.  
  4005. case ITEM_AUTO_HP_RECOVERY_S:
  4006. case ITEM_AUTO_HP_RECOVERY_M:
  4007. case ITEM_AUTO_HP_RECOVERY_L:
  4008. case ITEM_AUTO_HP_RECOVERY_X:
  4009. case ITEM_AUTO_SP_RECOVERY_S:
  4010. case ITEM_AUTO_SP_RECOVERY_M:
  4011. case ITEM_AUTO_SP_RECOVERY_L:
  4012. case ITEM_AUTO_SP_RECOVERY_X:
  4013. // 무시무시하지만 이전에 하던 걸 고치기는 무섭고...
  4014. // 그래서 그냥 하드 코딩. 선물 상자용 자동물약 아이템들.
  4015. case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_XS:
  4016. case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_S:
  4017. case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_XS:
  4018. case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_S:
  4019. case FUCKING_BRAZIL_ITEM_AUTO_SP_RECOVERY_S:
  4020. case FUCKING_BRAZIL_ITEM_AUTO_HP_RECOVERY_S:
  4021. {
  4022. if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true)
  4023. {
  4024. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장에서 사용하실 수 없습니다."));
  4025. return false;
  4026. }
  4027.  
  4028. EAffectTypes type = AFFECT_NONE;
  4029. bool isSpecialPotion = false;
  4030.  
  4031. switch (item->GetVnum())
  4032. {
  4033. case ITEM_AUTO_HP_RECOVERY_X:
  4034. isSpecialPotion = true;
  4035.  
  4036. case ITEM_AUTO_HP_RECOVERY_S:
  4037. case ITEM_AUTO_HP_RECOVERY_M:
  4038. case ITEM_AUTO_HP_RECOVERY_L:
  4039. case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_XS:
  4040. case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_S:
  4041. case FUCKING_BRAZIL_ITEM_AUTO_HP_RECOVERY_S:
  4042. type = AFFECT_AUTO_HP_RECOVERY;
  4043. break;
  4044.  
  4045. case ITEM_AUTO_SP_RECOVERY_X:
  4046. isSpecialPotion = true;
  4047.  
  4048. case ITEM_AUTO_SP_RECOVERY_S:
  4049. case ITEM_AUTO_SP_RECOVERY_M:
  4050. case ITEM_AUTO_SP_RECOVERY_L:
  4051. case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_XS:
  4052. case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_S:
  4053. case FUCKING_BRAZIL_ITEM_AUTO_SP_RECOVERY_S:
  4054. type = AFFECT_AUTO_SP_RECOVERY;
  4055. break;
  4056. }
  4057.  
  4058. if (AFFECT_NONE == type)
  4059. break;
  4060.  
  4061. if (item->GetCount() > 1)
  4062. {
  4063. int pos = GetEmptyInventory(item->GetSize());
  4064.  
  4065. if (-1 == pos)
  4066. {
  4067. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("소지품에 빈 공간이 없습니다."));
  4068. break;
  4069. }
  4070.  
  4071. item->SetCount( item->GetCount() - 1 );
  4072.  
  4073. LPITEM item2 = ITEM_MANAGER::instance().CreateItem( item->GetVnum(), 1 );
  4074. item2->AddToCharacter(this, TItemPos(INVENTORY, pos));
  4075.  
  4076. if (item->GetSocket(1) != 0)
  4077. {
  4078. item2->SetSocket(1, item->GetSocket(1));
  4079. }
  4080.  
  4081. item = item2;
  4082. }
  4083.  
  4084. CAffect* pAffect = FindAffect( type );
  4085.  
  4086. if (NULL == pAffect)
  4087. {
  4088. EPointTypes bonus = POINT_NONE;
  4089.  
  4090. if (true == isSpecialPotion)
  4091. {
  4092. if (type == AFFECT_AUTO_HP_RECOVERY)
  4093. {
  4094. bonus = POINT_MAX_HP_PCT;
  4095. }
  4096. else if (type == AFFECT_AUTO_SP_RECOVERY)
  4097. {
  4098. bonus = POINT_MAX_SP_PCT;
  4099. }
  4100. }
  4101.  
  4102. AddAffect( type, bonus, 4, item->GetID(), INFINITE_AFFECT_DURATION, 0, true, false);
  4103.  
  4104. item->Lock(true);
  4105. item->SetSocket(0, true);
  4106.  
  4107. AutoRecoveryItemProcess( type );
  4108. }
  4109. else
  4110. {
  4111. if (item->GetID() == pAffect->dwFlag)
  4112. {
  4113. RemoveAffect( pAffect );
  4114.  
  4115. item->Lock(false);
  4116. item->SetSocket(0, false);
  4117. }
  4118. else
  4119. {
  4120. LPITEM old = FindItemByID( pAffect->dwFlag );
  4121.  
  4122. if (NULL != old)
  4123. {
  4124. old->Lock(false);
  4125. old->SetSocket(0, false);
  4126. }
  4127.  
  4128. RemoveAffect( pAffect );
  4129.  
  4130. EPointTypes bonus = POINT_NONE;
  4131.  
  4132. if (true == isSpecialPotion)
  4133. {
  4134. if (type == AFFECT_AUTO_HP_RECOVERY)
  4135. {
  4136. bonus = POINT_MAX_HP_PCT;
  4137. }
  4138. else if (type == AFFECT_AUTO_SP_RECOVERY)
  4139. {
  4140. bonus = POINT_MAX_SP_PCT;
  4141. }
  4142. }
  4143.  
  4144. AddAffect( type, bonus, 4, item->GetID(), INFINITE_AFFECT_DURATION, 0, true, false);
  4145.  
  4146. item->Lock(true);
  4147. item->SetSocket(0, true);
  4148.  
  4149. AutoRecoveryItemProcess( type );
  4150. }
  4151. }
  4152. }
  4153. break;
  4154. }
  4155. break;
  4156.  
  4157. case USE_CLEAR:
  4158. {
  4159. RemoveBadAffect();
  4160. item->SetCount(item->GetCount() - 1);
  4161. }
  4162. break;
  4163.  
  4164. case USE_INVISIBILITY:
  4165. {
  4166. if (item->GetVnum() == 70026)
  4167. {
  4168. quest::CQuestManager& q = quest::CQuestManager::instance();
  4169. quest::PC* pPC = q.GetPC(GetPlayerID());
  4170.  
  4171. if (pPC != NULL)
  4172. {
  4173. int last_use_time = pPC->GetFlag("mirror_of_disapper.last_use_time");
  4174.  
  4175. if (get_global_time() - last_use_time < 10*60)
  4176. {
  4177. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아직 사용할 수 없습니다."));
  4178. return false;
  4179. }
  4180.  
  4181. pPC->SetFlag("mirror_of_disapper.last_use_time", get_global_time());
  4182. }
  4183. }
  4184.  
  4185. AddAffect(AFFECT_INVISIBILITY, POINT_NONE, 0, AFF_INVISIBILITY, 300, 0, true);
  4186. item->SetCount(item->GetCount() - 1);
  4187. }
  4188. break;
  4189.  
  4190. case USE_POTION_NODELAY:
  4191. {
  4192. if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true)
  4193. {
  4194. if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit") > 0)
  4195. {
  4196. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장에서 사용하실 수 없습니다."));
  4197. return false;
  4198. }
  4199.  
  4200. switch (item->GetVnum())
  4201. {
  4202. case 70020 :
  4203. case 71018 :
  4204. case 71019 :
  4205. case 71020 :
  4206. if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count") < 10000)
  4207. {
  4208. if (m_nPotionLimit <= 0)
  4209. {
  4210. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("사용 제한량을 초과하였습니다."));
  4211. return false;
  4212. }
  4213. }
  4214. break;
  4215.  
  4216. default :
  4217. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장에서 사용하실 수 없습니다."));
  4218. return false;
  4219. }
  4220. }
  4221.  
  4222. bool used = false;
  4223.  
  4224. if (item->GetValue(0) != 0) // HP 절대값 회복
  4225. {
  4226. if (GetHP() < GetMaxHP())
  4227. {
  4228. PointChange(POINT_HP, item->GetValue(0) * (100 + GetPoint(POINT_POTION_BONUS)) / 100);
  4229. EffectPacket(SE_HPUP_RED);
  4230. used = TRUE;
  4231. }
  4232. }
  4233.  
  4234. if (item->GetValue(1) != 0) // SP 절대값 회복
  4235. {
  4236. if (GetSP() < GetMaxSP())
  4237. {
  4238. PointChange(POINT_SP, item->GetValue(1) * (100 + GetPoint(POINT_POTION_BONUS)) / 100);
  4239. EffectPacket(SE_SPUP_BLUE);
  4240. used = TRUE;
  4241. }
  4242. }
  4243.  
  4244. if (item->GetValue(3) != 0) // HP % 회복
  4245. {
  4246. if (GetHP() < GetMaxHP())
  4247. {
  4248. PointChange(POINT_HP, item->GetValue(3) * GetMaxHP() / 100);
  4249. EffectPacket(SE_HPUP_RED);
  4250. used = TRUE;
  4251. }
  4252. }
  4253.  
  4254. if (item->GetValue(4) != 0) // SP % 회복
  4255. {
  4256. if (GetSP() < GetMaxSP())
  4257. {
  4258. PointChange(POINT_SP, item->GetValue(4) * GetMaxSP() / 100);
  4259. EffectPacket(SE_SPUP_BLUE);
  4260. used = TRUE;
  4261. }
  4262. }
  4263.  
  4264. if (used)
  4265. {
  4266. if (item->GetVnum() == 50085 || item->GetVnum() == 50086)
  4267. {
  4268. if (test_server)
  4269. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("월병 또는 종자 를 사용하였습니다"));
  4270. SetUseSeedOrMoonBottleTime();
  4271. }
  4272. if (GetDungeon())
  4273. GetDungeon()->UsePotion(this);
  4274.  
  4275. if (GetWarMap())
  4276. GetWarMap()->UsePotion(this, item);
  4277.  
  4278. m_nPotionLimit--;
  4279.  
  4280. //RESTRICT_USE_SEED_OR_MOONBOTTLE
  4281. item->SetCount(item->GetCount() - 1);
  4282. //END_RESTRICT_USE_SEED_OR_MOONBOTTLE
  4283. }
  4284. }
  4285. break;
  4286.  
  4287. case USE_POTION:
  4288. if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true)
  4289. {
  4290. if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit") > 0)
  4291. {
  4292. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장에서 사용하실 수 없습니다."));
  4293. return false;
  4294. }
  4295.  
  4296. switch (item->GetVnum())
  4297. {
  4298. case 27001 :
  4299. case 27002 :
  4300. case 27003 :
  4301. case 27004 :
  4302. case 27005 :
  4303. case 27006 :
  4304. if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count") < 10000)
  4305. {
  4306. if (m_nPotionLimit <= 0)
  4307. {
  4308. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("사용 제한량을 초과하였습니다."));
  4309. return false;
  4310. }
  4311. }
  4312. break;
  4313.  
  4314. default :
  4315. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장에서 사용하실 수 없습니다."));
  4316. return false;
  4317. }
  4318. }
  4319.  
  4320. if (item->GetValue(1) != 0)
  4321. {
  4322. if (GetPoint(POINT_SP_RECOVERY) + GetSP() >= GetMaxSP())
  4323. {
  4324. return false;
  4325. }
  4326.  
  4327. PointChange(POINT_SP_RECOVERY, item->GetValue(1) * MIN(200, (100 + GetPoint(POINT_POTION_BONUS))) / 100);
  4328. StartAffectEvent();
  4329. EffectPacket(SE_SPUP_BLUE);
  4330. }
  4331.  
  4332. if (item->GetValue(0) != 0)
  4333. {
  4334. if (GetPoint(POINT_HP_RECOVERY) + GetHP() >= GetMaxHP())
  4335. {
  4336. return false;
  4337. }
  4338.  
  4339. PointChange(POINT_HP_RECOVERY, item->GetValue(0) * MIN(200, (100 + GetPoint(POINT_POTION_BONUS))) / 100);
  4340. StartAffectEvent();
  4341. EffectPacket(SE_HPUP_RED);
  4342. }
  4343.  
  4344. if (GetDungeon())
  4345. GetDungeon()->UsePotion(this);
  4346.  
  4347. if (GetWarMap())
  4348. GetWarMap()->UsePotion(this, item);
  4349.  
  4350. item->SetCount(item->GetCount() - 1);
  4351. m_nPotionLimit--;
  4352. break;
  4353.  
  4354. case USE_POTION_CONTINUE:
  4355. {
  4356. if (item->GetValue(0) != 0)
  4357. {
  4358. AddAffect(AFFECT_HP_RECOVER_CONTINUE, POINT_HP_RECOVER_CONTINUE, item->GetValue(0), 0, item->GetValue(2), 0, true);
  4359. }
  4360. else if (item->GetValue(1) != 0)
  4361. {
  4362. AddAffect(AFFECT_SP_RECOVER_CONTINUE, POINT_SP_RECOVER_CONTINUE, item->GetValue(1), 0, item->GetValue(2), 0, true);
  4363. }
  4364. else
  4365. return false;
  4366. }
  4367.  
  4368. if (GetDungeon())
  4369. GetDungeon()->UsePotion(this);
  4370.  
  4371. if (GetWarMap())
  4372. GetWarMap()->UsePotion(this, item);
  4373.  
  4374. item->SetCount(item->GetCount() - 1);
  4375. break;
  4376.  
  4377. case USE_ABILITY_UP:
  4378. {
  4379. switch (item->GetValue(0))
  4380. {
  4381. case APPLY_MOV_SPEED:
  4382. AddAffect(AFFECT_MOV_SPEED, POINT_MOV_SPEED, item->GetValue(2), AFF_MOV_SPEED_POTION, item->GetValue(1), 0, true);
  4383. break;
  4384.  
  4385. case APPLY_ATT_SPEED:
  4386. AddAffect(AFFECT_ATT_SPEED, POINT_ATT_SPEED, item->GetValue(2), AFF_ATT_SPEED_POTION, item->GetValue(1), 0, true);
  4387. break;
  4388.  
  4389. case APPLY_STR:
  4390. AddAffect(AFFECT_STR, POINT_ST, item->GetValue(2), 0, item->GetValue(1), 0, true);
  4391. break;
  4392.  
  4393. case APPLY_DEX:
  4394. AddAffect(AFFECT_DEX, POINT_DX, item->GetValue(2), 0, item->GetValue(1), 0, true);
  4395. break;
  4396.  
  4397. case APPLY_CON:
  4398. AddAffect(AFFECT_CON, POINT_HT, item->GetValue(2), 0, item->GetValue(1), 0, true);
  4399. break;
  4400.  
  4401. case APPLY_INT:
  4402. AddAffect(AFFECT_INT, POINT_IQ, item->GetValue(2), 0, item->GetValue(1), 0, true);
  4403. break;
  4404.  
  4405. case APPLY_CAST_SPEED:
  4406. AddAffect(AFFECT_CAST_SPEED, POINT_CASTING_SPEED, item->GetValue(2), 0, item->GetValue(1), 0, true);
  4407. break;
  4408.  
  4409. case APPLY_ATT_GRADE_BONUS:
  4410. AddAffect(AFFECT_ATT_GRADE, POINT_ATT_GRADE_BONUS,
  4411. item->GetValue(2), 0, item->GetValue(1), 0, true);
  4412. break;
  4413.  
  4414. case APPLY_DEF_GRADE_BONUS:
  4415. AddAffect(AFFECT_DEF_GRADE, POINT_DEF_GRADE_BONUS,
  4416. item->GetValue(2), 0, item->GetValue(1), 0, true);
  4417. break;
  4418. }
  4419. }
  4420.  
  4421. if (GetDungeon())
  4422. GetDungeon()->UsePotion(this);
  4423.  
  4424. if (GetWarMap())
  4425. GetWarMap()->UsePotion(this, item);
  4426.  
  4427. item->SetCount(item->GetCount() - 1);
  4428. break;
  4429.  
  4430. case USE_TALISMAN:
  4431. {
  4432. const int TOWN_PORTAL = 1;
  4433. const int MEMORY_PORTAL = 2;
  4434.  
  4435.  
  4436. // gm_guild_build, oxevent 맵에서 귀환부 귀환기억부 를 사용못하게 막음
  4437. if (GetMapIndex() == 200 || GetMapIndex() == 113)
  4438. {
  4439. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("현재 위치에서 사용할 수 없습니다."));
  4440. return false;
  4441. }
  4442.  
  4443. if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true)
  4444. {
  4445. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 중에는 이용할 수 없는 물품입니다."));
  4446. return false;
  4447. }
  4448.  
  4449. if (m_pkWarpEvent)
  4450. {
  4451. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이동할 준비가 되어있음으로 귀환부를 사용할수 없습니다"));
  4452. return false;
  4453. }
  4454.  
  4455. // CONSUME_LIFE_WHEN_USE_WARP_ITEM
  4456. int consumeLife = CalculateConsume(this);
  4457.  
  4458. if (consumeLife < 0)
  4459. return false;
  4460. // END_OF_CONSUME_LIFE_WHEN_USE_WARP_ITEM
  4461.  
  4462. if (item->GetValue(0) == TOWN_PORTAL) // 귀환부
  4463. {
  4464. if (item->GetSocket(0) == 0)
  4465. {
  4466. if (!GetDungeon())
  4467. if (!GiveRecallItem(item))
  4468. return false;
  4469.  
  4470. PIXEL_POSITION posWarp;
  4471.  
  4472. if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(GetMapIndex(), GetEmpire(), posWarp))
  4473. {
  4474. // CONSUME_LIFE_WHEN_USE_WARP_ITEM
  4475. PointChange(POINT_HP, -consumeLife, false);
  4476. // END_OF_CONSUME_LIFE_WHEN_USE_WARP_ITEM
  4477.  
  4478. WarpSet(posWarp.x, posWarp.y);
  4479. }
  4480. else
  4481. {
  4482. sys_err("CHARACTER::UseItem : cannot find spawn position (name %s, %d x %d)", GetName(), GetX(), GetY());
  4483. }
  4484. }
  4485. else
  4486. {
  4487. if (test_server)
  4488. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("원래 위치로 복귀"));
  4489.  
  4490. ProcessRecallItem(item);
  4491. }
  4492. }
  4493. else if (item->GetValue(0) == MEMORY_PORTAL) // 귀환기억부
  4494. {
  4495. if (item->GetSocket(0) == 0)
  4496. {
  4497. if (GetDungeon())
  4498. {
  4499. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("던전 안에서는 %s%s 사용할 수 없습니다."),
  4500. item->GetName(),
  4501. g_iUseLocale ? "" : (under_han(item->GetName()) ? LC_TEXT("을") : LC_TEXT("를")));
  4502. return false;
  4503. }
  4504.  
  4505. if (!GiveRecallItem(item))
  4506. return false;
  4507. }
  4508. else
  4509. {
  4510. // CONSUME_LIFE_WHEN_USE_WARP_ITEM
  4511. PointChange(POINT_HP, -consumeLife, false);
  4512. // END_OF_CONSUME_LIFE_WHEN_USE_WARP_ITEM
  4513.  
  4514. ProcessRecallItem(item);
  4515. }
  4516. }
  4517. }
  4518. break;
  4519.  
  4520. case USE_TUNING:
  4521. case USE_DETACHMENT:
  4522. {
  4523. LPITEM item2;
  4524.  
  4525. if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell)))
  4526. return false;
  4527.  
  4528. if (item2->IsExchanging())
  4529. return false;
  4530.  
  4531. if (item2->IsEquipped()) // Fix
  4532. return false;
  4533.  
  4534. if (item2->GetVnum() >= 28330 && item2->GetVnum() <= 28343) // 영석+3
  4535. {
  4536. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("+3 영석은 이 아이템으로 개량할 수 없습니다"));
  4537. return false;
  4538. }
  4539.  
  4540. if (item2->GetVnum() >= 28430 && item2->GetVnum() <= 28443) // 영석+4
  4541. {
  4542. if (item->GetVnum() == 71056) // 청룡의숨결
  4543. {
  4544. RefineItem(item, item2);
  4545. }
  4546. else
  4547. {
  4548. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("영석은 이 아이템으로 개량할 수 없습니다"));
  4549. }
  4550. }
  4551. else
  4552. {
  4553. RefineItem(item, item2);
  4554. }
  4555. }
  4556. break;
  4557.  
  4558. // ACCESSORY_REFINE & ADD/CHANGE_ATTRIBUTES
  4559. case USE_PUT_INTO_BELT_SOCKET:
  4560. case USE_PUT_INTO_RING_SOCKET:
  4561. case USE_PUT_INTO_ACCESSORY_SOCKET:
  4562. case USE_ADD_ACCESSORY_SOCKET:
  4563. case USE_CLEAN_SOCKET:
  4564. case USE_CHANGE_ATTRIBUTE:
  4565. case USE_CHANGE_ATTRIBUTE2 :
  4566. case USE_ADD_ATTRIBUTE:
  4567. case USE_ADD_ATTRIBUTE2:
  4568. {
  4569. LPITEM item2;
  4570. if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell)))
  4571. return false;
  4572.  
  4573. if (item2->IsEquipped())
  4574. {
  4575. BuffOnAttr_RemoveBuffsFromItem(item2);
  4576. }
  4577.  
  4578. // [NOTE] 코스튬 아이템에는 아이템 최초 생성시 랜덤 속성을 부여하되, 재경재가 등등은 막아달라는 요청이 있었음.
  4579. // 원래 ANTI_CHANGE_ATTRIBUTE 같은 아이템 Flag를 추가하여 기획 레벨에서 유연하게 컨트롤 할 수 있도록 할 예정이었으나
  4580. // 그딴거 필요없으니 닥치고 빨리 해달래서 그냥 여기서 막음... -_-
  4581. if (ITEM_COSTUME == item2->GetType())
  4582. {
  4583. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("속성을 변경할 수 없는 아이템입니다."));
  4584. return false;
  4585. }
  4586.  
  4587. if (item2->IsExchanging())
  4588. return false;
  4589.  
  4590. if (item2->IsEquipped()) // Fix
  4591. return false;
  4592.  
  4593. switch (item->GetSubType())
  4594. {
  4595. case USE_CLEAN_SOCKET:
  4596. {
  4597. int i;
  4598. for (i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  4599. {
  4600. if (item2->GetSocket(i) == ITEM_BROKEN_METIN_VNUM)
  4601. break;
  4602. }
  4603.  
  4604. if (i == ITEM_SOCKET_MAX_NUM)
  4605. {
  4606. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("청소할 석이 박혀있지 않습니다."));
  4607. return false;
  4608. }
  4609.  
  4610. int j = 0;
  4611.  
  4612. for (i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  4613. {
  4614. if (item2->GetSocket(i) != ITEM_BROKEN_METIN_VNUM && item2->GetSocket(i) != 0)
  4615. item2->SetSocket(j++, item2->GetSocket(i));
  4616. }
  4617.  
  4618. for (; j < ITEM_SOCKET_MAX_NUM; ++j)
  4619. {
  4620. if (item2->GetSocket(j) > 0)
  4621. item2->SetSocket(j, 1);
  4622. }
  4623.  
  4624. {
  4625. char buf[21];
  4626. snprintf(buf, sizeof(buf), "%u", item2->GetID());
  4627. LogManager::instance().ItemLog(this, item, "CLEAN_SOCKET", buf);
  4628. }
  4629.  
  4630. item->SetCount(item->GetCount() - 1);
  4631.  
  4632. }
  4633. break;
  4634.  
  4635. case USE_CHANGE_ATTRIBUTE :
  4636. if (item2->GetAttributeSetIndex() == -1)
  4637. {
  4638. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("속성을 변경할 수 없는 아이템입니다."));
  4639. return false;
  4640. }
  4641.  
  4642. if (item2->GetAttributeCount() == 0)
  4643. {
  4644. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("변경할 속성이 없습니다."));
  4645. return false;
  4646. }
  4647.  
  4648. if (GM_PLAYER == GetGMLevel() && false == test_server)
  4649. {
  4650. //
  4651. // Event Flag 를 통해 이전에 아이템 속성 변경을 한 시간으로 부터 충분한 시간이 흘렀는지 검사하고
  4652. // 시간이 충분히 흘렀다면 현재 속성변경에 대한 시간을 설정해 준다.
  4653. //
  4654.  
  4655. DWORD dwChangeItemAttrCycle = quest::CQuestManager::instance().GetEventFlag(msc_szChangeItemAttrCycleFlag);
  4656. if (dwChangeItemAttrCycle < msc_dwDefaultChangeItemAttrCycle)
  4657. dwChangeItemAttrCycle = msc_dwDefaultChangeItemAttrCycle;
  4658.  
  4659. quest::PC* pPC = quest::CQuestManager::instance().GetPC(GetPlayerID());
  4660.  
  4661. if (pPC)
  4662. {
  4663. DWORD dwNowMin = get_global_time() / 60;
  4664.  
  4665. // DWORD dwLastChangeItemAttrMin = pPC->GetFlag(msc_szLastChangeItemAttrFlag);
  4666.  
  4667. // if (dwLastChangeItemAttrMin + dwChangeItemAttrCycle > dwNowMin)
  4668. // {
  4669. // ChatPacket(CHAT_TYPE_INFO, LC_TEXT("속성을 바꾼지 %d분 이내에는 다시 변경할 수 없습니다.(%d 분 남음)"),
  4670. // dwChangeItemAttrCycle, dwChangeItemAttrCycle - (dwNowMin - dwLastChangeItemAttrMin));
  4671. // return false;
  4672. // }
  4673.  
  4674. pPC->SetFlag(msc_szLastChangeItemAttrFlag, dwNowMin);
  4675. }
  4676. }
  4677.  
  4678. if (item->GetSubType() == USE_CHANGE_ATTRIBUTE2)
  4679. {
  4680. int aiChangeProb[ITEM_ATTRIBUTE_MAX_LEVEL] =
  4681. {
  4682. 0, 0, 30, 40, 3
  4683. };
  4684.  
  4685. item2->ChangeAttribute(aiChangeProb);
  4686. }
  4687. else if (item->GetVnum() == 76014)
  4688. {
  4689. int aiChangeProb[ITEM_ATTRIBUTE_MAX_LEVEL] =
  4690. {
  4691. 0, 10, 50, 39, 1
  4692. };
  4693.  
  4694. item2->ChangeAttribute(aiChangeProb);
  4695. }
  4696.  
  4697. else
  4698. {
  4699. // 연재경 특수처리
  4700. // 절대로 연재가 추가 안될거라 하여 하드 코딩함.
  4701. if (item->GetVnum() == 71151 || item->GetVnum() == 76023)
  4702. {
  4703. if ((item2->GetType() == ITEM_WEAPON)
  4704. || (item2->GetType() == ITEM_ARMOR && item2->GetSubType() == ARMOR_BODY))
  4705. {
  4706. bool bCanUse = true;
  4707. for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i)
  4708. {
  4709. if (item2->GetLimitType(i) == LIMIT_LEVEL && item2->GetLimitValue(i) > 40)
  4710. {
  4711. bCanUse = false;
  4712. break;
  4713. }
  4714. }
  4715. if (false == bCanUse)
  4716. {
  4717. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("적용 레벨보다 높아 사용이 불가능합니다."));
  4718. break;
  4719. }
  4720. }
  4721. else
  4722. {
  4723. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("무기와 갑옷에만 사용 가능합니다."));
  4724. break;
  4725. }
  4726. }
  4727. item2->ChangeAttribute();
  4728. }
  4729.  
  4730. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("속성을 변경하였습니다."));
  4731. {
  4732. char buf[21];
  4733. snprintf(buf, sizeof(buf), "%u", item2->GetID());
  4734. LogManager::instance().ItemLog(this, item, "CHANGE_ATTRIBUTE", buf);
  4735. }
  4736.  
  4737. item->SetCount(item->GetCount() - 1);
  4738. break;
  4739.  
  4740. case USE_ADD_ATTRIBUTE :
  4741. if (item2->GetAttributeSetIndex() == -1)
  4742. {
  4743. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("속성을 변경할 수 없는 아이템입니다."));
  4744. return false;
  4745. }
  4746.  
  4747. if (item2->GetAttributeCount() < 4)
  4748. {
  4749. // 연재가 특수처리
  4750. // 절대로 연재가 추가 안될거라 하여 하드 코딩함.
  4751. if (item->GetVnum() == 71152 || item->GetVnum() == 76024)
  4752. {
  4753. if ((item2->GetType() == ITEM_WEAPON)
  4754. || (item2->GetType() == ITEM_ARMOR && item2->GetSubType() == ARMOR_BODY))
  4755. {
  4756. bool bCanUse = true;
  4757. for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i)
  4758. {
  4759. if (item2->GetLimitType(i) == LIMIT_LEVEL && item2->GetLimitValue(i) > 40)
  4760. {
  4761. bCanUse = false;
  4762. break;
  4763. }
  4764. }
  4765. if (false == bCanUse)
  4766. {
  4767. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("적용 레벨보다 높아 사용이 불가능합니다."));
  4768. break;
  4769. }
  4770. }
  4771. else
  4772. {
  4773. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("무기와 갑옷에만 사용 가능합니다."));
  4774. break;
  4775. }
  4776. }
  4777. char buf[21];
  4778. snprintf(buf, sizeof(buf), "%u", item2->GetID());
  4779.  
  4780. if (number(1, 100) <= aiItemAttributeAddPercent[item2->GetAttributeCount()])
  4781. {
  4782. item2->AddAttribute();
  4783. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("속성 추가에 성공하였습니다."));
  4784.  
  4785. int iAddedIdx = item2->GetAttributeCount() - 1;
  4786. LogManager::instance().ItemLog(
  4787. GetPlayerID(),
  4788. item2->GetAttributeType(iAddedIdx),
  4789. item2->GetAttributeValue(iAddedIdx),
  4790. item->GetID(),
  4791. "ADD_ATTRIBUTE_SUCCESS",
  4792. buf,
  4793. GetDesc()->GetHostName(),
  4794. item->GetOriginalVnum());
  4795. }
  4796. else
  4797. {
  4798. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("속성 추가에 실패하였습니다."));
  4799. LogManager::instance().ItemLog(this, item, "ADD_ATTRIBUTE_FAIL", buf);
  4800. }
  4801.  
  4802. item->SetCount(item->GetCount() - 1);
  4803. }
  4804. else
  4805. {
  4806. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("더이상 이 아이템을 이용하여 속성을 추가할 수 없습니다."));
  4807. }
  4808. break;
  4809.  
  4810. case USE_ADD_ATTRIBUTE2 :
  4811. // 축복의 구슬
  4812. // 재가비서를 통해 속성을 4개 추가 시킨 아이템에 대해서 하나의 속성을 더 붙여준다.
  4813. if (item2->GetAttributeSetIndex() == -1)
  4814. {
  4815. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("속성을 변경할 수 없는 아이템입니다."));
  4816. return false;
  4817. }
  4818.  
  4819. // 속성이 이미 4개 추가 되었을 때만 속성을 추가 가능하다.
  4820. if (item2->GetAttributeCount() == 4)
  4821. {
  4822. char buf[21];
  4823. snprintf(buf, sizeof(buf), "%u", item2->GetID());
  4824.  
  4825. if (number(1, 100) <= aiItemAttributeAddPercent[item2->GetAttributeCount()])
  4826. {
  4827. item2->AddAttribute();
  4828. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("속성 추가에 성공하였습니다."));
  4829.  
  4830. int iAddedIdx = item2->GetAttributeCount() - 1;
  4831. LogManager::instance().ItemLog(
  4832. GetPlayerID(),
  4833. item2->GetAttributeType(iAddedIdx),
  4834. item2->GetAttributeValue(iAddedIdx),
  4835. item->GetID(),
  4836. "ADD_ATTRIBUTE2_SUCCESS",
  4837. buf,
  4838. GetDesc()->GetHostName(),
  4839. item->GetOriginalVnum());
  4840. }
  4841. else
  4842. {
  4843. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("속성 추가에 실패하였습니다."));
  4844. LogManager::instance().ItemLog(this, item, "ADD_ATTRIBUTE2_FAIL", buf);
  4845. }
  4846.  
  4847. item->SetCount(item->GetCount() - 1);
  4848. }
  4849. else if (item2->GetAttributeCount() == 5)
  4850. {
  4851. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("더 이상 이 아이템을 이용하여 속성을 추가할 수 없습니다."));
  4852. }
  4853. else if (item2->GetAttributeCount() < 4)
  4854. {
  4855. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("먼저 재가비서를 이용하여 속성을 추가시켜 주세요."));
  4856. }
  4857. else
  4858. {
  4859. // wtf ?!
  4860. sys_err("ADD_ATTRIBUTE2 : Item has wrong AttributeCount(%d)", item2->GetAttributeCount());
  4861. }
  4862. break;
  4863.  
  4864. case USE_ADD_ACCESSORY_SOCKET:
  4865. {
  4866. char buf[21];
  4867. snprintf(buf, sizeof(buf), "%u", item2->GetID());
  4868.  
  4869. if (item2->IsAccessoryForSocket())
  4870. {
  4871. if (item2->GetAccessorySocketMaxGrade() < ITEM_ACCESSORY_SOCKET_MAX_NUM)
  4872. {
  4873. if (number(1, 100) <= 50)
  4874. {
  4875. item2->SetAccessorySocketMaxGrade(item2->GetAccessorySocketMaxGrade() + 1);
  4876. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("소켓이 성공적으로 추가되었습니다."));
  4877. LogManager::instance().ItemLog(this, item, "ADD_SOCKET_SUCCESS", buf);
  4878. }
  4879. else
  4880. {
  4881. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("소켓 추가에 실패하였습니다."));
  4882. LogManager::instance().ItemLog(this, item, "ADD_SOCKET_FAIL", buf);
  4883. }
  4884.  
  4885. item->SetCount(item->GetCount() - 1);
  4886. }
  4887. else
  4888. {
  4889. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 액세서리에는 더이상 소켓을 추가할 공간이 없습니다."));
  4890. }
  4891. }
  4892. else
  4893. {
  4894. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템으로 소켓을 추가할 수 없는 아이템입니다."));
  4895. }
  4896. }
  4897. break;
  4898.  
  4899. case USE_PUT_INTO_BELT_SOCKET:
  4900. case USE_PUT_INTO_ACCESSORY_SOCKET:
  4901. if (item2->IsAccessoryForSocket() && item->CanPutInto(item2))
  4902. {
  4903. char buf[21];
  4904. snprintf(buf, sizeof(buf), "%u", item2->GetID());
  4905.  
  4906. if (item2->GetAccessorySocketGrade() < item2->GetAccessorySocketMaxGrade())
  4907. {
  4908. if (number(1, 100) <= aiAccessorySocketPutPct[item2->GetAccessorySocketGrade()])
  4909. {
  4910. item2->SetAccessorySocketGrade(item2->GetAccessorySocketGrade() + 1);
  4911. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("장착에 성공하였습니다."));
  4912. LogManager::instance().ItemLog(this, item, "PUT_SOCKET_SUCCESS", buf);
  4913. }
  4914. else
  4915. {
  4916. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("장착에 실패하였습니다."));
  4917. LogManager::instance().ItemLog(this, item, "PUT_SOCKET_FAIL", buf);
  4918. }
  4919.  
  4920. item->SetCount(item->GetCount() - 1);
  4921. }
  4922. else
  4923. {
  4924. if (item2->GetAccessorySocketMaxGrade() == 0)
  4925. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("먼저 다이아몬드로 악세서리에 소켓을 추가해야합니다."));
  4926. else if (item2->GetAccessorySocketMaxGrade() < ITEM_ACCESSORY_SOCKET_MAX_NUM)
  4927. {
  4928. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 액세서리에는 더이상 장착할 소켓이 없습니다."));
  4929. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다이아몬드로 소켓을 추가해야합니다."));
  4930. }
  4931. else
  4932. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 액세서리에는 더이상 보석을 장착할 수 없습니다."));
  4933. }
  4934. }
  4935. else
  4936. {
  4937. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템을 장착할 수 없습니다."));
  4938. }
  4939. break;
  4940. }
  4941. if (item2->IsEquipped())
  4942. {
  4943. BuffOnAttr_AddBuffsFromItem(item2);
  4944. }
  4945. }
  4946. break;
  4947. // END_OF_ACCESSORY_REFINE & END_OF_ADD_ATTRIBUTES & END_OF_CHANGE_ATTRIBUTES
  4948.  
  4949. case USE_BAIT:
  4950. {
  4951.  
  4952. if (m_pkFishingEvent)
  4953. {
  4954. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("낚시 중에 미끼를 갈아끼울 수 없습니다."));
  4955. return false;
  4956. }
  4957.  
  4958. LPITEM weapon = GetWear(WEAR_WEAPON);
  4959.  
  4960. if (!weapon || weapon->GetType() != ITEM_ROD)
  4961. return false;
  4962.  
  4963. if (weapon->GetSocket(2))
  4964. {
  4965. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이미 꽂혀있던 미끼를 빼고 %s를 끼웁니다."), item->GetName());
  4966. }
  4967. else
  4968. {
  4969. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("낚시대에 %s를 미끼로 끼웁니다."), item->GetName());
  4970. }
  4971.  
  4972. weapon->SetSocket(2, item->GetValue(0));
  4973. item->SetCount(item->GetCount() - 1);
  4974. }
  4975. break;
  4976.  
  4977. case USE_MOVE:
  4978. case USE_TREASURE_BOX:
  4979. case USE_MONEYBAG:
  4980. break;
  4981.  
  4982. case USE_AFFECT :
  4983. {
  4984. if (FindAffect(item->GetValue(0), aApplyInfo[item->GetValue(1)].bPointType))
  4985. {
  4986. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이미 효과가 걸려 있습니다."));
  4987. }
  4988. else
  4989. {
  4990. // PC_BANG_ITEM_ADD
  4991. if (item->IsPCBangItem() == true)
  4992. {
  4993. // PC방인지 체크해서 처리
  4994. if (CPCBangManager::instance().IsPCBangIP(GetDesc()->GetHostName()) == false)
  4995. {
  4996. // PC방이 아님!
  4997. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템은 PC방에서만 사용할 수 있습니다."));
  4998. return false;
  4999. }
  5000. }
  5001. // END_PC_BANG_ITEM_ADD
  5002.  
  5003. AddAffect(item->GetValue(0), aApplyInfo[item->GetValue(1)].bPointType, item->GetValue(2), 0, item->GetValue(3), 0, false);
  5004. item->SetCount(item->GetCount() - 1);
  5005. }
  5006. }
  5007. break;
  5008.  
  5009. case USE_CREATE_STONE:
  5010. AutoGiveItem(number(28000, 28013));
  5011. item->SetCount(item->GetCount() - 1);
  5012. break;
  5013.  
  5014. // 물약 제조 스킬용 레시피 처리
  5015. case USE_RECIPE :
  5016. {
  5017. LPITEM pSource1 = FindSpecifyItem(item->GetValue(1));
  5018. DWORD dwSourceCount1 = item->GetValue(2);
  5019.  
  5020. LPITEM pSource2 = FindSpecifyItem(item->GetValue(3));
  5021. DWORD dwSourceCount2 = item->GetValue(4);
  5022.  
  5023. if (dwSourceCount1 != 0)
  5024. {
  5025. if (pSource1 == NULL)
  5026. {
  5027. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("물약 조합을 위한 재료가 부족합니다."));
  5028. return false;
  5029. }
  5030. }
  5031.  
  5032. if (dwSourceCount2 != 0)
  5033. {
  5034. if (pSource2 == NULL)
  5035. {
  5036. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("물약 조합을 위한 재료가 부족합니다."));
  5037. return false;
  5038. }
  5039. }
  5040.  
  5041. if (pSource1 != NULL)
  5042. {
  5043. if (pSource1->GetCount() < dwSourceCount1)
  5044. {
  5045. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("재료(%s)가 부족합니다."), pSource1->GetName());
  5046. return false;
  5047. }
  5048.  
  5049. pSource1->SetCount(pSource1->GetCount() - dwSourceCount1);
  5050. }
  5051.  
  5052. if (pSource2 != NULL)
  5053. {
  5054. if (pSource2->GetCount() < dwSourceCount2)
  5055. {
  5056. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("재료(%s)가 부족합니다."), pSource2->GetName());
  5057. return false;
  5058. }
  5059.  
  5060. pSource2->SetCount(pSource2->GetCount() - dwSourceCount2);
  5061. }
  5062.  
  5063. LPITEM pBottle = FindSpecifyItem(50901);
  5064.  
  5065. if (!pBottle || pBottle->GetCount() < 1)
  5066. {
  5067. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("빈 병이 모자릅니다."));
  5068. return false;
  5069. }
  5070.  
  5071. pBottle->SetCount(pBottle->GetCount() - 1);
  5072.  
  5073. if (number(1, 100) > item->GetValue(5))
  5074. {
  5075. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("물약 제조에 실패했습니다."));
  5076. return false;
  5077. }
  5078.  
  5079. AutoGiveItem(item->GetValue(0));
  5080. }
  5081. break;
  5082. }
  5083. }
  5084. break;
  5085.  
  5086. case ITEM_METIN:
  5087. {
  5088. LPITEM item2;
  5089.  
  5090. if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell)))
  5091. return false;
  5092.  
  5093. if (item2->IsExchanging())
  5094. return false;
  5095.  
  5096. if (item2->IsEquipped()) //Fix
  5097. return false;
  5098.  
  5099. if (item2->GetType() == ITEM_PICK) return false;
  5100. if (item2->GetType() == ITEM_ROD) return false;
  5101.  
  5102. int i;
  5103.  
  5104. for (i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  5105. {
  5106. DWORD dwVnum;
  5107.  
  5108. if ((dwVnum = item2->GetSocket(i)) <= 2)
  5109. continue;
  5110.  
  5111. TItemTable * p = ITEM_MANAGER::instance().GetTable(dwVnum);
  5112.  
  5113. if (!p)
  5114. continue;
  5115.  
  5116. if (item->GetValue(5) == p->alValues[5])
  5117. {
  5118. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("같은 종류의 메틴석은 여러개 부착할 수 없습니다."));
  5119. return false;
  5120. }
  5121. }
  5122.  
  5123. if (item2->GetType() == ITEM_ARMOR)
  5124. {
  5125. if (!IS_SET(item->GetWearFlag(), WEARABLE_BODY) || !IS_SET(item2->GetWearFlag(), WEARABLE_BODY))
  5126. {
  5127. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 메틴석은 장비에 부착할 수 없습니다."));
  5128. return false;
  5129. }
  5130. }
  5131. else if (item2->GetType() == ITEM_WEAPON)
  5132. {
  5133. if (!IS_SET(item->GetWearFlag(), WEARABLE_WEAPON))
  5134. {
  5135. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 메틴석은 무기에 부착할 수 없습니다."));
  5136. return false;
  5137. }
  5138. }
  5139. else
  5140. {
  5141. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("부착할 수 있는 슬롯이 없습니다."));
  5142. return false;
  5143. }
  5144.  
  5145. for (i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  5146. if (item2->GetSocket(i) >= 1 && item2->GetSocket(i) <= 2 && item2->GetSocket(i) >= item->GetValue(2))
  5147. {
  5148. // 석 확률
  5149. if (number(1, 100) <= 30)
  5150. {
  5151. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("메틴석 부착에 성공하였습니다."));
  5152. item2->SetSocket(i, item->GetVnum());
  5153. }
  5154. else
  5155. {
  5156. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("메틴석 부착에 실패하였습니다."));
  5157. item2->SetSocket(i, ITEM_BROKEN_METIN_VNUM);
  5158. }
  5159.  
  5160. LogManager::instance().ItemLog(this, item2, "SOCKET", item->GetName());
  5161. ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (METIN)");
  5162. break;
  5163. }
  5164.  
  5165. if (i == ITEM_SOCKET_MAX_NUM)
  5166. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("부착할 수 있는 슬롯이 없습니다."));
  5167. }
  5168. break;
  5169.  
  5170. case ITEM_AUTOUSE:
  5171. case ITEM_MATERIAL:
  5172. case ITEM_SPECIAL:
  5173. case ITEM_TOOL:
  5174. case ITEM_LOTTERY:
  5175. break;
  5176.  
  5177. case ITEM_TOTEM:
  5178. {
  5179. if (!item->IsEquipped())
  5180. EquipItem(item);
  5181. }
  5182. break;
  5183.  
  5184. case ITEM_BLEND:
  5185. // 새로운 약초들
  5186. sys_log(0,"ITEM_BLEND!!");
  5187. if (Blend_Item_find(item->GetVnum()))
  5188. {
  5189. int affect_type = AFFECT_BLEND;
  5190. if (item->GetSocket(0) >= _countof(aApplyInfo))
  5191. {
  5192. sys_err ("INVALID BLEND ITEM(id : %d, vnum : %d). APPLY TYPE IS %d.", item->GetID(), item->GetVnum(), item->GetSocket(0));
  5193. return false;
  5194. }
  5195. int apply_type = aApplyInfo[item->GetSocket(0)].bPointType;
  5196. int apply_value = item->GetSocket(1);
  5197. int apply_duration = item->GetSocket(2);
  5198.  
  5199. if (FindAffect(affect_type, apply_type))
  5200. {
  5201. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이미 효과가 걸려 있습니다."));
  5202. }
  5203. else
  5204. {
  5205. if (FindAffect(AFFECT_EXP_BONUS_EURO_FREE, POINT_RESIST_MAGIC))
  5206. {
  5207. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이미 효과가 걸려 있습니다."));
  5208. }
  5209. else
  5210. {
  5211. AddAffect(affect_type, apply_type, apply_value, 0, apply_duration, 0, false);
  5212. item->SetCount(item->GetCount() - 1);
  5213. }
  5214. }
  5215. }
  5216. break;
  5217. case ITEM_EXTRACT:
  5218. {
  5219. LPITEM pDestItem = GetItem(DestCell);
  5220. if (NULL == pDestItem)
  5221. {
  5222. return false;
  5223. }
  5224. switch (item->GetSubType())
  5225. {
  5226. case EXTRACT_DRAGON_SOUL:
  5227. if (pDestItem->IsDragonSoul())
  5228. {
  5229. return DSManager::instance().PullOut(this, NPOS, pDestItem, item);
  5230. }
  5231. return false;
  5232. case EXTRACT_DRAGON_HEART:
  5233. if (pDestItem->IsDragonSoul())
  5234. {
  5235. return DSManager::instance().ExtractDragonHeart(this, pDestItem, item);
  5236. }
  5237. return false;
  5238. default:
  5239. return false;
  5240. }
  5241. }
  5242. break;
  5243.  
  5244. case ITEM_NONE:
  5245. sys_err("Item type NONE %s", item->GetName());
  5246. break;
  5247.  
  5248. default:
  5249. sys_log(0, "UseItemEx: Unknown type %s %d", item->GetName(), item->GetType());
  5250. return false;
  5251. }
  5252.  
  5253. return true;
  5254. }
  5255.  
  5256. int g_nPortalLimitTime = 10;
  5257.  
  5258. bool CHARACTER::UseItem(TItemPos Cell, TItemPos DestCell)
  5259. {
  5260. WORD wCell = Cell.cell;
  5261. BYTE window_type = Cell.window_type;
  5262. WORD wDestCell = DestCell.cell;
  5263. BYTE bDestInven = DestCell.window_type;
  5264. LPITEM item;
  5265.  
  5266. if (!CanHandleItem())
  5267. return false;
  5268.  
  5269. if (!IsValidItemPosition(Cell) || !(item = GetItem(Cell)))
  5270. return false;
  5271.  
  5272. sys_log(0, "%s: USE_ITEM %s (inven %d, cell: %d)", GetName(), item->GetName(), window_type, wCell);
  5273.  
  5274. if (item->IsExchanging())
  5275. return false;
  5276.  
  5277. if (!item->CanUsedBy(this))
  5278. {
  5279. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Your character class can not use this item."));
  5280. return false;
  5281. }
  5282.  
  5283. #ifdef ENABLE_SWITCHBOT
  5284. if (Cell.IsSwitchbotPosition())
  5285. {
  5286. CSwitchbot* pkSwitchbot = CSwitchbotManager::Instance().FindSwitchbot(GetPlayerID());
  5287. if (pkSwitchbot && pkSwitchbot->IsActive(Cell.cell))
  5288. {
  5289. return false;
  5290. }
  5291.  
  5292. int iEmptyCell = GetEmptyInventory(item->GetSize());
  5293. if (iEmptyCell == -1)
  5294. {
  5295. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You carry too many Items."));
  5296. return false;
  5297. }
  5298.  
  5299. MoveItem(Cell, TItemPos(INVENTORY, iEmptyCell), item->GetCount());
  5300. return true;
  5301. }
  5302. #endif
  5303.  
  5304.  
  5305. if (IsStun())
  5306. return false;
  5307.  
  5308. if (false == FN_check_item_sex(this, item))
  5309. {
  5310. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This Item can be used by the other gender only."));
  5311. return false;
  5312. }
  5313.  
  5314. //PREVENT_TRADE_WINDOW
  5315. if (IS_SUMMON_ITEM(item->GetVnum()))
  5316. {
  5317. if (false == IS_SUMMONABLE_ZONE(GetMapIndex()))
  5318. {
  5319. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("사용할수 없습니다."));
  5320. return false;
  5321. }
  5322.  
  5323. // 경혼반지 사용지 상대방이 SUMMONABLE_ZONE에 있는가는 WarpToPC()에서 체크
  5324.  
  5325. //삼거리 관려 맵에서는 귀환부를 막아버린다.
  5326. if (CThreeWayWar::instance().IsThreeWayWarMapIndex(GetMapIndex()))
  5327. {
  5328. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("삼거리 전투 참가중에는 귀환부,귀환기억부를 사용할수 없습니다."));
  5329. return false;
  5330. }
  5331. int iPulse = thecore_pulse();
  5332.  
  5333. //창고 연후 체크
  5334. if (iPulse - GetSafeboxLoadTime() < PASSES_PER_SEC(g_nPortalLimitTime))
  5335. {
  5336. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("창고를 연후 %d초 이내에는 귀환부,귀환기억부를 사용할 수 없습니다."), g_nPortalLimitTime);
  5337.  
  5338. if (test_server)
  5339. ChatPacket(CHAT_TYPE_INFO, "[TestOnly]Pulse %d LoadTime %d PASS %d", iPulse, GetSafeboxLoadTime(), PASSES_PER_SEC(g_nPortalLimitTime));
  5340. return false;
  5341. }
  5342.  
  5343. //거래관련 창 체크
  5344. if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen())
  5345. {
  5346. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래창,창고 등을 연 상태에서는 귀환부,귀환기억부 를 사용할수 없습니다."));
  5347. return false;
  5348. }
  5349.  
  5350. //PREVENT_REFINE_HACK
  5351. //개량후 시간체크
  5352. {
  5353. if (iPulse - GetRefineTime() < PASSES_PER_SEC(g_nPortalLimitTime))
  5354. {
  5355. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 개량후 %d초 이내에는 귀환부,귀환기억부를 사용할 수 없습니다."), g_nPortalLimitTime);
  5356. return false;
  5357. }
  5358. }
  5359. //END_PREVENT_REFINE_HACK
  5360.  
  5361.  
  5362. //PREVENT_ITEM_COPY
  5363. {
  5364. if (iPulse - GetMyShopTime() < PASSES_PER_SEC(g_nPortalLimitTime))
  5365. {
  5366. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("개인상점 사용후 %d초 이내에는 귀환부,귀환기억부를 사용할 수 없습니다."), g_nPortalLimitTime);
  5367. return false;
  5368. }
  5369.  
  5370. }
  5371. //END_PREVENT_ITEM_COPY
  5372.  
  5373.  
  5374. //귀환부 거리체크
  5375. if (item->GetVnum() != 70302)
  5376. {
  5377. PIXEL_POSITION posWarp;
  5378.  
  5379. int x = 0;
  5380. int y = 0;
  5381.  
  5382. double nDist = 0;
  5383. const double nDistant = 5000.0;
  5384. //귀환기억부
  5385. if (item->GetVnum() == 22010)
  5386. {
  5387. x = item->GetSocket(0) - GetX();
  5388. y = item->GetSocket(1) - GetY();
  5389. }
  5390. //귀환부
  5391. else if (item->GetVnum() == 22000)
  5392. {
  5393. SECTREE_MANAGER::instance().GetRecallPositionByEmpire(GetMapIndex(), GetEmpire(), posWarp);
  5394.  
  5395. if (item->GetSocket(0) == 0)
  5396. {
  5397. x = posWarp.x - GetX();
  5398. y = posWarp.y - GetY();
  5399. }
  5400. else
  5401. {
  5402. x = item->GetSocket(0) - GetX();
  5403. y = item->GetSocket(1) - GetY();
  5404. }
  5405. }
  5406.  
  5407. nDist = sqrt(pow((float)x,2) + pow((float)y,2));
  5408.  
  5409. if (nDistant > nDist)
  5410. {
  5411. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이동 되어질 위치와 너무 가까워 귀환부를 사용할수 없습니다."));
  5412. if (test_server)
  5413. ChatPacket(CHAT_TYPE_INFO, "PossibleDistant %f nNowDist %f", nDistant,nDist);
  5414. return false;
  5415. }
  5416. }
  5417.  
  5418. //PREVENT_PORTAL_AFTER_EXCHANGE
  5419. //교환 후 시간체크
  5420. if (iPulse - GetExchangeTime() < PASSES_PER_SEC(g_nPortalLimitTime))
  5421. {
  5422. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래 후 %d초 이내에는 귀환부,귀환기억부등을 사용할 수 없습니다."), g_nPortalLimitTime);
  5423. return false;
  5424. }
  5425. //END_PREVENT_PORTAL_AFTER_EXCHANGE
  5426.  
  5427. }
  5428.  
  5429. //보따리 비단 사용시 거래창 제한 체크
  5430. if (item->GetVnum() == 50200 | item->GetVnum() == 71049)
  5431. {
  5432. if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen())
  5433. {
  5434. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래창,창고 등을 연 상태에서는 보따리,비단보따리를 사용할수 없습니다."));
  5435. return false;
  5436. }
  5437.  
  5438. }
  5439. //END_PREVENT_TRADE_WINDOW
  5440.  
  5441. if (IS_SET(item->GetFlag(), ITEM_FLAG_LOG)) // 사용 로그를 남기는 아이템 처리
  5442. {
  5443. DWORD vid = item->GetVID();
  5444. DWORD oldCount = item->GetCount();
  5445. DWORD vnum = item->GetVnum();
  5446.  
  5447. char hint[ITEM_NAME_MAX_LEN + 32 + 1];
  5448. int len = snprintf(hint, sizeof(hint) - 32, "%s", item->GetName());
  5449.  
  5450. if (len < 0 || len >= (int) sizeof(hint) - 32)
  5451. len = (sizeof(hint) - 32) - 1;
  5452.  
  5453. bool ret = UseItemEx(item, DestCell);
  5454.  
  5455. if (NULL == ITEM_MANAGER::instance().FindByVID(vid)) // UseItemEx에서 아이템이 삭제 되었다. 삭제 로그를 남김
  5456. {
  5457. LogManager::instance().ItemLog(this, vid, vnum, "REMOVE", hint);
  5458. }
  5459. else if (oldCount != item->GetCount())
  5460. {
  5461. snprintf(hint + len, sizeof(hint) - len, " %u", oldCount - 1);
  5462. LogManager::instance().ItemLog(this, vid, vnum, "USE_ITEM", hint);
  5463. }
  5464. return (ret);
  5465. }
  5466. else
  5467. return UseItemEx(item, DestCell);
  5468. }
  5469.  
  5470. bool CHARACTER::DropItem(TItemPos Cell, BYTE bCount)
  5471. {
  5472. LPITEM item = NULL;
  5473.  
  5474. if (!CanHandleItem())
  5475. {
  5476. if (NULL != DragonSoul_RefineWindow_GetOpener())
  5477. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("강화창을 연 상태에서는 아이템을 옮길 수 없습니다."));
  5478. return false;
  5479. }
  5480.  
  5481. #ifdef ENABLE_SWITCHBOT
  5482. if (Cell.IsSwitchbotPosition())
  5483. return false;
  5484. #endif
  5485.  
  5486. if (IsDead())
  5487. return false;
  5488.  
  5489. if (!IsValidItemPosition(Cell) || !(item = GetItem(Cell)))
  5490. return false;
  5491.  
  5492. if (item->IsExchanging())
  5493. return false;
  5494.  
  5495. if (true == item->isLocked())
  5496. return false;
  5497.  
  5498. if (quest::CQuestManager::instance().GetPCForce(GetPlayerID())->IsRunning() == true)
  5499. return false;
  5500.  
  5501. if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_DROP | ITEM_ANTIFLAG_GIVE))
  5502. {
  5503. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("버릴 수 없는 아이템입니다."));
  5504. return false;
  5505. }
  5506.  
  5507. if (bCount == 0 || bCount > item->GetCount())
  5508. bCount = item->GetCount();
  5509.  
  5510. SyncQuickslot(QUICKSLOT_TYPE_ITEM, Cell.cell, 255); // Quickslot 에서 지움
  5511.  
  5512. LPITEM pkItemToDrop;
  5513.  
  5514. if (bCount == item->GetCount())
  5515. {
  5516. item->RemoveFromCharacter();
  5517. pkItemToDrop = item;
  5518. }
  5519. else
  5520. {
  5521. if (bCount == 0)
  5522. {
  5523. if (test_server)
  5524. sys_log(0, "[DROP_ITEM] drop item count == 0");
  5525. return false;
  5526. }
  5527.  
  5528. // check non-split items for china
  5529. //if (LC_IsNewCIBN())
  5530. // if (item->GetVnum() == 71095 || item->GetVnum() == 71050 || item->GetVnum() == 70038)
  5531. // return false;
  5532.  
  5533. item->SetCount(item->GetCount() - bCount);
  5534. ITEM_MANAGER::instance().FlushDelayedSave(item);
  5535.  
  5536. pkItemToDrop = ITEM_MANAGER::instance().CreateItem(item->GetVnum(), bCount);
  5537.  
  5538. // copy item socket -- by mhh
  5539. FN_copy_item_socket(pkItemToDrop, item);
  5540.  
  5541. char szBuf[51 + 1];
  5542. snprintf(szBuf, sizeof(szBuf), "%u %u", pkItemToDrop->GetID(), pkItemToDrop->GetCount());
  5543. LogManager::instance().ItemLog(this, item, "ITEM_SPLIT", szBuf);
  5544. }
  5545.  
  5546. PIXEL_POSITION pxPos = GetXYZ();
  5547.  
  5548. // Clear the variable, it looks the player does not dropped any item in the past second.
  5549. if (thecore_pulse() > LastDropTime + 25)
  5550. CountDrops = 0;
  5551.  
  5552. // It looks the player dropped min. 4 items in the past 1 second
  5553. if (thecore_pulse() < LastDropTime + 25 && CountDrops >= 4)
  5554. {
  5555. // Set it to 0
  5556. CountDrops = 0;
  5557. sys_err("%s[%d] has been disconnected because of drophack using", GetName(), GetPlayerID());
  5558. // Disconnect the player
  5559. GetDesc()->SetPhase(PHASE_CLOSE);
  5560. return false;
  5561. }
  5562.  
  5563. if (pkItemToDrop->AddToGround(GetMapIndex(), pxPos))
  5564. {
  5565. // 한국에는 아이템을 버리고 복구해달라는 진상유저들이 많아서
  5566. // 아이템을 바닥에 버릴 시 속성로그를 남긴다.
  5567. if (LC_IsYMIR())
  5568. item->AttrLog();
  5569.  
  5570. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("떨어진 아이템은 3분 후 사라집니다."));
  5571. pkItemToDrop->StartDestroyEvent();
  5572.  
  5573. ITEM_MANAGER::instance().FlushDelayedSave(pkItemToDrop);
  5574.  
  5575. char szHint[32 + 1];
  5576. snprintf(szHint, sizeof(szHint), "%s %u %u", pkItemToDrop->GetName(), pkItemToDrop->GetCount(), pkItemToDrop->GetOriginalVnum());
  5577. LogManager::instance().ItemLog(this, pkItemToDrop, "DROP", szHint);
  5578.  
  5579. LastDropTime = thecore_pulse();
  5580. CountDrops++;
  5581.  
  5582. //Motion(MOTION_PICKUP);
  5583. }
  5584.  
  5585. return true;
  5586. }
  5587.  
  5588. bool CHARACTER::DropGold(int gold)
  5589. {
  5590. if (gold <= 0 || gold > GetGold())
  5591. return false;
  5592.  
  5593. if (!CanHandleItem())
  5594. return false;
  5595.  
  5596. if (0 != g_GoldDropTimeLimitValue)
  5597. {
  5598. if (get_dword_time() < m_dwLastGoldDropTime+g_GoldDropTimeLimitValue)
  5599. {
  5600. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아직 골드를 버릴 수 없습니다."));
  5601. return false;
  5602. }
  5603. }
  5604.  
  5605. m_dwLastGoldDropTime = get_dword_time();
  5606.  
  5607. LPITEM item = ITEM_MANAGER::instance().CreateItem(1, gold);
  5608.  
  5609. if (item)
  5610. {
  5611. PIXEL_POSITION pos = GetXYZ();
  5612.  
  5613. if (item->AddToGround(GetMapIndex(), pos))
  5614. {
  5615. //Motion(MOTION_PICKUP);
  5616. PointChange(POINT_GOLD, -gold, true);
  5617.  
  5618. // 브라질에 돈이 없어진다는 버그가 있는데,
  5619. // 가능한 시나리오 중에 하나는,
  5620. // 메크로나, 핵을 써서 1000원 이하의 돈을 계속 버려 골드를 0으로 만들고,
  5621. // 돈이 없어졌다고 복구 신청하는 것일 수도 있다.
  5622. // 따라서 그런 경우를 잡기 위해 낮은 수치의 골드에 대해서도 로그를 남김.
  5623. if (LC_IsBrazil() == true)
  5624. {
  5625. if (gold >= 213)
  5626. LogManager::instance().CharLog(this, gold, "DROP_GOLD", "");
  5627. }
  5628. else
  5629. {
  5630. if (gold > 1000) // 천원 이상만 기록한다.
  5631. LogManager::instance().CharLog(this, gold, "DROP_GOLD", "");
  5632. }
  5633.  
  5634. if (false == LC_IsBrazil())
  5635. {
  5636. item->StartDestroyEvent(150);
  5637. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("떨어진 아이템은 %d분 후 사라집니다."), 150/60);
  5638. }
  5639. else
  5640. {
  5641. item->StartDestroyEvent(60);
  5642. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("떨어진 아이템은 %d분 후 사라집니다."), 1);
  5643. }
  5644. }
  5645.  
  5646. Save();
  5647. return true;
  5648. }
  5649.  
  5650. return false;
  5651. }
  5652.  
  5653. bool CHARACTER::MoveItem(TItemPos Cell, TItemPos DestCell, BYTE count)
  5654. {
  5655. if (Cell.IsSamePosition(DestCell)) // ItemMove dupe exploit fix
  5656. return false;
  5657.  
  5658. LPITEM item = NULL;
  5659.  
  5660. if (!IsValidItemPosition(Cell))
  5661. return false;
  5662.  
  5663. if (!(item = GetItem(Cell)))
  5664. return false;
  5665.  
  5666. if (item->IsExchanging())
  5667. return false;
  5668.  
  5669. if (item->GetCount() < count)
  5670. return false;
  5671.  
  5672. if (INVENTORY == Cell.window_type && Cell.cell >= INVENTORY_MAX_NUM && IS_SET(item->GetFlag(), ITEM_FLAG_IRREMOVABLE))
  5673. return false;
  5674.  
  5675. if (true == item->isLocked())
  5676. return false;
  5677.  
  5678. if (!IsValidItemPosition(DestCell))
  5679. {
  5680. return false;
  5681. }
  5682.  
  5683. if (!CanHandleItem())
  5684. {
  5685. if (NULL != DragonSoul_RefineWindow_GetOpener())
  5686. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't do that with dragonsoul refine window opened."));
  5687. return false;
  5688. }
  5689.  
  5690. #ifdef ENABLE_MOUNT_LIKE_HORSE
  5691. if (item->IsMount() && item->IsEquipped() && DestCell.IsDefaultInventoryPosition()) {
  5692. StopRiding();
  5693. HorseSummon(false);
  5694. }
  5695. #endif
  5696.  
  5697. #ifdef ENABLE_SWITCHBOT
  5698. if (Cell.IsSwitchbotPosition() && CSwitchbotManager::Instance().IsActive(GetPlayerID(), Cell.cell))
  5699. {
  5700. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Cannot move active switchbot item."));
  5701. return false;
  5702. }
  5703.  
  5704. if ((DestCell.IsSwitchbotPosition() && item->IsEquipped())
  5705. || (Cell.IsSwitchbotPosition() && DestCell.IsEquipPosition()))
  5706. return false;
  5707.  
  5708. if (DestCell.IsSwitchbotPosition() && !SwitchbotHelper::IsValidItem(item))
  5709. {
  5710. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Invalid item type for switchbot."));
  5711. return false;
  5712. }
  5713. #endif
  5714.  
  5715. if (DestCell.IsBeltInventoryPosition() && false == CBeltInventoryHelper::CanMoveIntoBeltInventory(item))
  5716. {
  5717. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템은 벨트 인벤토리로 옮길 수 없습니다."));
  5718. return false;
  5719. }
  5720.  
  5721. // 이미 착용중인 아이템을 다른 곳으로 옮기는 경우, '장책 해제' 가능한 지 확인하고 옮김
  5722. if (Cell.IsEquipPosition() && !CanUnequipNow(item))
  5723. return false;
  5724.  
  5725. if (DestCell.IsEquipPosition())
  5726. {
  5727. if (GetItem(DestCell)) // 장비일 경우 한 곳만 검사해도 된다.
  5728. {
  5729. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이미 장비를 착용하고 있습니다."));
  5730.  
  5731. return false;
  5732. }
  5733.  
  5734. EquipItem(item, DestCell.cell - INVENTORY_MAX_NUM);
  5735. }
  5736. else
  5737. {
  5738. if (item->IsDragonSoul())
  5739. {
  5740. if (item->IsEquipped())
  5741. {
  5742. return DSManager::instance().PullOut(this, DestCell, item);
  5743. }
  5744. else
  5745. {
  5746. if (DestCell.window_type != DRAGON_SOUL_INVENTORY)
  5747. {
  5748. return false;
  5749. }
  5750.  
  5751. if (!DSManager::instance().IsValidCellForThisItem(item, DestCell))
  5752. return false;
  5753. }
  5754. }
  5755. // 용혼석이 아닌 아이템은 용혼석 인벤에 들어갈 수 없다.
  5756. else if (DRAGON_SOUL_INVENTORY == DestCell.window_type)
  5757. return false;
  5758.  
  5759. LPITEM item2;
  5760.  
  5761. if ((item2 = GetItem(DestCell)) && item != item2 && item2->IsStackable() &&
  5762. !IS_SET(item2->GetAntiFlag(), ITEM_ANTIFLAG_STACK) &&
  5763. item2->GetVnum() == item->GetVnum()) // 합칠 수 있는 아이템의 경우
  5764. {
  5765. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  5766. if (item2->GetSocket(i) != item->GetSocket(i))
  5767. return false;
  5768.  
  5769. if (count == 0)
  5770. count = item->GetCount();
  5771.  
  5772. sys_log(0, "%s: ITEM_STACK %s (window: %d, cell : %d) -> (window:%d, cell %d) count %d", GetName(), item->GetName(), Cell.window_type, Cell.cell,
  5773. DestCell.window_type, DestCell.cell, count);
  5774.  
  5775. count = MIN(200 - item2->GetCount(), count);
  5776.  
  5777. item->SetCount(item->GetCount() - count);
  5778. item2->SetCount(item2->GetCount() + count);
  5779. return true;
  5780. }
  5781.  
  5782. if (!IsEmptyItemGrid(DestCell, item->GetSize(), Cell.cell))
  5783. return false;
  5784.  
  5785. if (count == 0 || count >= item->GetCount() || !item->IsStackable() || IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_STACK))
  5786. {
  5787. sys_log(0, "%s: ITEM_MOVE %s (window: %d, cell : %d) -> (window:%d, cell %d) count %d", GetName(), item->GetName(), Cell.window_type, Cell.cell,
  5788. DestCell.window_type, DestCell.cell, count);
  5789.  
  5790. item->RemoveFromCharacter();
  5791. SetItem(DestCell, item);
  5792.  
  5793. if (INVENTORY == Cell.window_type && INVENTORY == DestCell.window_type)
  5794. SyncQuickslot(QUICKSLOT_TYPE_ITEM, Cell.cell, DestCell.cell);
  5795. }
  5796. else if (count < item->GetCount())
  5797. {
  5798. //check non-split items
  5799. //if (LC_IsNewCIBN())
  5800. //{
  5801. // if (item->GetVnum() == 71095 || item->GetVnum() == 71050 || item->GetVnum() == 70038)
  5802. // {
  5803. // return false;
  5804. // }
  5805. //}
  5806.  
  5807. sys_log(0, "%s: ITEM_SPLIT %s (window: %d, cell : %d) -> (window:%d, cell %d) count %d", GetName(), item->GetName(), Cell.window_type, Cell.cell,
  5808. DestCell.window_type, DestCell.cell, count);
  5809.  
  5810. item->SetCount(item->GetCount() - count);
  5811. LPITEM item2 = ITEM_MANAGER::instance().CreateItem(item->GetVnum(), count);
  5812.  
  5813. // copy socket -- by mhh
  5814. FN_copy_item_socket(item2, item);
  5815.  
  5816. item2->AddToCharacter(this, DestCell);
  5817.  
  5818. char szBuf[51+1];
  5819. snprintf(szBuf, sizeof(szBuf), "%u %u %u %u ", item2->GetID(), item2->GetCount(), item->GetCount(), item->GetCount() + item2->GetCount());
  5820. LogManager::instance().ItemLog(this, item, "ITEM_SPLIT", szBuf);
  5821. }
  5822. }
  5823.  
  5824. return true;
  5825. }
  5826.  
  5827. namespace NPartyPickupDistribute
  5828. {
  5829. struct FFindOwnership
  5830. {
  5831. LPITEM item;
  5832. LPCHARACTER owner;
  5833.  
  5834. FFindOwnership(LPITEM item)
  5835. : item(item), owner(NULL)
  5836. {
  5837. }
  5838.  
  5839. void operator () (LPCHARACTER ch)
  5840. {
  5841. if (item->IsOwnership(ch))
  5842. owner = ch;
  5843. }
  5844. };
  5845.  
  5846. struct FCountNearMember
  5847. {
  5848. int total;
  5849. int x, y;
  5850.  
  5851. FCountNearMember(LPCHARACTER center )
  5852. : total(0), x(center->GetX()), y(center->GetY())
  5853. {
  5854. }
  5855.  
  5856. void operator () (LPCHARACTER ch)
  5857. {
  5858. if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE)
  5859. total += 1;
  5860. }
  5861. };
  5862.  
  5863. struct FMoneyDistributor
  5864. {
  5865. int total;
  5866. LPCHARACTER c;
  5867. int x, y;
  5868. int iMoney;
  5869.  
  5870. FMoneyDistributor(LPCHARACTER center, int iMoney)
  5871. : total(0), c(center), x(center->GetX()), y(center->GetY()), iMoney(iMoney)
  5872. {
  5873. }
  5874.  
  5875. void operator ()(LPCHARACTER ch)
  5876. {
  5877. if (ch!=c)
  5878. if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE)
  5879. {
  5880. ch->PointChange(POINT_GOLD, iMoney, true);
  5881.  
  5882. if (iMoney > 1000) // 천원 이상만 기록한다.
  5883. LogManager::instance().CharLog(ch, iMoney, "GET_GOLD", "");
  5884. }
  5885. }
  5886. };
  5887. }
  5888.  
  5889. void CHARACTER::GiveGold(int iAmount)
  5890. {
  5891. if (iAmount <= 0)
  5892. return;
  5893.  
  5894. sys_log(0, "GIVE_GOLD: %s %d", GetName(), iAmount);
  5895.  
  5896. if (GetParty())
  5897. {
  5898. LPPARTY pParty = GetParty();
  5899.  
  5900. // 파티가 있는 경우 나누어 가진다.
  5901. DWORD dwTotal = iAmount;
  5902. DWORD dwMyAmount = dwTotal;
  5903.  
  5904. NPartyPickupDistribute::FCountNearMember funcCountNearMember(this);
  5905. pParty->ForEachOnlineMember(funcCountNearMember);
  5906.  
  5907. if (funcCountNearMember.total > 1)
  5908. {
  5909. DWORD dwShare = dwTotal / funcCountNearMember.total;
  5910. dwMyAmount -= dwShare * (funcCountNearMember.total - 1);
  5911.  
  5912. NPartyPickupDistribute::FMoneyDistributor funcMoneyDist(this, dwShare);
  5913.  
  5914. pParty->ForEachOnlineMember(funcMoneyDist);
  5915. }
  5916.  
  5917. PointChange(POINT_GOLD, dwMyAmount, true);
  5918.  
  5919. if (dwMyAmount > 1000) // 천원 이상만 기록한다.
  5920. LogManager::instance().CharLog(this, dwMyAmount, "GET_GOLD", "");
  5921. }
  5922. else
  5923. {
  5924. PointChange(POINT_GOLD, iAmount, true);
  5925.  
  5926. // 브라질에 돈이 없어진다는 버그가 있는데,
  5927. // 가능한 시나리오 중에 하나는,
  5928. // 메크로나, 핵을 써서 1000원 이하의 돈을 계속 버려 골드를 0으로 만들고,
  5929. // 돈이 없어졌다고 복구 신청하는 것일 수도 있다.
  5930. // 따라서 그런 경우를 잡기 위해 낮은 수치의 골드에 대해서도 로그를 남김.
  5931. if (LC_IsBrazil() == true)
  5932. {
  5933. if (iAmount >= 213)
  5934. LogManager::instance().CharLog(this, iAmount, "GET_GOLD", "");
  5935. }
  5936. else
  5937. {
  5938. if (iAmount > 1000) // 천원 이상만 기록한다.
  5939. LogManager::instance().CharLog(this, iAmount, "GET_GOLD", "");
  5940. }
  5941. }
  5942. }
  5943.  
  5944. bool CHARACTER::PickupItem(DWORD dwVID)
  5945. {
  5946. LPITEM item = ITEM_MANAGER::instance().FindByVID(dwVID);
  5947.  
  5948. if (IsObserverMode())
  5949. return false;
  5950.  
  5951. if (!item || !item->GetSectree())
  5952. return false;
  5953.  
  5954. if (item->DistanceValid(this))
  5955. {
  5956. if (item->IsOwnership(this))
  5957. {
  5958. // 만약 주으려 하는 아이템이 엘크라면
  5959. if (item->GetType() == ITEM_ELK)
  5960. {
  5961. GiveGold(item->GetCount());
  5962. item->RemoveFromGround();
  5963.  
  5964. M2_DESTROY_ITEM(item);
  5965.  
  5966. Save();
  5967. }
  5968. // 평범한 아이템이라면
  5969. else
  5970. {
  5971. if (item->IsStackable() && !IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_STACK))
  5972. {
  5973. BYTE bCount = item->GetCount();
  5974.  
  5975. for (int i = 0; i < INVENTORY_MAX_NUM; ++i)
  5976. {
  5977. LPITEM item2 = GetInventoryItem(i);
  5978.  
  5979. if (!item2)
  5980. continue;
  5981.  
  5982. if (item2->GetVnum() == item->GetVnum())
  5983. {
  5984. int j;
  5985.  
  5986. for (j = 0; j < ITEM_SOCKET_MAX_NUM; ++j)
  5987. if (item2->GetSocket(j) != item->GetSocket(j))
  5988. break;
  5989.  
  5990. if (j != ITEM_SOCKET_MAX_NUM)
  5991. continue;
  5992.  
  5993. BYTE bCount2 = MIN(200 - item2->GetCount(), bCount);
  5994. bCount -= bCount2;
  5995.  
  5996. item2->SetCount(item2->GetCount() + bCount2);
  5997.  
  5998. if (bCount == 0)
  5999. {
  6000. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 획득: %s"), item2->GetName());
  6001. M2_DESTROY_ITEM(item);
  6002. if (item2->GetType() == ITEM_QUEST)
  6003. quest::CQuestManager::instance().PickupItem (GetPlayerID(), item2);
  6004. return true;
  6005. }
  6006. }
  6007. }
  6008.  
  6009. item->SetCount(bCount);
  6010. }
  6011.  
  6012. int iEmptyCell;
  6013. if (item->IsDragonSoul())
  6014. {
  6015. if ((iEmptyCell = GetEmptyDragonSoulInventory(item)) == -1)
  6016. {
  6017. sys_log(0, "No empty ds inventory pid %u size %ud itemid %u", GetPlayerID(), item->GetSize(), item->GetID());
  6018. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("소지하고 있는 아이템이 너무 많습니다."));
  6019. return false;
  6020. }
  6021. }
  6022. else
  6023. {
  6024. if ((iEmptyCell = GetEmptyInventory(item->GetSize())) == -1)
  6025. {
  6026. sys_log(0, "No empty inventory pid %u size %ud itemid %u", GetPlayerID(), item->GetSize(), item->GetID());
  6027. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("소지하고 있는 아이템이 너무 많습니다."));
  6028. return false;
  6029. }
  6030. }
  6031.  
  6032. item->RemoveFromGround();
  6033.  
  6034. if (item->IsDragonSoul())
  6035. item->AddToCharacter(this, TItemPos(DRAGON_SOUL_INVENTORY, iEmptyCell));
  6036. else
  6037. item->AddToCharacter(this, TItemPos(INVENTORY, iEmptyCell));
  6038.  
  6039. char szHint[32+1];
  6040. snprintf(szHint, sizeof(szHint), "%s %u %u", item->GetName(), item->GetCount(), item->GetOriginalVnum());
  6041. LogManager::instance().ItemLog(this, item, "GET", szHint);
  6042. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 획득: %s"), item->GetName());
  6043.  
  6044. if (item->GetType() == ITEM_QUEST)
  6045. quest::CQuestManager::instance().PickupItem (GetPlayerID(), item);
  6046. }
  6047.  
  6048. //Motion(MOTION_PICKUP);
  6049. return true;
  6050. }
  6051. else if (!IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_DROP) && GetParty())
  6052. {
  6053. // 다른 파티원 소유권 아이템을 주으려고 한다면
  6054. NPartyPickupDistribute::FFindOwnership funcFindOwnership(item);
  6055.  
  6056. GetParty()->ForEachOnlineMember(funcFindOwnership);
  6057.  
  6058. LPCHARACTER owner = funcFindOwnership.owner;
  6059.  
  6060. int iEmptyCell;
  6061.  
  6062. if (item->IsDragonSoul())
  6063. {
  6064. if (!(owner && (iEmptyCell = owner->GetEmptyDragonSoulInventory(item)) != -1))
  6065. {
  6066. owner = this;
  6067.  
  6068. if ((iEmptyCell = GetEmptyDragonSoulInventory(item)) == -1)
  6069. {
  6070. owner->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("소지하고 있는 아이템이 너무 많습니다."));
  6071. return false;
  6072. }
  6073. }
  6074. }
  6075. else
  6076. {
  6077. if (!(owner && (iEmptyCell = owner->GetEmptyInventory(item->GetSize())) != -1))
  6078. {
  6079. owner = this;
  6080.  
  6081. if ((iEmptyCell = GetEmptyInventory(item->GetSize())) == -1)
  6082. {
  6083. owner->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("소지하고 있는 아이템이 너무 많습니다."));
  6084. return false;
  6085. }
  6086. }
  6087. }
  6088.  
  6089. item->RemoveFromGround();
  6090.  
  6091. if (item->IsDragonSoul())
  6092. item->AddToCharacter(owner, TItemPos(DRAGON_SOUL_INVENTORY, iEmptyCell));
  6093. else
  6094. item->AddToCharacter(owner, TItemPos(INVENTORY, iEmptyCell));
  6095.  
  6096. char szHint[32+1];
  6097. snprintf(szHint, sizeof(szHint), "%s %u %u", item->GetName(), item->GetCount(), item->GetOriginalVnum());
  6098. LogManager::instance().ItemLog(owner, item, "GET", szHint);
  6099.  
  6100. if (owner == this)
  6101. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 획득: %s"), item->GetName());
  6102. else
  6103. {
  6104. owner->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 획득: %s 님으로부터 %s"), GetName(), item->GetName());
  6105. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 전달: %s 님에게 %s"), owner->GetName(), item->GetName());
  6106. }
  6107.  
  6108. if (item->GetType() == ITEM_QUEST)
  6109. quest::CQuestManager::instance().PickupItem (owner->GetPlayerID(), item);
  6110.  
  6111. return true;
  6112. }
  6113. }
  6114.  
  6115. return false;
  6116. }
  6117.  
  6118. bool CHARACTER::SwapItem(BYTE bCell, BYTE bDestCell)
  6119. {
  6120. if (!CanHandleItem())
  6121. return false;
  6122.  
  6123. TItemPos srcCell(INVENTORY, bCell), destCell(INVENTORY, bDestCell);
  6124.  
  6125. if (srcCell.IsDragonSoulEquipPosition() || destCell.IsDragonSoulEquipPosition())
  6126. return false;
  6127.  
  6128. if (bCell == bDestCell)
  6129. return false;
  6130.  
  6131. if (srcCell.IsEquipPosition() && destCell.IsEquipPosition())
  6132. return false;
  6133.  
  6134. LPITEM item1, item2;
  6135.  
  6136. if (srcCell.IsEquipPosition())
  6137. {
  6138. item1 = GetInventoryItem(bDestCell);
  6139. item2 = GetInventoryItem(bCell);
  6140. }
  6141. else
  6142. {
  6143. item1 = GetInventoryItem(bCell);
  6144. item2 = GetInventoryItem(bDestCell);
  6145. }
  6146.  
  6147. if (!item1 || !item2)
  6148. return false;
  6149.  
  6150. if (item1 == item2)
  6151. {
  6152. sys_log(0, "[WARNING][WARNING][HACK USER!] : %s %d %d", m_stName.c_str(), bCell, bDestCell);
  6153. return false;
  6154. }
  6155.  
  6156. // item2가 bCell위치에 들어갈 수 있는지 확인한다.
  6157. if (!IsEmptyItemGrid(TItemPos (INVENTORY, item1->GetCell()), item2->GetSize(), item1->GetCell()))
  6158. return false;
  6159.  
  6160. // 바꿀 아이템이 장비창에 있으면
  6161. if (TItemPos(EQUIPMENT, item2->GetCell()).IsEquipPosition())
  6162. {
  6163. BYTE bEquipCell = item2->GetCell() - INVENTORY_MAX_NUM;
  6164. BYTE bInvenCell = item1->GetCell();
  6165.  
  6166. // 착용중인 아이템을 벗을 수 있고, 착용 예정 아이템이 착용 가능한 상태여야만 진행
  6167. if (false == CanUnequipNow(item2) || false == CanEquipNow(item1))
  6168. return false;
  6169.  
  6170. if (bEquipCell != item1->FindEquipCell(this)) // 같은 위치일때만 허용
  6171. return false;
  6172.  
  6173. item2->RemoveFromCharacter();
  6174.  
  6175. if (item1->EquipTo(this, bEquipCell))
  6176. item2->AddToCharacter(this, TItemPos(INVENTORY, bInvenCell));
  6177. else
  6178. sys_err("SwapItem cannot equip %s! item1 %s", item2->GetName(), item1->GetName());
  6179. }
  6180. else
  6181. {
  6182. BYTE bCell1 = item1->GetCell();
  6183. BYTE bCell2 = item2->GetCell();
  6184.  
  6185. item1->RemoveFromCharacter();
  6186. item2->RemoveFromCharacter();
  6187.  
  6188. item1->AddToCharacter(this, TItemPos(INVENTORY, bCell2));
  6189. item2->AddToCharacter(this, TItemPos(INVENTORY, bCell1));
  6190. }
  6191.  
  6192. #ifdef ENABLE_MOUNT_LIKE_HORSE
  6193. if (item1->IsMount() && item2->IsMount() && destCell.IsEquipPosition())
  6194. HorseSummon(true);
  6195. #endif
  6196.  
  6197. return true;
  6198. }
  6199.  
  6200. bool CHARACTER::UnequipItem(LPITEM item)
  6201. {
  6202. int pos;
  6203.  
  6204. #ifdef ENABLE_MOUNT_LIKE_HORSE
  6205. if (item->IsMount()) {
  6206. StopRiding();
  6207. HorseSummon(false);
  6208. }
  6209. #endif
  6210.  
  6211. if (false == CanUnequipNow(item))
  6212. return false;
  6213.  
  6214. if (item->IsDragonSoul())
  6215. pos = GetEmptyDragonSoulInventory(item);
  6216. else
  6217. pos = GetEmptyInventory(item->GetSize());
  6218.  
  6219. // HARD CODING
  6220. if (item->GetVnum() == UNIQUE_ITEM_HIDE_ALIGNMENT_TITLE)
  6221. ShowAlignment(true);
  6222.  
  6223. item->RemoveFromCharacter();
  6224. if (item->IsDragonSoul())
  6225. {
  6226. item->AddToCharacter(this, TItemPos(DRAGON_SOUL_INVENTORY, pos));
  6227. }
  6228. else
  6229. item->AddToCharacter(this, TItemPos(INVENTORY, pos));
  6230.  
  6231. CheckMaximumPoints();
  6232.  
  6233. return true;
  6234. }
  6235.  
  6236.  
  6237. bool CHARACTER::EquipItem(LPITEM item, int iCandidateCell)
  6238. {
  6239. if (item->IsExchanging())
  6240. return false;
  6241.  
  6242. if (false == item->IsEquipable())
  6243. return false;
  6244.  
  6245. if (false == CanEquipNow(item))
  6246. return false;
  6247.  
  6248. int iWearCell = item->FindEquipCell(this, iCandidateCell);
  6249.  
  6250. if (iWearCell < 0)
  6251. return false;
  6252.  
  6253. if (iWearCell == WEAR_BODY && IsRiding() && (item->GetVnum() >= 11901 && item->GetVnum() <= 11904))
  6254. {
  6255. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말을 탄 상태에서 예복을 입을 수 없습니다."));
  6256. return false;
  6257. }
  6258.  
  6259. if (iWearCell != WEAR_ARROW && IsPolymorphed())
  6260. {
  6261. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("둔갑 중에는 착용중인 장비를 변경할 수 없습니다."));
  6262. return false;
  6263. }
  6264.  
  6265. if (FN_check_item_sex(this, item) == false)
  6266. {
  6267. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("성별이 맞지않아 이 아이템을 사용할 수 없습니다."));
  6268. return false;
  6269. }
  6270.  
  6271. if(item->IsRideItem() && IsRiding())
  6272. {
  6273. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이미 탈것을 이용중입니다."));
  6274. return false;
  6275. }
  6276.  
  6277. DWORD dwCurTime = get_dword_time();
  6278.  
  6279. if (iWearCell != WEAR_ARROW
  6280. && (dwCurTime - GetLastAttackTime() <= 1500 || dwCurTime - m_dwLastSkillTime <= 1500))
  6281. {
  6282. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("가만히 있을 때만 착용할 수 있습니다."));
  6283. return false;
  6284. }
  6285.  
  6286. if (item->IsDragonSoul())
  6287. {
  6288. if(GetInventoryItem(INVENTORY_MAX_NUM + iWearCell))
  6289. {
  6290. ChatPacket(CHAT_TYPE_INFO, "이미 같은 종류의 용혼석을 착용하고 있습니다.");
  6291. return false;
  6292. }
  6293.  
  6294. if (!item->EquipTo(this, iWearCell))
  6295. {
  6296. return false;
  6297. }
  6298. }
  6299. // 용혼석이 아님.
  6300. else
  6301. {
  6302. // 착용할 곳에 아이템이 있다면,
  6303. if (GetWear(iWearCell) && !IS_SET(GetWear(iWearCell)->GetFlag(), ITEM_FLAG_IRREMOVABLE))
  6304. {
  6305. // 이 아이템은 한번 박히면 변경 불가. swap 역시 완전 불가
  6306. if (item->GetWearFlag() == WEARABLE_ABILITY)
  6307. return false;
  6308.  
  6309. if (false == SwapItem(item->GetCell(), INVENTORY_MAX_NUM + iWearCell))
  6310. {
  6311. return false;
  6312. }
  6313. }
  6314. else
  6315. {
  6316. BYTE bOldCell = item->GetCell();
  6317.  
  6318. if (item->EquipTo(this, iWearCell))
  6319. {
  6320. SyncQuickslot(QUICKSLOT_TYPE_ITEM, bOldCell, iWearCell);
  6321. }
  6322. }
  6323. }
  6324.  
  6325. if (true == item->IsEquipped())
  6326. {
  6327. // 아이템 최초 사용 이후부터는 사용하지 않아도 시간이 차감되는 방식 처리.
  6328. if (-1 != item->GetProto()->cLimitRealTimeFirstUseIndex)
  6329. {
  6330. // 한 번이라도 사용한 아이템인지 여부는 Socket1을 보고 판단한다. (Socket1에 사용횟수 기록)
  6331. if (0 == item->GetSocket(1))
  6332. {
  6333. // 사용가능시간은 Default 값으로 Limit Value 값을 사용하되, Socket0에 값이 있으면 그 값을 사용하도록 한다. (단위는 초)
  6334. long duration = (0 != item->GetSocket(0)) ? item->GetSocket(0) : item->GetProto()->aLimits[item->GetProto()->cLimitRealTimeFirstUseIndex].lValue;
  6335.  
  6336. if (0 == duration)
  6337. duration = 60 * 60 * 24 * 7;
  6338.  
  6339. item->SetSocket(0, time(0) + duration);
  6340. item->StartRealTimeExpireEvent();
  6341. }
  6342.  
  6343. item->SetSocket(1, item->GetSocket(1) + 1);
  6344. }
  6345.  
  6346. if (item->GetVnum() == UNIQUE_ITEM_HIDE_ALIGNMENT_TITLE)
  6347. ShowAlignment(false);
  6348.  
  6349. const DWORD& dwVnum = item->GetVnum();
  6350.  
  6351. // 라마단 이벤트 초승달의 반지(71135) 착용시 이펙트 발동
  6352. if (true == CItemVnumHelper::IsRamadanMoonRing(dwVnum))
  6353. {
  6354. this->EffectPacket(SE_EQUIP_RAMADAN_RING);
  6355. }
  6356. // 할로윈 사탕(71136) 착용시 이펙트 발동
  6357. else if (true == CItemVnumHelper::IsHalloweenCandy(dwVnum))
  6358. {
  6359. this->EffectPacket(SE_EQUIP_HALLOWEEN_CANDY);
  6360. }
  6361. // 행복의 반지(71143) 착용시 이펙트 발동
  6362. else if (true == CItemVnumHelper::IsHappinessRing(dwVnum))
  6363. {
  6364. this->EffectPacket(SE_EQUIP_HAPPINESS_RING);
  6365. }
  6366. // 사랑의 팬던트(71145) 착용시 이펙트 발동
  6367. else if (true == CItemVnumHelper::IsLovePendant(dwVnum))
  6368. {
  6369. this->EffectPacket(SE_EQUIP_LOVE_PENDANT);
  6370. }
  6371. // ITEM_UNIQUE의 경우, SpecialItemGroup에 정의되어 있고, (item->GetSIGVnum() != NULL)
  6372. //
  6373. else if (ITEM_UNIQUE == item->GetType() && 0 != item->GetSIGVnum())
  6374. {
  6375. const CSpecialItemGroup* pGroup = ITEM_MANAGER::instance().GetSpecialItemGroup(item->GetSIGVnum());
  6376. if (NULL != pGroup)
  6377. {
  6378. const CSpecialAttrGroup* pAttrGroup = ITEM_MANAGER::instance().GetSpecialAttrGroup(pGroup->GetAttrVnum(item->GetVnum()));
  6379. if (NULL != pAttrGroup)
  6380. {
  6381. const std::string& std = pAttrGroup->m_stEffectFileName;
  6382. SpecificEffectPacket(std.c_str());
  6383. }
  6384. }
  6385. }
  6386.  
  6387. if (UNIQUE_SPECIAL_RIDE == item->GetSubType() && IS_SET(item->GetFlag(), ITEM_FLAG_QUEST_USE))
  6388. {
  6389. quest::CQuestManager::instance().UseItem(GetPlayerID(), item, false);
  6390. }
  6391. }
  6392.  
  6393. #ifdef ENABLE_MOUNT_LIKE_HORSE
  6394. if (item->IsMount())
  6395. HorseSummon(true);
  6396. #endif
  6397.  
  6398. return true;
  6399. }
  6400.  
  6401. void CHARACTER::BuffOnAttr_AddBuffsFromItem(LPITEM pItem)
  6402. {
  6403. for (int i = 0; i < sizeof(g_aBuffOnAttrPoints)/sizeof(g_aBuffOnAttrPoints[0]); i++)
  6404. {
  6405. TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.find(g_aBuffOnAttrPoints[i]);
  6406. if (it != m_map_buff_on_attrs.end())
  6407. {
  6408. it->second->AddBuffFromItem(pItem);
  6409. }
  6410. }
  6411. }
  6412.  
  6413. void CHARACTER::BuffOnAttr_RemoveBuffsFromItem(LPITEM pItem)
  6414. {
  6415. for (int i = 0; i < sizeof(g_aBuffOnAttrPoints)/sizeof(g_aBuffOnAttrPoints[0]); i++)
  6416. {
  6417. TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.find(g_aBuffOnAttrPoints[i]);
  6418. if (it != m_map_buff_on_attrs.end())
  6419. {
  6420. it->second->RemoveBuffFromItem(pItem);
  6421. }
  6422. }
  6423. }
  6424.  
  6425. void CHARACTER::BuffOnAttr_ClearAll()
  6426. {
  6427. for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++)
  6428. {
  6429. CBuffOnAttributes* pBuff = it->second;
  6430. if (pBuff)
  6431. {
  6432. pBuff->Initialize();
  6433. }
  6434. }
  6435. }
  6436.  
  6437. void CHARACTER::BuffOnAttr_ValueChange(BYTE bType, BYTE bOldValue, BYTE bNewValue)
  6438. {
  6439. TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.find(bType);
  6440.  
  6441. if (0 == bNewValue)
  6442. {
  6443. if (m_map_buff_on_attrs.end() == it)
  6444. return;
  6445. else
  6446. it->second->Off();
  6447. }
  6448. else if(0 == bOldValue)
  6449. {
  6450. CBuffOnAttributes* pBuff;
  6451. if (m_map_buff_on_attrs.end() == it)
  6452. {
  6453. switch (bType)
  6454. {
  6455. case POINT_ENERGY:
  6456. {
  6457. static BYTE abSlot[] = { WEAR_BODY, WEAR_HEAD, WEAR_FOOTS, WEAR_WRIST, WEAR_WEAPON, WEAR_NECK, WEAR_EAR, WEAR_SHIELD };
  6458. static std::vector <BYTE> vec_slots (abSlot, abSlot + _countof(abSlot));
  6459. pBuff = M2_NEW CBuffOnAttributes(this, bType, &vec_slots);
  6460. }
  6461. break;
  6462. case POINT_COSTUME_ATTR_BONUS:
  6463. {
  6464. static BYTE abSlot[] = { WEAR_COSTUME_BODY, WEAR_COSTUME_HAIR };
  6465. static std::vector <BYTE> vec_slots (abSlot, abSlot + _countof(abSlot));
  6466. pBuff = M2_NEW CBuffOnAttributes(this, bType, &vec_slots);
  6467. }
  6468. break;
  6469. default:
  6470. break;
  6471. }
  6472. m_map_buff_on_attrs.insert(TMapBuffOnAttrs::value_type(bType, pBuff));
  6473.  
  6474. }
  6475. else
  6476. pBuff = it->second;
  6477.  
  6478. pBuff->On(bNewValue);
  6479. }
  6480. else
  6481. {
  6482. if (m_map_buff_on_attrs.end() == it)
  6483. return;
  6484. else
  6485. it->second->ChangeBuffValue(bNewValue);
  6486. }
  6487. }
  6488.  
  6489.  
  6490. LPITEM CHARACTER::FindSpecifyItem(DWORD vnum) const
  6491. {
  6492. for (int i = 0; i < INVENTORY_MAX_NUM; ++i)
  6493. if (GetInventoryItem(i) && GetInventoryItem(i)->GetVnum() == vnum)
  6494. return GetInventoryItem(i);
  6495.  
  6496. return NULL;
  6497. }
  6498.  
  6499. LPITEM CHARACTER::FindItemByID(DWORD id) const
  6500. {
  6501. for (int i=0 ; i < INVENTORY_MAX_NUM ; ++i)
  6502. {
  6503. if (NULL != GetInventoryItem(i) && GetInventoryItem(i)->GetID() == id)
  6504. return GetInventoryItem(i);
  6505. }
  6506.  
  6507. for (int i=BELT_INVENTORY_SLOT_START; i < BELT_INVENTORY_SLOT_END ; ++i)
  6508. {
  6509. if (NULL != GetInventoryItem(i) && GetInventoryItem(i)->GetID() == id)
  6510. return GetInventoryItem(i);
  6511. }
  6512.  
  6513. return NULL;
  6514. }
  6515.  
  6516. int CHARACTER::CountSpecifyItem(DWORD vnum) const
  6517. {
  6518. int count = 0;
  6519. LPITEM item;
  6520.  
  6521. for (int i = 0; i < INVENTORY_MAX_NUM; ++i)
  6522. {
  6523. item = GetInventoryItem(i);
  6524. if (NULL != item && item->GetVnum() == vnum)
  6525. {
  6526. // 개인 상점에 등록된 물건이면 넘어간다.
  6527. if (m_pkMyShop && m_pkMyShop->IsSellingItem(item->GetID()))
  6528. {
  6529. continue;
  6530. }
  6531. else
  6532. {
  6533. count += item->GetCount();
  6534. }
  6535. }
  6536. }
  6537.  
  6538. return count;
  6539. }
  6540.  
  6541. void CHARACTER::RemoveSpecifyItem(DWORD vnum, DWORD count)
  6542. {
  6543. if (0 == count)
  6544. return;
  6545.  
  6546. for (UINT i = 0; i < INVENTORY_MAX_NUM; ++i)
  6547. {
  6548. if (NULL == GetInventoryItem(i))
  6549. continue;
  6550.  
  6551. if (GetInventoryItem(i)->GetVnum() != vnum)
  6552. continue;
  6553.  
  6554. //개인 상점에 등록된 물건이면 넘어간다. (개인 상점에서 판매될때 이 부분으로 들어올 경우 문제!)
  6555. if(m_pkMyShop)
  6556. {
  6557. bool isItemSelling = m_pkMyShop->IsSellingItem(GetInventoryItem(i)->GetID());
  6558. if (isItemSelling)
  6559. continue;
  6560. }
  6561.  
  6562. if (vnum >= 80003 && vnum <= 80007)
  6563. LogManager::instance().GoldBarLog(GetPlayerID(), GetInventoryItem(i)->GetID(), QUEST, "RemoveSpecifyItem");
  6564.  
  6565. if (count >= GetInventoryItem(i)->GetCount())
  6566. {
  6567. count -= GetInventoryItem(i)->GetCount();
  6568. GetInventoryItem(i)->SetCount(0);
  6569.  
  6570. if (0 == count)
  6571. return;
  6572. }
  6573. else
  6574. {
  6575. GetInventoryItem(i)->SetCount(GetInventoryItem(i)->GetCount() - count);
  6576. return;
  6577. }
  6578. }
  6579.  
  6580. // 예외처리가 약하다.
  6581. if (count)
  6582. sys_log(0, "CHARACTER::RemoveSpecifyItem cannot remove enough item vnum %u, still remain %d", vnum, count);
  6583. }
  6584.  
  6585. int CHARACTER::CountSpecifyTypeItem(BYTE type) const
  6586. {
  6587. int count = 0;
  6588.  
  6589. for (int i = 0; i < INVENTORY_MAX_NUM; ++i)
  6590. {
  6591. LPITEM pItem = GetInventoryItem(i);
  6592. if (pItem != NULL && pItem->GetType() == type)
  6593. {
  6594. count += pItem->GetCount();
  6595. }
  6596. }
  6597.  
  6598. return count;
  6599. }
  6600.  
  6601. void CHARACTER::RemoveSpecifyTypeItem(BYTE type, DWORD count)
  6602. {
  6603. if (0 == count)
  6604. return;
  6605.  
  6606. for (UINT i = 0; i < INVENTORY_MAX_NUM; ++i)
  6607. {
  6608. if (NULL == GetInventoryItem(i))
  6609. continue;
  6610.  
  6611. if (GetInventoryItem(i)->GetType() != type)
  6612. continue;
  6613.  
  6614. //개인 상점에 등록된 물건이면 넘어간다. (개인 상점에서 판매될때 이 부분으로 들어올 경우 문제!)
  6615. if(m_pkMyShop)
  6616. {
  6617. bool isItemSelling = m_pkMyShop->IsSellingItem(GetInventoryItem(i)->GetID());
  6618. if (isItemSelling)
  6619. continue;
  6620. }
  6621.  
  6622. if (count >= GetInventoryItem(i)->GetCount())
  6623. {
  6624. count -= GetInventoryItem(i)->GetCount();
  6625. GetInventoryItem(i)->SetCount(0);
  6626.  
  6627. if (0 == count)
  6628. return;
  6629. }
  6630. else
  6631. {
  6632. GetInventoryItem(i)->SetCount(GetInventoryItem(i)->GetCount() - count);
  6633. return;
  6634. }
  6635. }
  6636. }
  6637.  
  6638. void CHARACTER::AutoGiveItem(LPITEM item, bool longOwnerShip)
  6639. {
  6640. if (NULL == item)
  6641. {
  6642. sys_err ("NULL point.");
  6643. return;
  6644. }
  6645. if (item->GetOwner())
  6646. {
  6647. sys_err ("item %d 's owner exists!",item->GetID());
  6648. return;
  6649. }
  6650.  
  6651. int cell;
  6652. if (item->IsDragonSoul())
  6653. {
  6654. cell = GetEmptyDragonSoulInventory(item);
  6655. }
  6656. else
  6657. {
  6658. cell = GetEmptyInventory (item->GetSize());
  6659. }
  6660.  
  6661. if (cell != -1)
  6662. {
  6663. if (item->IsDragonSoul())
  6664. item->AddToCharacter(this, TItemPos(DRAGON_SOUL_INVENTORY, cell));
  6665. else
  6666. item->AddToCharacter(this, TItemPos(INVENTORY, cell));
  6667.  
  6668. LogManager::instance().ItemLog(this, item, "SYSTEM", item->GetName());
  6669.  
  6670. if (item->GetType() == ITEM_USE && item->GetSubType() == USE_POTION)
  6671. {
  6672. TQuickslot * pSlot;
  6673.  
  6674. if (GetQuickslot(0, &pSlot) && pSlot->type == QUICKSLOT_TYPE_NONE)
  6675. {
  6676. TQuickslot slot;
  6677. slot.type = QUICKSLOT_TYPE_ITEM;
  6678. slot.pos = cell;
  6679. SetQuickslot(0, slot);
  6680. }
  6681. }
  6682. }
  6683. else
  6684. {
  6685. item->AddToGround (GetMapIndex(), GetXYZ());
  6686. item->StartDestroyEvent();
  6687.  
  6688. if (longOwnerShip)
  6689. item->SetOwnership (this, 300);
  6690. else
  6691. item->SetOwnership (this, 60);
  6692. LogManager::instance().ItemLog(this, item, "SYSTEM_DROP", item->GetName());
  6693. }
  6694. }
  6695.  
  6696. LPITEM CHARACTER::AutoGiveItem(DWORD dwItemVnum, BYTE bCount, int iRarePct, bool bMsg)
  6697. {
  6698. TItemTable * p = ITEM_MANAGER::instance().GetTable(dwItemVnum);
  6699.  
  6700. if (!p)
  6701. return NULL;
  6702.  
  6703. DBManager::instance().SendMoneyLog(MONEY_LOG_DROP, dwItemVnum, bCount);
  6704.  
  6705. if (p->dwFlags & ITEM_FLAG_STACKABLE && p->bType != ITEM_BLEND)
  6706. {
  6707. for (int i = 0; i < INVENTORY_MAX_NUM; ++i)
  6708. {
  6709. LPITEM item = GetInventoryItem(i);
  6710.  
  6711. if (!item)
  6712. continue;
  6713.  
  6714. if (item->GetVnum() == dwItemVnum && FN_check_item_socket(item))
  6715. {
  6716. if (IS_SET(p->dwFlags, ITEM_FLAG_MAKECOUNT))
  6717. {
  6718. if (bCount < p->alValues[1])
  6719. bCount = p->alValues[1];
  6720. }
  6721.  
  6722. BYTE bCount2 = MIN(200 - item->GetCount(), bCount);
  6723. bCount -= bCount2;
  6724.  
  6725. item->SetCount(item->GetCount() + bCount2);
  6726.  
  6727. if (bCount == 0)
  6728. {
  6729. if (bMsg)
  6730. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 획득: %s"), item->GetName());
  6731.  
  6732. return item;
  6733. }
  6734. }
  6735. }
  6736. }
  6737.  
  6738. LPITEM item = ITEM_MANAGER::instance().CreateItem(dwItemVnum, bCount, 0, true);
  6739.  
  6740. if (!item)
  6741. {
  6742. sys_err("cannot create item by vnum %u (name: %s)", dwItemVnum, GetName());
  6743. return NULL;
  6744. }
  6745.  
  6746. if (item->GetType() == ITEM_BLEND)
  6747. {
  6748. for (int i=0; i < INVENTORY_MAX_NUM; i++)
  6749. {
  6750. LPITEM inv_item = GetInventoryItem(i);
  6751.  
  6752. if (inv_item == NULL) continue;
  6753.  
  6754. if (inv_item->GetType() == ITEM_BLEND)
  6755. {
  6756. if (inv_item->GetVnum() == item->GetVnum())
  6757. {
  6758. if (inv_item->GetSocket(0) == item->GetSocket(0) &&
  6759. inv_item->GetSocket(1) == item->GetSocket(1) &&
  6760. inv_item->GetSocket(2) == item->GetSocket(2) &&
  6761. inv_item->GetCount() < ITEM_MAX_COUNT)
  6762. {
  6763. inv_item->SetCount(inv_item->GetCount() + item->GetCount());
  6764. return inv_item;
  6765. }
  6766. }
  6767. }
  6768. }
  6769. }
  6770.  
  6771. int iEmptyCell;
  6772. if (item->IsDragonSoul())
  6773. {
  6774. iEmptyCell = GetEmptyDragonSoulInventory(item);
  6775. }
  6776. else
  6777. iEmptyCell = GetEmptyInventory(item->GetSize());
  6778.  
  6779. if (iEmptyCell != -1)
  6780. {
  6781. if (bMsg)
  6782. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 획득: %s"), item->GetName());
  6783.  
  6784. if (item->IsDragonSoul())
  6785. item->AddToCharacter(this, TItemPos(DRAGON_SOUL_INVENTORY, iEmptyCell));
  6786. else
  6787. item->AddToCharacter(this, TItemPos(INVENTORY, iEmptyCell));
  6788. LogManager::instance().ItemLog(this, item, "SYSTEM", item->GetName());
  6789.  
  6790. if (item->GetType() == ITEM_USE && item->GetSubType() == USE_POTION)
  6791. {
  6792. TQuickslot * pSlot;
  6793.  
  6794. if (GetQuickslot(0, &pSlot) && pSlot->type == QUICKSLOT_TYPE_NONE)
  6795. {
  6796. TQuickslot slot;
  6797. slot.type = QUICKSLOT_TYPE_ITEM;
  6798. slot.pos = iEmptyCell;
  6799. SetQuickslot(0, slot);
  6800. }
  6801. }
  6802. }
  6803. else
  6804. {
  6805. item->AddToGround(GetMapIndex(), GetXYZ());
  6806. item->StartDestroyEvent();
  6807. // 안티 드랍 flag가 걸려있는 아이템의 경우,
  6808. // 인벤에 빈 공간이 없어서 어쩔 수 없이 떨어트리게 되면,
  6809. // ownership을 아이템이 사라질 때까지(300초) 유지한다.
  6810. if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_DROP))
  6811. item->SetOwnership(this, 300);
  6812. else
  6813. item->SetOwnership(this, 60);
  6814. LogManager::instance().ItemLog(this, item, "SYSTEM_DROP", item->GetName());
  6815. }
  6816.  
  6817. sys_log(0,
  6818. "7: %d %d", dwItemVnum, bCount);
  6819. return item;
  6820. }
  6821.  
  6822. bool CHARACTER::GiveItem(LPCHARACTER victim, TItemPos Cell)
  6823. {
  6824. if (!CanHandleItem())
  6825. return false;
  6826.  
  6827. LPITEM item = GetItem(Cell);
  6828.  
  6829. if (item && !item->IsExchanging())
  6830. {
  6831. if (victim->CanReceiveItem(this, item))
  6832. {
  6833. victim->ReceiveItem(this, item);
  6834. return true;
  6835. }
  6836. }
  6837.  
  6838. return false;
  6839. }
  6840.  
  6841. bool CHARACTER::CanReceiveItem(LPCHARACTER from, LPITEM item) const
  6842. {
  6843. if (IsPC())
  6844. return false;
  6845.  
  6846. // TOO_LONG_DISTANCE_EXCHANGE_BUG_FIX
  6847. if (DISTANCE_APPROX(GetX() - from->GetX(), GetY() - from->GetY()) > 2000)
  6848. return false;
  6849. // END_OF_TOO_LONG_DISTANCE_EXCHANGE_BUG_FIX
  6850.  
  6851. switch (GetRaceNum())
  6852. {
  6853. case fishing::CAMPFIRE_MOB:
  6854. if (item->GetType() == ITEM_FISH &&
  6855. (item->GetSubType() == FISH_ALIVE || item->GetSubType() == FISH_DEAD))
  6856. return true;
  6857. break;
  6858.  
  6859. case fishing::FISHER_MOB:
  6860. if (item->GetType() == ITEM_ROD)
  6861. return true;
  6862. break;
  6863.  
  6864. // BUILDING_NPC
  6865. case BLACKSMITH_WEAPON_MOB:
  6866. case DEVILTOWER_BLACKSMITH_WEAPON_MOB:
  6867. if (item->GetType() == ITEM_WEAPON &&
  6868. item->GetRefinedVnum())
  6869. return true;
  6870. else
  6871. return false;
  6872. break;
  6873.  
  6874. case BLACKSMITH_ARMOR_MOB:
  6875. case DEVILTOWER_BLACKSMITH_ARMOR_MOB:
  6876. if (item->GetType() == ITEM_ARMOR &&
  6877. (item->GetSubType() == ARMOR_BODY || item->GetSubType() == ARMOR_SHIELD || item->GetSubType() == ARMOR_HEAD) &&
  6878. item->GetRefinedVnum())
  6879. return true;
  6880. else
  6881. return false;
  6882. break;
  6883.  
  6884. case BLACKSMITH_ACCESSORY_MOB:
  6885. case DEVILTOWER_BLACKSMITH_ACCESSORY_MOB:
  6886. if (item->GetType() == ITEM_ARMOR &&
  6887. !(item->GetSubType() == ARMOR_BODY || item->GetSubType() == ARMOR_SHIELD || item->GetSubType() == ARMOR_HEAD) &&
  6888. item->GetRefinedVnum())
  6889. return true;
  6890. else
  6891. return false;
  6892. break;
  6893. // END_OF_BUILDING_NPC
  6894.  
  6895. case BLACKSMITH_MOB:
  6896. if (item->GetRefinedVnum() && item->GetRefineSet() < 500)
  6897. {
  6898. return true;
  6899. }
  6900. else
  6901. {
  6902. return false;
  6903. }
  6904.  
  6905. case BLACKSMITH2_MOB:
  6906. if (item->GetRefineSet() >= 500)
  6907. {
  6908. return true;
  6909. }
  6910. else
  6911. {
  6912. return false;
  6913. }
  6914.  
  6915. case ALCHEMIST_MOB:
  6916. if (item->GetRefinedVnum())
  6917. return true;
  6918. break;
  6919.  
  6920. case 20101:
  6921. case 20102:
  6922. case 20103:
  6923. // 초급 말
  6924. if (item->GetVnum() == ITEM_REVIVE_HORSE_1)
  6925. {
  6926. if (!IsDead())
  6927. {
  6928. from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("죽지 않은 말에게 선초를 먹일 수 없습니다."));
  6929. return false;
  6930. }
  6931. return true;
  6932. }
  6933. else if (item->GetVnum() == ITEM_HORSE_FOOD_1)
  6934. {
  6935. if (IsDead())
  6936. {
  6937. from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("죽은 말에게 사료를 먹일 수 없습니다."));
  6938. return false;
  6939. }
  6940. return true;
  6941. }
  6942. else if (item->GetVnum() == ITEM_HORSE_FOOD_2 || item->GetVnum() == ITEM_HORSE_FOOD_3)
  6943. {
  6944. return false;
  6945. }
  6946. break;
  6947. case 20104:
  6948. case 20105:
  6949. case 20106:
  6950. // 중급 말
  6951. if (item->GetVnum() == ITEM_REVIVE_HORSE_2)
  6952. {
  6953. if (!IsDead())
  6954. {
  6955. from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("죽지 않은 말에게 선초를 먹일 수 없습니다."));
  6956. return false;
  6957. }
  6958. return true;
  6959. }
  6960. else if (item->GetVnum() == ITEM_HORSE_FOOD_2)
  6961. {
  6962. if (IsDead())
  6963. {
  6964. from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("죽은 말에게 사료를 먹일 수 없습니다."));
  6965. return false;
  6966. }
  6967. return true;
  6968. }
  6969. else if (item->GetVnum() == ITEM_HORSE_FOOD_1 || item->GetVnum() == ITEM_HORSE_FOOD_3)
  6970. {
  6971. return false;
  6972. }
  6973. break;
  6974. case 20107:
  6975. case 20108:
  6976. case 20109:
  6977. // 고급 말
  6978. if (item->GetVnum() == ITEM_REVIVE_HORSE_3)
  6979. {
  6980. if (!IsDead())
  6981. {
  6982. from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("죽지 않은 말에게 선초를 먹일 수 없습니다."));
  6983. return false;
  6984. }
  6985. return true;
  6986. }
  6987. else if (item->GetVnum() == ITEM_HORSE_FOOD_3)
  6988. {
  6989. if (IsDead())
  6990. {
  6991. from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("죽은 말에게 사료를 먹일 수 없습니다."));
  6992. return false;
  6993. }
  6994. return true;
  6995. }
  6996. else if (item->GetVnum() == ITEM_HORSE_FOOD_1 || item->GetVnum() == ITEM_HORSE_FOOD_2)
  6997. {
  6998. return false;
  6999. }
  7000. break;
  7001. }
  7002.  
  7003. //if (IS_SET(item->GetFlag(), ITEM_FLAG_QUEST_GIVE))
  7004. {
  7005. return true;
  7006. }
  7007.  
  7008. return false;
  7009. }
  7010.  
  7011. void CHARACTER::ReceiveItem(LPCHARACTER from, LPITEM item)
  7012. {
  7013. if (IsPC())
  7014. return;
  7015.  
  7016. switch (GetRaceNum())
  7017. {
  7018. case fishing::CAMPFIRE_MOB:
  7019. if (item->GetType() == ITEM_FISH && (item->GetSubType() == FISH_ALIVE || item->GetSubType() == FISH_DEAD))
  7020. fishing::Grill(from, item);
  7021. else
  7022. {
  7023. // TAKE_ITEM_BUG_FIX
  7024. from->SetQuestNPCID(GetVID());
  7025. // END_OF_TAKE_ITEM_BUG_FIX
  7026. quest::CQuestManager::instance().TakeItem(from->GetPlayerID(), GetRaceNum(), item);
  7027. }
  7028. break;
  7029.  
  7030. // DEVILTOWER_NPC
  7031. case DEVILTOWER_BLACKSMITH_WEAPON_MOB:
  7032. case DEVILTOWER_BLACKSMITH_ARMOR_MOB:
  7033. case DEVILTOWER_BLACKSMITH_ACCESSORY_MOB:
  7034. if (item->GetRefinedVnum() != 0 && item->GetRefineSet() != 0 && item->GetRefineSet() < 500)
  7035. {
  7036. from->SetRefineNPC(this);
  7037. from->RefineInformation(item->GetCell(), REFINE_TYPE_MONEY_ONLY);
  7038. }
  7039. else
  7040. {
  7041. from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템은 개량할 수 없습니다."));
  7042. }
  7043. break;
  7044. // END_OF_DEVILTOWER_NPC
  7045.  
  7046. case BLACKSMITH_MOB:
  7047. case BLACKSMITH2_MOB:
  7048. case BLACKSMITH_WEAPON_MOB:
  7049. case BLACKSMITH_ARMOR_MOB:
  7050. case BLACKSMITH_ACCESSORY_MOB:
  7051. if (item->GetRefinedVnum())
  7052. {
  7053. from->SetRefineNPC(this);
  7054. from->RefineInformation(item->GetCell(), REFINE_TYPE_NORMAL);
  7055. }
  7056. else
  7057. {
  7058. from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템은 개량할 수 없습니다."));
  7059. }
  7060. break;
  7061.  
  7062. case 20101:
  7063. case 20102:
  7064. case 20103:
  7065. case 20104:
  7066. case 20105:
  7067. case 20106:
  7068. case 20107:
  7069. case 20108:
  7070. case 20109:
  7071. if (item->GetVnum() == ITEM_REVIVE_HORSE_1 ||
  7072. item->GetVnum() == ITEM_REVIVE_HORSE_2 ||
  7073. item->GetVnum() == ITEM_REVIVE_HORSE_3)
  7074. {
  7075. from->ReviveHorse();
  7076. item->SetCount(item->GetCount()-1);
  7077. from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말에게 선초를 주었습니다."));
  7078. }
  7079. else if (item->GetVnum() == ITEM_HORSE_FOOD_1 ||
  7080. item->GetVnum() == ITEM_HORSE_FOOD_2 ||
  7081. item->GetVnum() == ITEM_HORSE_FOOD_3)
  7082. {
  7083. from->FeedHorse();
  7084. from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말에게 사료를 주었습니다."));
  7085. item->SetCount(item->GetCount()-1);
  7086. EffectPacket(SE_HPUP_RED);
  7087. }
  7088. break;
  7089.  
  7090. default:
  7091. sys_log(0, "TakeItem %s %d %s", from->GetName(), GetRaceNum(), item->GetName());
  7092. from->SetQuestNPCID(GetVID());
  7093. quest::CQuestManager::instance().TakeItem(from->GetPlayerID(), GetRaceNum(), item);
  7094. break;
  7095. }
  7096. }
  7097.  
  7098. bool CHARACTER::IsEquipUniqueItem(DWORD dwItemVnum) const
  7099. {
  7100. {
  7101. LPITEM u = GetWear(WEAR_UNIQUE1);
  7102.  
  7103. if (u && u->GetVnum() == dwItemVnum)
  7104. return true;
  7105. }
  7106.  
  7107. {
  7108. LPITEM u = GetWear(WEAR_UNIQUE2);
  7109.  
  7110. if (u && u->GetVnum() == dwItemVnum)
  7111. return true;
  7112. }
  7113.  
  7114. // 언어반지인 경우 언어반지(견본) 인지도 체크한다.
  7115. if (dwItemVnum == UNIQUE_ITEM_RING_OF_LANGUAGE)
  7116. return IsEquipUniqueItem(UNIQUE_ITEM_RING_OF_LANGUAGE_SAMPLE);
  7117.  
  7118. return false;
  7119. }
  7120.  
  7121. // CHECK_UNIQUE_GROUP
  7122. bool CHARACTER::IsEquipUniqueGroup(DWORD dwGroupVnum) const
  7123. {
  7124. {
  7125. LPITEM u = GetWear(WEAR_UNIQUE1);
  7126.  
  7127. if (u && u->GetSpecialGroup() == (int) dwGroupVnum)
  7128. return true;
  7129. }
  7130.  
  7131. {
  7132. LPITEM u = GetWear(WEAR_UNIQUE2);
  7133.  
  7134. if (u && u->GetSpecialGroup() == (int) dwGroupVnum)
  7135. return true;
  7136. }
  7137.  
  7138. return false;
  7139. }
  7140. // END_OF_CHECK_UNIQUE_GROUP
  7141.  
  7142. void CHARACTER::SetRefineMode(int iAdditionalCell)
  7143. {
  7144. m_iRefineAdditionalCell = iAdditionalCell;
  7145. m_bUnderRefine = true;
  7146. }
  7147.  
  7148. void CHARACTER::ClearRefineMode()
  7149. {
  7150. m_bUnderRefine = false;
  7151. SetRefineNPC( NULL );
  7152. }
  7153.  
  7154. bool CHARACTER::GiveItemFromSpecialItemGroup(DWORD dwGroupNum, std::vector<DWORD> &dwItemVnums,
  7155. std::vector<DWORD> &dwItemCounts, std::vector <LPITEM> &item_gets, int &count)
  7156. {
  7157. const CSpecialItemGroup* pGroup = ITEM_MANAGER::instance().GetSpecialItemGroup(dwGroupNum);
  7158.  
  7159. if (!pGroup)
  7160. {
  7161. sys_err("cannot find special item group %d", dwGroupNum);
  7162. return false;
  7163. }
  7164.  
  7165. std::vector <int> idxes;
  7166. int n = pGroup->GetMultiIndex(idxes);
  7167.  
  7168. bool bSuccess;
  7169.  
  7170. for (int i = 0; i < n; i++)
  7171. {
  7172. bSuccess = false;
  7173. int idx = idxes[i];
  7174. DWORD dwVnum = pGroup->GetVnum(idx);
  7175. DWORD dwCount = pGroup->GetCount(idx);
  7176. int iRarePct = pGroup->GetRarePct(idx);
  7177. LPITEM item_get = NULL;
  7178. switch (dwVnum)
  7179. {
  7180. case CSpecialItemGroup::GOLD:
  7181. PointChange(POINT_GOLD, dwCount);
  7182. LogManager::instance().CharLog(this, dwCount, "TREASURE_GOLD", "");
  7183.  
  7184. bSuccess = true;
  7185. break;
  7186. case CSpecialItemGroup::EXP:
  7187. {
  7188. PointChange(POINT_EXP, dwCount);
  7189. LogManager::instance().CharLog(this, dwCount, "TREASURE_EXP", "");
  7190.  
  7191. bSuccess = true;
  7192. }
  7193. break;
  7194.  
  7195. case CSpecialItemGroup::MOB:
  7196. {
  7197. sys_log(0, "CSpecialItemGroup::MOB %d", dwCount);
  7198. int x = GetX() + number(-500, 500);
  7199. int y = GetY() + number(-500, 500);
  7200.  
  7201. LPCHARACTER ch = CHARACTER_MANAGER::instance().SpawnMob(dwCount, GetMapIndex(), x, y, 0, true, -1);
  7202. if (ch)
  7203. ch->SetAggressive();
  7204. bSuccess = true;
  7205. }
  7206. break;
  7207. case CSpecialItemGroup::SLOW:
  7208. {
  7209. sys_log(0, "CSpecialItemGroup::SLOW %d", -(int)dwCount);
  7210. AddAffect(AFFECT_SLOW, POINT_MOV_SPEED, -(int)dwCount, AFF_SLOW, 300, 0, true);
  7211. bSuccess = true;
  7212. }
  7213. break;
  7214. case CSpecialItemGroup::DRAIN_HP:
  7215. {
  7216. int iDropHP = GetMaxHP()*dwCount/100;
  7217. sys_log(0, "CSpecialItemGroup::DRAIN_HP %d", -iDropHP);
  7218. iDropHP = MIN(iDropHP, GetHP()-1);
  7219. sys_log(0, "CSpecialItemGroup::DRAIN_HP %d", -iDropHP);
  7220. PointChange(POINT_HP, -iDropHP);
  7221. bSuccess = true;
  7222. }
  7223. break;
  7224. case CSpecialItemGroup::POISON:
  7225. {
  7226. AttackedByPoison(NULL);
  7227. bSuccess = true;
  7228. }
  7229. break;
  7230.  
  7231. case CSpecialItemGroup::MOB_GROUP:
  7232. {
  7233. int sx = GetX() - number(300, 500);
  7234. int sy = GetY() - number(300, 500);
  7235. int ex = GetX() + number(300, 500);
  7236. int ey = GetY() + number(300, 500);
  7237. CHARACTER_MANAGER::instance().SpawnGroup(dwCount, GetMapIndex(), sx, sy, ex, ey, NULL, true);
  7238.  
  7239. bSuccess = true;
  7240. }
  7241. break;
  7242. default:
  7243. {
  7244. item_get = AutoGiveItem(dwVnum, dwCount, iRarePct);
  7245.  
  7246. if (item_get)
  7247. {
  7248. bSuccess = true;
  7249. }
  7250. }
  7251. break;
  7252. }
  7253.  
  7254. if (bSuccess)
  7255. {
  7256. dwItemVnums.push_back(dwVnum);
  7257. dwItemCounts.push_back(dwCount);
  7258. item_gets.push_back(item_get);
  7259. count++;
  7260.  
  7261. }
  7262. else
  7263. {
  7264. return false;
  7265. }
  7266. }
  7267. return bSuccess;
  7268. }
  7269.  
  7270. // NEW_HAIR_STYLE_ADD
  7271. bool CHARACTER::ItemProcess_Hair(LPITEM item, int iDestCell)
  7272. {
  7273. if (item->CheckItemUseLevel(GetLevel()) == false)
  7274. {
  7275. // 레벨 제한에 걸림
  7276. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아직 이 머리를 사용할 수 없는 레벨입니다."));
  7277. return false;
  7278. }
  7279.  
  7280. DWORD hair = item->GetVnum();
  7281.  
  7282. switch (GetJob())
  7283. {
  7284. case JOB_WARRIOR :
  7285. hair -= 72000; // 73001 - 72000 = 1001 부터 헤어 번호 시작
  7286. break;
  7287.  
  7288. case JOB_ASSASSIN :
  7289. hair -= 71250;
  7290. break;
  7291.  
  7292. case JOB_SURA :
  7293. hair -= 70500;
  7294. break;
  7295.  
  7296. case JOB_SHAMAN :
  7297. hair -= 69750;
  7298. break;
  7299.  
  7300. default :
  7301. return false;
  7302. break;
  7303. }
  7304.  
  7305. if (hair == GetPart(PART_HAIR))
  7306. {
  7307. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("동일한 머리 스타일로는 교체할 수 없습니다."));
  7308. return true;
  7309. }
  7310.  
  7311. item->SetCount(item->GetCount() - 1);
  7312.  
  7313. SetPart(PART_HAIR, hair);
  7314. UpdatePacket();
  7315.  
  7316. return true;
  7317. }
  7318. // END_NEW_HAIR_STYLE_ADD
  7319.  
  7320. bool CHARACTER::ItemProcess_Polymorph(LPITEM item)
  7321. {
  7322. if (IsPolymorphed())
  7323. {
  7324. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이미 둔갑중인 상태입니다."));
  7325. return false;
  7326. }
  7327.  
  7328. if (true == IsRiding())
  7329. {
  7330. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("둔갑할 수 없는 상태입니다."));
  7331. return false;
  7332. }
  7333.  
  7334. DWORD dwVnum = item->GetSocket(0);
  7335.  
  7336. if (dwVnum == 0)
  7337. {
  7338. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("잘못된 둔갑 아이템입니다."));
  7339. item->SetCount(item->GetCount()-1);
  7340. return false;
  7341. }
  7342.  
  7343. const CMob* pMob = CMobManager::instance().Get(dwVnum);
  7344.  
  7345. if (pMob == NULL)
  7346. {
  7347. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("잘못된 둔갑 아이템입니다."));
  7348. item->SetCount(item->GetCount()-1);
  7349. return false;
  7350. }
  7351.  
  7352. switch (item->GetVnum())
  7353. {
  7354. case 70104 :
  7355. case 70105 :
  7356. case 70106 :
  7357. case 70107 :
  7358. case 71093 :
  7359. {
  7360. // 둔갑구 처리
  7361. sys_log(0, "USE_POLYMORPH_BALL PID(%d) vnum(%d)", GetPlayerID(), dwVnum);
  7362.  
  7363. // 레벨 제한 체크
  7364. int iPolymorphLevelLimit = MAX(0, 20 - GetLevel() * 3 / 10);
  7365. if (pMob->m_table.bLevel >= GetLevel() + iPolymorphLevelLimit)
  7366. {
  7367. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("나보다 너무 높은 레벨의 몬스터로는 변신 할 수 없습니다."));
  7368. return false;
  7369. }
  7370.  
  7371. int iDuration = GetSkillLevel(POLYMORPH_SKILL_ID) == 0 ? 5 : (5 + (5 + GetSkillLevel(POLYMORPH_SKILL_ID)/40 * 25));
  7372. iDuration *= 60;
  7373.  
  7374. DWORD dwBonus = 0;
  7375.  
  7376. if (true == LC_IsYMIR() || true == LC_IsKorea())
  7377. {
  7378. dwBonus = GetSkillLevel(POLYMORPH_SKILL_ID) + 60;
  7379. }
  7380. else
  7381. {
  7382. dwBonus = (2 + GetSkillLevel(POLYMORPH_SKILL_ID)/40) * 100;
  7383. }
  7384.  
  7385. AddAffect(AFFECT_POLYMORPH, POINT_POLYMORPH, dwVnum, AFF_POLYMORPH, iDuration, 0, true);
  7386. AddAffect(AFFECT_POLYMORPH, POINT_ATT_BONUS, dwBonus, AFF_POLYMORPH, iDuration, 0, false);
  7387.  
  7388. item->SetCount(item->GetCount()-1);
  7389. }
  7390. break;
  7391.  
  7392. case 50322:
  7393. {
  7394. // 보류
  7395.  
  7396. // 둔갑서 처리
  7397. // 소켓0 소켓1 소켓2
  7398. // 둔갑할 몬스터 번호 수련정도 둔갑서 레벨
  7399. sys_log(0, "USE_POLYMORPH_BOOK: %s(%u) vnum(%u)", GetName(), GetPlayerID(), dwVnum);
  7400.  
  7401. if (CPolymorphUtils::instance().PolymorphCharacter(this, item, pMob) == true)
  7402. {
  7403. CPolymorphUtils::instance().UpdateBookPracticeGrade(this, item);
  7404. }
  7405. else
  7406. {
  7407. }
  7408. }
  7409. break;
  7410.  
  7411. default :
  7412. sys_err("POLYMORPH invalid item passed PID(%d) vnum(%d)", GetPlayerID(), item->GetOriginalVnum());
  7413. return false;
  7414. }
  7415.  
  7416. return true;
  7417. }
  7418.  
  7419. bool CHARACTER::CanDoCube() const
  7420. {
  7421. if (m_bIsObserver) return false;
  7422. if (GetShop()) return false;
  7423. if (GetMyShop()) return false;
  7424. if (m_bUnderRefine) return false;
  7425. if (IsWarping()) return false;
  7426.  
  7427. return true;
  7428. }
  7429.  
  7430. bool CHARACTER::UnEquipSpecialRideUniqueItem()
  7431. {
  7432. LPITEM Unique1 = GetWear(WEAR_UNIQUE1);
  7433. LPITEM Unique2 = GetWear(WEAR_UNIQUE2);
  7434.  
  7435. if( NULL != Unique1 )
  7436. {
  7437. if( UNIQUE_GROUP_SPECIAL_RIDE == Unique1->GetSpecialGroup() )
  7438. {
  7439. return UnequipItem(Unique1);
  7440. }
  7441. }
  7442.  
  7443. if( NULL != Unique2 )
  7444. {
  7445. if( UNIQUE_GROUP_SPECIAL_RIDE == Unique2->GetSpecialGroup() )
  7446. {
  7447. return UnequipItem(Unique2);
  7448. }
  7449. }
  7450.  
  7451. return true;
  7452. }
  7453.  
  7454. void CHARACTER::AutoRecoveryItemProcess(const EAffectTypes type)
  7455. {
  7456. if (true == IsDead() || true == IsStun())
  7457. return;
  7458.  
  7459. if (false == IsPC())
  7460. return;
  7461.  
  7462. if (AFFECT_AUTO_HP_RECOVERY != type && AFFECT_AUTO_SP_RECOVERY != type)
  7463. return;
  7464.  
  7465. if (NULL != FindAffect(AFFECT_STUN))
  7466. return;
  7467.  
  7468. {
  7469. const DWORD stunSkills[] = { SKILL_TANHWAN, SKILL_GEOMPUNG, SKILL_BYEURAK, SKILL_GIGUNG };
  7470.  
  7471. for (size_t i=0 ; i < sizeof(stunSkills)/sizeof(DWORD) ; ++i)
  7472. {
  7473. const CAffect* p = FindAffect(stunSkills[i]);
  7474.  
  7475. if (NULL != p && AFF_STUN == p->dwFlag)
  7476. return;
  7477. }
  7478. }
  7479.  
  7480. const CAffect* pAffect = FindAffect(type);
  7481. const size_t idx_of_amount_of_used = 1;
  7482. const size_t idx_of_amount_of_full = 2;
  7483.  
  7484. if (NULL != pAffect)
  7485. {
  7486. LPITEM pItem = FindItemByID(pAffect->dwFlag);
  7487.  
  7488. if (NULL != pItem && true == pItem->GetSocket(0))
  7489. {
  7490. if (false == CArenaManager::instance().IsArenaMap(GetMapIndex()))
  7491. {
  7492. const long amount_of_used = pItem->GetSocket(idx_of_amount_of_used);
  7493. const long amount_of_full = pItem->GetSocket(idx_of_amount_of_full);
  7494.  
  7495. const int32_t avail = amount_of_full - amount_of_used;
  7496.  
  7497. int32_t amount = 0;
  7498.  
  7499. if (AFFECT_AUTO_HP_RECOVERY == type)
  7500. {
  7501. amount = GetMaxHP() - (GetHP() + GetPoint(POINT_HP_RECOVERY));
  7502. }
  7503. else if (AFFECT_AUTO_SP_RECOVERY == type)
  7504. {
  7505. amount = GetMaxSP() - (GetSP() + GetPoint(POINT_SP_RECOVERY));
  7506. }
  7507.  
  7508. if (amount > 0)
  7509. {
  7510. if (avail > amount)
  7511. {
  7512. const int pct_of_used = amount_of_used * 100 / amount_of_full;
  7513. const int pct_of_will_used = (amount_of_used + amount) * 100 / amount_of_full;
  7514.  
  7515. bool bLog = false;
  7516. // 사용량의 10% 단위로 로그를 남김
  7517. // (사용량의 %에서, 십의 자리가 바뀔 때마다 로그를 남김.)
  7518. if ((pct_of_will_used / 10) - (pct_of_used / 10) >= 1)
  7519. bLog = true;
  7520. pItem->SetSocket(idx_of_amount_of_used, amount_of_used + amount, bLog);
  7521. }
  7522. else
  7523. {
  7524. amount = avail;
  7525.  
  7526. ITEM_MANAGER::instance().RemoveItem( pItem );
  7527. }
  7528.  
  7529. if (AFFECT_AUTO_HP_RECOVERY == type)
  7530. {
  7531. PointChange( POINT_HP_RECOVERY, amount );
  7532. EffectPacket( SE_AUTO_HPUP );
  7533. }
  7534. else if (AFFECT_AUTO_SP_RECOVERY == type)
  7535. {
  7536. PointChange( POINT_SP_RECOVERY, amount );
  7537. EffectPacket( SE_AUTO_SPUP );
  7538. }
  7539. }
  7540. }
  7541. else
  7542. {
  7543. pItem->Lock(false);
  7544. pItem->SetSocket(0, false);
  7545. RemoveAffect( const_cast<CAffect*>(pAffect) );
  7546. }
  7547. }
  7548. else
  7549. {
  7550. RemoveAffect( const_cast<CAffect*>(pAffect) );
  7551. }
  7552. }
  7553. }
  7554.  
  7555. bool CHARACTER::IsValidItemPosition(TItemPos Pos) const
  7556. {
  7557. BYTE window_type = Pos.window_type;
  7558. WORD cell = Pos.cell;
  7559.  
  7560. switch (window_type)
  7561. {
  7562. case RESERVED_WINDOW:
  7563. return false;
  7564.  
  7565. case INVENTORY:
  7566. case EQUIPMENT:
  7567. return cell < (INVENTORY_AND_EQUIP_SLOT_MAX);
  7568.  
  7569. case DRAGON_SOUL_INVENTORY:
  7570. return cell < (DRAGON_SOUL_INVENTORY_MAX_NUM);
  7571.  
  7572. case SAFEBOX:
  7573. if (NULL != m_pkSafebox)
  7574. return m_pkSafebox->IsValidPosition(cell);
  7575. else
  7576. return false;
  7577.  
  7578. case MALL:
  7579. if (NULL != m_pkMall)
  7580. return m_pkMall->IsValidPosition(cell);
  7581. else
  7582. return false;
  7583.  
  7584. #ifdef ENABLE_SWITCHBOT
  7585. case SWITCHBOT:
  7586. return cell < SWITCHBOT_SLOT_COUNT;
  7587. #endif
  7588.  
  7589. default:
  7590. return false;
  7591. }
  7592. }
  7593.  
  7594.  
  7595. // 귀찮아서 만든 매크로.. exp가 true면 msg를 출력하고 return false 하는 매크로 (일반적인 verify 용도랑은 return 때문에 약간 반대라 이름때문에 헷갈릴 수도 있겠다..)
  7596. #define VERIFY_MSG(exp, msg) \
  7597. if (true == (exp)) { \
  7598. ChatPacket(CHAT_TYPE_INFO, LC_TEXT(msg)); \
  7599. return false; \
  7600. }
  7601.  
  7602.  
  7603. /// 현재 캐릭터의 상태를 바탕으로 주어진 item을 착용할 수 있는 지 확인하고, 불가능 하다면 캐릭터에게 이유를 알려주는 함수
  7604. bool CHARACTER::CanEquipNow(const LPITEM item, const TItemPos& srcCell, const TItemPos& destCell) /*const*/
  7605. {
  7606. const TItemTable* itemTable = item->GetProto();
  7607. BYTE itemType = item->GetType();
  7608. BYTE itemSubType = item->GetSubType();
  7609.  
  7610. switch (GetJob())
  7611. {
  7612. case JOB_WARRIOR:
  7613. if (item->GetAntiFlag() & ITEM_ANTIFLAG_WARRIOR)
  7614. return false;
  7615. break;
  7616.  
  7617. case JOB_ASSASSIN:
  7618. if (item->GetAntiFlag() & ITEM_ANTIFLAG_ASSASSIN)
  7619. return false;
  7620. break;
  7621.  
  7622. case JOB_SHAMAN:
  7623. if (item->GetAntiFlag() & ITEM_ANTIFLAG_SHAMAN)
  7624. return false;
  7625. break;
  7626.  
  7627. case JOB_SURA:
  7628. if (item->GetAntiFlag() & ITEM_ANTIFLAG_SURA)
  7629. return false;
  7630. break;
  7631. }
  7632.  
  7633. for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i)
  7634. {
  7635. long limit = itemTable->aLimits[i].lValue;
  7636. switch (itemTable->aLimits[i].bType)
  7637. {
  7638. case LIMIT_LEVEL:
  7639. if (GetLevel() < limit)
  7640. {
  7641. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("레벨이 낮아 착용할 수 없습니다."));
  7642. return false;
  7643. }
  7644. break;
  7645.  
  7646. case LIMIT_STR:
  7647. if (GetPoint(POINT_ST) < limit)
  7648. {
  7649. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("근력이 낮아 착용할 수 없습니다."));
  7650. return false;
  7651. }
  7652. break;
  7653.  
  7654. case LIMIT_INT:
  7655. if (GetPoint(POINT_IQ) < limit)
  7656. {
  7657. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("지능이 낮아 착용할 수 없습니다."));
  7658. return false;
  7659. }
  7660. break;
  7661.  
  7662. case LIMIT_DEX:
  7663. if (GetPoint(POINT_DX) < limit)
  7664. {
  7665. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("민첩이 낮아 착용할 수 없습니다."));
  7666. return false;
  7667. }
  7668. break;
  7669.  
  7670. case LIMIT_CON:
  7671. if (GetPoint(POINT_HT) < limit)
  7672. {
  7673. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("체력이 낮아 착용할 수 없습니다."));
  7674. return false;
  7675. }
  7676. break;
  7677. }
  7678. }
  7679.  
  7680. if (item->GetWearFlag() & WEARABLE_UNIQUE)
  7681. {
  7682. if ((GetWear(WEAR_UNIQUE1) && GetWear(WEAR_UNIQUE1)->IsSameSpecialGroup(item)) ||
  7683. (GetWear(WEAR_UNIQUE2) && GetWear(WEAR_UNIQUE2)->IsSameSpecialGroup(item)))
  7684. {
  7685. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("같은 종류의 유니크 아이템 두 개를 동시에 장착할 수 없습니다."));
  7686. return false;
  7687. }
  7688.  
  7689. if (marriage::CManager::instance().IsMarriageUniqueItem(item->GetVnum()) &&
  7690. !marriage::CManager::instance().IsMarried(GetPlayerID()))
  7691. {
  7692. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("결혼하지 않은 상태에서 예물을 착용할 수 없습니다."));
  7693. return false;
  7694. }
  7695.  
  7696. }
  7697.  
  7698. return true;
  7699. }
  7700.  
  7701. /// 현재 캐릭터의 상태를 바탕으로 착용 중인 item을 벗을 수 있는 지 확인하고, 불가능 하다면 캐릭터에게 이유를 알려주는 함수
  7702. bool CHARACTER::CanUnequipNow(const LPITEM item, const TItemPos& srcCell, const TItemPos& destCell) /*const*/
  7703. {
  7704.  
  7705. if (ITEM_BELT == item->GetType())
  7706. VERIFY_MSG(CBeltInventoryHelper::IsExistItemInBeltInventory(this), "벨트 인벤토리에 아이템이 존재하면 해제할 수 없습니다.");
  7707.  
  7708. // 영원히 해제할 수 없는 아이템
  7709. if (IS_SET(item->GetFlag(), ITEM_FLAG_IRREMOVABLE))
  7710. return false;
  7711.  
  7712. // 아이템 unequip시 인벤토리로 옮길 때 빈 자리가 있는 지 확인
  7713. {
  7714. int pos = -1;
  7715.  
  7716. if (item->IsDragonSoul())
  7717. pos = GetEmptyDragonSoulInventory(item);
  7718. else
  7719. pos = GetEmptyInventory(item->GetSize());
  7720.  
  7721. VERIFY_MSG( -1 == pos, "소지품에 빈 공간이 없습니다." );
  7722. }
  7723.  
  7724.  
  7725. return true;
  7726. }
  7727.  
Advertisement
Add Comment
Please, Sign In to add comment