1. #include "stdafx.h"
  2. #include "utils.h"
  3. #include "config.h"
  4. #include "char.h"
  5. #include "desc.h"
  6. #include "sectree_manager.h"
  7. #include "packet.h"
  8. #include "protocol.h"
  9. #include "log.h"
  10. #include "skill.h"
  11. #include "unique_item.h"
  12. #include "profiler.h"
  13. #include "marriage.h"
  14. #include "item_addon.h"
  15. #include "dev_log.h"
  16. #include "locale_service.h"
  17. #include "item.h"
  18. #include "item_manager.h"
  19. #include "affect.h"
  20. #include "DragonSoul.h"
  21. #include "buff_on_attributes.h"
  22. #include "belt_inventory_helper.h"
  23. #include "../../common/VnumHelper.h"
  24. CItem::CItem(DWORD dwVnum)
  25. : m_dwVnum(dwVnum), m_bWindow(0), m_dwID(0), m_bEquipped(false), m_dwVID(0), m_wCell(0), m_dwCount(0), m_lFlag(0), m_dwLastOwnerPID(0),
  26. m_bExchanging(false), m_pkDestroyEvent(NULL), m_pkExpireEvent(NULL), m_pkUniqueExpireEvent(NULL), m_pkTimerBasedOnWearExpireEvent(NULL),
  27. m_pkRealTimeExpireEvent(NULL),
  28. m_pkAccessorySocketExpireEvent(NULL), m_pkOwnershipEvent(NULL), m_dwOwnershipPID(0), m_bSkipSave(false), m_isLocked(false),
  29. m_dwMaskVnum(0), m_dwSIGVnum (0)
  30. {
  31. memset( &m_alSockets, 0, sizeof(m_alSockets) );
  32. memset( &m_aAttr, 0, sizeof(m_aAttr) );
  33. }
  34. CItem::~CItem()
  35. {
  36. Destroy();
  37. }
  38. void CItem::Initialize()
  39. {
  40. CEntity::Initialize(ENTITY_ITEM);
  41. m_bWindow = RESERVED_WINDOW;
  42. m_pOwner = NULL;
  43. m_dwID = 0;
  44. m_bEquipped = false;
  45. m_dwVID = m_wCell = m_dwCount = m_lFlag = 0;
  46. m_pProto = NULL;
  47. m_bExchanging = false;
  48. memset(&m_alSockets, 0, sizeof(m_alSockets));
  49. memset(&m_aAttr, 0, sizeof(m_aAttr));
  50. m_pkDestroyEvent = NULL;
  51. m_pkOwnershipEvent = NULL;
  52. m_dwOwnershipPID = 0;
  53. m_pkUniqueExpireEvent = NULL;
  54. m_pkTimerBasedOnWearExpireEvent = NULL;
  55. m_pkRealTimeExpireEvent = NULL;
  56. m_pkAccessorySocketExpireEvent = NULL;
  57. m_bSkipSave = false;
  58. m_dwLastOwnerPID = 0;
  59. }
  60. void CItem::Destroy()
  61. {
  62. event_cancel(&m_pkDestroyEvent);
  63. event_cancel(&m_pkOwnershipEvent);
  64. event_cancel(&m_pkUniqueExpireEvent);
  65. event_cancel(&m_pkTimerBasedOnWearExpireEvent);
  66. event_cancel(&m_pkRealTimeExpireEvent);
  67. event_cancel(&m_pkAccessorySocketExpireEvent);
  68. CEntity::Destroy();
  69. if (GetSectree())
  70. GetSectree()->RemoveEntity(this);
  71. }
  72. EVENTFUNC(item_destroy_event)
  73. {
  74. item_event_info* info = dynamic_cast<item_event_info*>( event->info );
  75. if ( info == NULL )
  76. {
  77. sys_err( "item_destroy_event> <Factor> Null pointer" );
  78. return 0;
  79. }
  80. LPITEM pkItem = info->item;
  81. if (pkItem->GetOwner())
  82. sys_err("item_destroy_event: Owner exist. (item %s owner %s)", pkItem->GetName(), pkItem->GetOwner()->GetName());
  83. pkItem->SetDestroyEvent(NULL);
  84. M2_DESTROY_ITEM(pkItem);
  85. return 0;
  86. }
  87. void CItem::SetDestroyEvent(LPEVENT pkEvent)
  88. {
  89. m_pkDestroyEvent = pkEvent;
  90. }
  91. void CItem::StartDestroyEvent(int iSec)
  92. {
  93. if (m_pkDestroyEvent)
  94. return;
  95. item_event_info* info = AllocEventInfo<item_event_info>();
  96. info->item = this;
  97. SetDestroyEvent(event_create(item_destroy_event, info, PASSES_PER_SEC(iSec)));
  98. }
  99. void CItem::EncodeInsertPacket(LPENTITY ent)
  100. {
  101. LPDESC d;
  102. if (!(d = ent->GetDesc()))
  103. return;
  104. const PIXEL_POSITION & c_pos = GetXYZ();
  105. struct packet_item_ground_add pack;
  106. pack.bHeader = HEADER_GC_ITEM_GROUND_ADD;
  107. pack.x = c_pos.x;
  108. pack.y = c_pos.y;
  109. pack.z = c_pos.z;
  110. pack.dwVnum = GetVnum();
  111. pack.dwVID = m_dwVID;
  112. //pack.count = m_dwCount;
  113. d->Packet(&pack, sizeof(pack));
  114. if (m_pkOwnershipEvent != NULL)
  115. {
  116. item_event_info * info = dynamic_cast<item_event_info *>(m_pkOwnershipEvent->info);
  117. if ( info == NULL )
  118. {
  119. sys_err( "CItem::EncodeInsertPacket> <Factor> Null pointer" );
  120. return;
  121. }
  122. TPacketGCItemOwnership p;
  123. p.bHeader = HEADER_GC_ITEM_OWNERSHIP;
  124. p.dwVID = m_dwVID;
  125. strlcpy(p.szName, info->szOwnerName, sizeof(p.szName));
  126. d->Packet(&p, sizeof(TPacketGCItemOwnership));
  127. }
  128. }
  129. void CItem::EncodeRemovePacket(LPENTITY ent)
  130. {
  131. LPDESC d;
  132. if (!(d = ent->GetDesc()))
  133. return;
  134. struct packet_item_ground_del pack;
  135. pack.bHeader = HEADER_GC_ITEM_GROUND_DEL;
  136. pack.dwVID = m_dwVID;
  137. d->Packet(&pack, sizeof(pack));
  138. sys_log(2, "Item::EncodeRemovePacket %s to %s", GetName(), ((LPCHARACTER) ent)->GetName());
  139. }
  140. void CItem::SetProto(const TItemTable * table)
  141. {
  142. assert(table != NULL);
  143. m_pProto = table;
  144. SetFlag(m_pProto->dwFlags);
  145. }
  146. void CItem::UsePacketEncode(LPCHARACTER ch, LPCHARACTER victim, struct packet_item_use *packet)
  147. {
  148. if (!GetVnum())
  149. return;
  150. packet->header = HEADER_GC_ITEM_USE;
  151. packet->ch_vid = ch->GetVID();
  152. packet->victim_vid = victim->GetVID();
  153. packet->Cell = TItemPos(GetWindow(), m_wCell);
  154. packet->vnum = GetVnum();
  155. }
  156. void CItem::RemoveFlag(long bit)
  157. {
  158. REMOVE_BIT(m_lFlag, bit);
  159. }
  160. void CItem::AddFlag(long bit)
  161. {
  162. SET_BIT(m_lFlag, bit);
  163. }
  164. void CItem::UpdatePacket()
  165. {
  166. if (!m_pOwner || !m_pOwner->GetDesc())
  167. return;
  168. TPacketGCItemUpdate pack;
  169. pack.header = HEADER_GC_ITEM_UPDATE;
  170. pack.Cell = TItemPos(GetWindow(), m_wCell);
  171. pack.count = m_dwCount;
  172. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  173. pack.alSockets[i] = m_alSockets[i];
  174. thecore_memcpy(pack.aAttr, GetAttributes(), sizeof(pack.aAttr));
  175. sys_log(2, "UpdatePacket %s -> %s", GetName(), m_pOwner->GetName());
  176. m_pOwner->GetDesc()->Packet(&pack, sizeof(pack));
  177. }
  178. DWORD CItem::GetCount()
  179. {
  180. if (GetType() == ITEM_ELK) return MIN(m_dwCount, INT_MAX);
  181. else
  182. {
  183. return MIN(m_dwCount, 200);
  184. }
  185. }
  186. bool CItem::SetCount(DWORD count)
  187. {
  188. if (GetType() == ITEM_ELK)
  189. {
  190. m_dwCount = MIN(count, INT_MAX);
  191. }
  192. else
  193. {
  194. m_dwCount = MIN(count, ITEM_MAX_COUNT);
  195. }
  196. if (count == 0 && m_pOwner)
  197. {
  198. if (GetSubType() == USE_ABILITY_UP || GetSubType() == USE_POTION || GetVnum() == 70020)
  199. {
  200. LPCHARACTER pOwner = GetOwner();
  201. WORD wCell = GetCell();
  202. RemoveFromCharacter();
  203. if (!IsDragonSoul())
  204. {
  205. LPITEM pItem = pOwner->FindSpecifyItem(GetVnum());
  206. if (NULL != pItem)
  207. {
  208. pOwner->ChainQuickslotItem(pItem, QUICKSLOT_TYPE_ITEM, wCell);
  209. }
  210. else
  211. {
  212. pOwner->SyncQuickslot(QUICKSLOT_TYPE_ITEM, wCell, 255);
  213. }
  214. }
  215. M2_DESTROY_ITEM(this);
  216. }
  217. else
  218. {
  219. if (!IsDragonSoul())
  220. {
  221. m_pOwner->SyncQuickslot(QUICKSLOT_TYPE_ITEM, m_wCell, 255);
  222. }
  223. M2_DESTROY_ITEM(RemoveFromCharacter());
  224. }
  225. return false;
  226. }
  227. UpdatePacket();
  228. Save();
  229. return true;
  230. }
  231. LPITEM CItem::RemoveFromCharacter()
  232. {
  233. if (!m_pOwner)
  234. {
  235. sys_err("Item::RemoveFromCharacter owner null");
  236. return (this);
  237. }
  238. LPCHARACTER pOwner = m_pOwner;
  239. if (m_bEquipped) // 장착되었는가?
  240. {
  241. Unequip();
  242. //pOwner->UpdatePacket();
  243. SetWindow(RESERVED_WINDOW);
  244. Save();
  245. return (this);
  246. }
  247. else
  248. {
  249. if (GetWindow() != SAFEBOX && GetWindow() != MALL)
  250. {
  251. if (IsDragonSoul())
  252. {
  253. if (m_wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM)
  254. sys_err("CItem::RemoveFromCharacter: pos >= DRAGON_SOUL_INVENTORY_MAX_NUM");
  255. else
  256. pOwner->SetItem(TItemPos(m_bWindow, m_wCell), NULL);
  257. }
  258. else
  259. {
  260. TItemPos cell(INVENTORY, m_wCell);
  261. if (false == cell.IsDefaultInventoryPosition() && false == cell.IsBeltInventoryPosition()) // 아니면 소지품에?
  262. sys_err("CItem::RemoveFromCharacter: Invalid Item Position");
  263. else
  264. {
  265. pOwner->SetItem(cell, NULL);
  266. }
  267. }
  268. }
  269. m_pOwner = NULL;
  270. m_wCell = 0;
  271. SetWindow(RESERVED_WINDOW);
  272. Save();
  273. return (this);
  274. }
  275. }
  276. bool CItem::AddToCharacter(LPCHARACTER ch, TItemPos Cell)
  277. {
  278. assert(GetSectree() == NULL);
  279. assert(m_pOwner == NULL);
  280. WORD pos = Cell.cell;
  281. BYTE window_type = Cell.window_type;
  282. if (INVENTORY == window_type)
  283. {
  284. if (m_wCell >= INVENTORY_MAX_NUM && BELT_INVENTORY_SLOT_START > m_wCell)
  285. {
  286. sys_err("CItem::AddToCharacter: cell overflow: %s to %s cell %d", m_pProto->szName, ch->GetName(), m_wCell);
  287. return false;
  288. }
  289. }
  290. else if (DRAGON_SOUL_INVENTORY == window_type)
  291. {
  292. if (m_wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM)
  293. {
  294. sys_err("CItem::AddToCharacter: cell overflow: %s to %s cell %d", m_pProto->szName, ch->GetName(), m_wCell);
  295. return false;
  296. }
  297. }
  298. bool bWereMine = this->GetLastOwnerPID() == ch->GetPlayerID();
  299. if (ch->GetDesc())
  300. m_dwLastOwnerPID = ch->GetPlayerID();
  301. event_cancel(&m_pkDestroyEvent);
  302. ch->SetItem(TItemPos(window_type, pos), this);
  303. m_pOwner = ch;
  304. Save();
  305. return true;
  306. }
  307. LPITEM CItem::RemoveFromGround()
  308. {
  309. if (GetSectree())
  310. {
  311. SetOwnership(NULL);
  312. GetSectree()->RemoveEntity(this);
  313. ViewCleanup();
  314. Save();
  315. }
  316. return (this);
  317. }
  318. bool CItem::AddToGround(long lMapIndex, const PIXEL_POSITION & pos, bool skipOwnerCheck)
  319. {
  320. if (0 == lMapIndex)
  321. {
  322. sys_err("wrong map index argument: %d", lMapIndex);
  323. return false;
  324. }
  325. if (GetSectree())
  326. {
  327. sys_err("sectree already assigned");
  328. return false;
  329. }
  330. if (!skipOwnerCheck && m_pOwner)
  331. {
  332. sys_err("owner pointer not null");
  333. return false;
  334. }
  335. LPSECTREE tree = SECTREE_MANAGER::instance().Get(lMapIndex, pos.x, pos.y);
  336. if (!tree)
  337. {
  338. sys_err("cannot find sectree by %dx%d", pos.x, pos.y);
  339. return false;
  340. }
  341. //tree->Touch();
  342. SetWindow(GROUND);
  343. SetXYZ(pos.x, pos.y, pos.z);
  344. tree->InsertEntity(this);
  345. UpdateSectree();
  346. Save();
  347. return true;
  348. }
  349. bool CItem::DistanceValid(LPCHARACTER ch)
  350. {
  351. if (!GetSectree())
  352. return false;
  353. int iDist = DISTANCE_APPROX(GetX() - ch->GetX(), GetY() - ch->GetY());
  354. if (iDist > 300)
  355. return false;
  356. return true;
  357. }
  358. bool CItem::CanUsedBy(LPCHARACTER ch)
  359. {
  360. // Anti flag check
  361. switch (ch->GetJob())
  362. {
  363. case JOB_WARRIOR:
  364. if (GetAntiFlag() & ITEM_ANTIFLAG_WARRIOR)
  365. return false;
  366. break;
  367. case JOB_ASSASSIN:
  368. if (GetAntiFlag() & ITEM_ANTIFLAG_ASSASSIN)
  369. return false;
  370. break;
  371. case JOB_SHAMAN:
  372. if (GetAntiFlag() & ITEM_ANTIFLAG_SHAMAN)
  373. return false;
  374. break;
  375. case JOB_SURA:
  376. if (GetAntiFlag() & ITEM_ANTIFLAG_SURA)
  377. return false;
  378. break;
  379. }
  380. return true;
  381. }
  382. int CItem::FindEquipCell(LPCHARACTER ch, int iCandidateCell)
  383. {
  384. // 코스츔 아이템(ITEM_COSTUME)은 WearFlag 없어도 됨. (sub type으로 착용위치 구분. 귀찮게 또 wear flag 줄 필요가 있나..)
  385. // 용혼석(ITEM_DS, ITEM_SPECIAL_DS)도 SUB_TYPE으로 구분. 신규 반지, 벨트는 ITEM_TYPE으로 구분 -_-
  386. if ((0 == GetWearFlag() || ITEM_TOTEM == GetType()) && ITEM_COSTUME != GetType() && ITEM_DS != GetType() && ITEM_SPECIAL_DS != GetType() && ITEM_RING != GetType() && ITEM_BELT != GetType())
  387. return -1;
  388. // 용혼석 슬롯을 WEAR로 처리할 수가 없어서(WEAR는 최대 32개까지 가능한데 용혼석을 추가하면 32가 넘는다.)
  389. // 인벤토리의 특정 위치((INVENTORY_MAX_NUM + WEAR_MAX_NUM)부터 (INVENTORY_MAX_NUM + WEAR_MAX_NUM + DRAGON_SOUL_DECK_MAX_NUM * DS_SLOT_MAX - 1)까지)를
  390. // 용혼석 슬롯으로 정함.
  391. // return 할 때에, INVENTORY_MAX_NUM을 뺀 이유는,
  392. // 본래 WearCell이 INVENTORY_MAX_NUM를 빼고 return 하기 때문.
  393. if (GetType() == ITEM_DS || GetType() == ITEM_SPECIAL_DS)
  394. {
  395. if (iCandidateCell < 0)
  396. {
  397. return WEAR_MAX_NUM + GetSubType();
  398. }
  399. else
  400. {
  401. for (int i = 0; i < DRAGON_SOUL_DECK_MAX_NUM; i++)
  402. {
  403. if (WEAR_MAX_NUM + i * DS_SLOT_MAX + GetSubType() == iCandidateCell)
  404. {
  405. return iCandidateCell;
  406. }
  407. }
  408. return -1;
  409. }
  410. }
  411. else if (GetType() == ITEM_COSTUME)
  412. {
  413. if (GetSubType() == COSTUME_BODY)
  414. return WEAR_COSTUME_BODY;
  415. else if (GetSubType() == COSTUME_HAIR)
  416. return WEAR_COSTUME_HAIR;
  417. else if (GetSubType() == COSTUME_ACCE)
  418. return WEAR_COSTUME_ACCE;
  419. #ifdef __WEAPON_COSTUME_SYSTEM__
  420. else if (GetSubType() == COSTUME_WEAPON)
  421. return WEAR_COSTUME_WEAPON;
  422. #endif
  423. else if (GetSubType() == COSTUME_MOUNT)
  424. return WEAR_COSTUME_MOUNT;
  425. }
  426. else if (GetType() == ITEM_RING)
  427. {
  428. if (ch->GetWear(WEAR_RING1))
  429. return WEAR_RING2;
  430. else
  431. return WEAR_RING1;
  432. }
  433. else if (GetType() == ITEM_BELT)
  434. return WEAR_BELT;
  435. else if (GetWearFlag() & WEARABLE_BODY)
  436. return WEAR_BODY;
  437. else if (GetWearFlag() & WEARABLE_HEAD)
  438. return WEAR_HEAD;
  439. else if (GetWearFlag() & WEARABLE_FOOTS)
  440. return WEAR_FOOTS;
  441. else if (GetWearFlag() & WEARABLE_WRIST)
  442. return WEAR_WRIST;
  443. else if (GetWearFlag() & WEARABLE_WEAPON)
  444. return WEAR_WEAPON;
  445. else if (GetWearFlag() & WEARABLE_SHIELD)
  446. return WEAR_SHIELD;
  447. else if (GetWearFlag() & WEARABLE_NECK)
  448. return WEAR_NECK;
  449. else if (GetWearFlag() & WEARABLE_EAR)
  450. return WEAR_EAR;
  451. else if (GetWearFlag() & WEARABLE_ARROW)
  452. return WEAR_ARROW;
  453. else if (GetWearFlag() & WEARABLE_UNIQUE)
  454. {
  455. if (ch->GetWear(WEAR_UNIQUE1))
  456. return WEAR_UNIQUE2;
  457. else
  458. return WEAR_UNIQUE1;
  459. }
  460. // 수집 퀘스트를 위한 아이템이 박히는곳으로 한번 박히면 절대 –E수 없다.
  461. else if (GetWearFlag() & WEARABLE_ABILITY)
  462. {
  463. if (!ch->GetWear(WEAR_ABILITY1))
  464. {
  465. return WEAR_ABILITY1;
  466. }
  467. else if (!ch->GetWear(WEAR_ABILITY2))
  468. {
  469. return WEAR_ABILITY2;
  470. }
  471. else if (!ch->GetWear(WEAR_ABILITY3))
  472. {
  473. return WEAR_ABILITY3;
  474. }
  475. else if (!ch->GetWear(WEAR_ABILITY4))
  476. {
  477. return WEAR_ABILITY4;
  478. }
  479. else if (!ch->GetWear(WEAR_ABILITY5))
  480. {
  481. return WEAR_ABILITY5;
  482. }
  483. else if (!ch->GetWear(WEAR_ABILITY6))
  484. {
  485. return WEAR_ABILITY6;
  486. }
  487. else if (!ch->GetWear(WEAR_ABILITY7))
  488. {
  489. return WEAR_ABILITY7;
  490. }
  491. else if (!ch->GetWear(WEAR_ABILITY8))
  492. {
  493. return WEAR_ABILITY8;
  494. }
  495. else
  496. {
  497. return -1;
  498. }
  499. }
  500. return -1;
  501. }
  502. void CItem::ModifyPoints(bool bAdd)
  503. {
  504. int accessoryGrade;
  505. // 무기와 갑옷만 소켓을 적용시킨다.
  506. if (false == IsAccessoryForSocket())
  507. {
  508. if (m_pProto->bType == ITEM_WEAPON || m_pProto->bType == ITEM_ARMOR)
  509. {
  510. // 소켓이 속성강화에 사용되는 경우 적용하지 않는다 (ARMOR_WRIST ARMOR_NECK ARMOR_EAR)
  511. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  512. {
  513. DWORD dwVnum;
  514. if ((dwVnum = GetSocket(i)) <= 2)
  515. continue;
  516. TItemTable * p = ITEM_MANAGER::instance().GetTable(dwVnum);
  517. if (!p)
  518. {
  519. sys_err("cannot find table by vnum %u", dwVnum);
  520. continue;
  521. }
  522. if (ITEM_METIN == p->bType)
  523. {
  524. //m_pOwner->ApplyPoint(p->alValues[0], bAdd ? p->alValues[1] : -p->alValues[1]);
  525. for (int i = 0; i < ITEM_APPLY_MAX_NUM; ++i)
  526. {
  527. if (p->aApplies[i].bType == APPLY_NONE)
  528. continue;
  529. if (p->aApplies[i].bType == APPLY_SKILL)
  530. m_pOwner->ApplyPoint(p->aApplies[i].bType, bAdd ? p->aApplies[i].lValue : p->aApplies[i].lValue ^ 0x00800000);
  531. else
  532. m_pOwner->ApplyPoint(p->aApplies[i].bType, bAdd ? p->aApplies[i].lValue : -p->aApplies[i].lValue);
  533. }
  534. }
  535. }
  536. }
  537. accessoryGrade = 0;
  538. }
  539. else
  540. {
  541. accessoryGrade = MIN(GetAccessorySocketGrade(), ITEM_ACCESSORY_SOCKET_MAX_NUM);
  542. }
  543. for (int i = 0; i < ITEM_APPLY_MAX_NUM; ++i)
  544. {
  545. if (m_pProto->aApplies[i].bType == APPLY_NONE)
  546. continue;
  547. long value = m_pProto->aApplies[i].lValue;
  548. if (m_pProto->aApplies[i].bType == APPLY_SKILL)
  549. {
  550. m_pOwner->ApplyPoint(m_pProto->aApplies[i].bType, bAdd ? value : value ^ 0x00800000);
  551. }
  552. else
  553. {
  554. if (0 != accessoryGrade)
  555. value += MAX(accessoryGrade, value * aiAccessorySocketEffectivePct[accessoryGrade] / 100);
  556. m_pOwner->ApplyPoint(m_pProto->aApplies[i].bType, bAdd ? value : -value);
  557. }
  558. }
  559. // 초승달의 반지, 할로윈 사탕, 행복의 반지, 영원한 사랑의 펜던트의 경우
  560. // 기존의 하드 코딩으로 강제로 속성을 부여했지만,
  561. // 그 부분을 제거하고 special item group 테이블에서 속성을 부여하도록 변경하였다.
  562. // 하지만 하드 코딩되어있을 때 생성된 아이템이 남아있을 수도 있어서 특수처리 해놓는다.
  563. // 이 아이템들의 경우, 밑에 ITEM_UNIQUE일 때의 처리로 속성이 부여되기 때문에,
  564. // 아이템에 박혀있는 attribute는 적용하지 않고 넘어간다.
  565. if (true == CItemVnumHelper::IsRamadanMoonRing(GetVnum()) || true == CItemVnumHelper::IsHalloweenCandy(GetVnum())
  566. || true == CItemVnumHelper::IsHappinessRing(GetVnum()) || true == CItemVnumHelper::IsLovePendant(GetVnum()))
  567. {
  568. // Do not anything.
  569. }
  570. else
  571. {
  572. for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; ++i)
  573. {
  574. if (GetAttributeType(i))
  575. {
  576. const TPlayerItemAttribute& ia = GetAttribute(i);
  577. if (ia.bType == APPLY_SKILL)
  578. m_pOwner->ApplyPoint(ia.bType, bAdd ? ia.sValue : ia.sValue ^ 0x00800000);
  579. else
  580. m_pOwner->ApplyPoint(ia.bType, bAdd ? ia.sValue : -ia.sValue);
  581. }
  582. }
  583. }
  584. switch (m_pProto->bType)
  585. {
  586. case ITEM_PICK:
  587. case ITEM_ROD:
  588. {
  589. if (bAdd)
  590. {
  591. if (m_wCell == INVENTORY_MAX_NUM + WEAR_WEAPON)
  592. m_pOwner->SetPart(PART_WEAPON, GetVnum());
  593. }
  594. else
  595. {
  596. if (m_wCell == INVENTORY_MAX_NUM + WEAR_WEAPON)
  597. #ifdef __WEAPON_COSTUME_SYSTEM__
  598. m_pOwner->SetPart(PART_WEAPON, 0);
  599. #else
  600. m_pOwner->SetPart(PART_WEAPON, m_pOwner->GetOriginalPart(PART_WEAPON));
  601. #endif
  602. }
  603. }
  604. break;
  605. case ITEM_WEAPON:
  606. {
  607. #ifdef __WEAPON_COSTUME_SYSTEM__
  608. if (m_pOwner->GetWear(WEAR_COSTUME_WEAPON) != 0)
  609. break;
  610. #endif
  611. if (bAdd)
  612. {
  613. if (m_wCell == INVENTORY_MAX_NUM + WEAR_WEAPON)
  614. m_pOwner->SetPart(PART_WEAPON, GetVnum());
  615. }
  616. else
  617. {
  618. if (m_wCell == INVENTORY_MAX_NUM + WEAR_WEAPON)
  619. #ifdef __WEAPON_COSTUME_SYSTEM__
  620. m_pOwner->SetPart(PART_WEAPON, 0);
  621. #else
  622. m_pOwner->SetPart(PART_WEAPON, m_pOwner->GetOriginalPart(PART_WEAPON));
  623. #endif
  624. }
  625. }
  626. break;
  627. case ITEM_ARMOR:
  628. {
  629. // 코스츔 body를 입고있다면 armor는 벗던 입던 상관 없이 비주얼에 영향을 주면 안 됨.
  630. if (0 != m_pOwner->GetWear(WEAR_COSTUME_BODY))
  631. break;
  632. if (GetSubType() == ARMOR_BODY || GetSubType() == ARMOR_HEAD || GetSubType() == ARMOR_FOOTS || GetSubType() == ARMOR_SHIELD)
  633. {
  634. if (bAdd)
  635. {
  636. if (GetProto()->bSubType == ARMOR_BODY)
  637. m_pOwner->SetPart(PART_MAIN, GetVnum());
  638. }
  639. else
  640. {
  641. if (GetProto()->bSubType == ARMOR_BODY)
  642. m_pOwner->SetPart(PART_MAIN, m_pOwner->GetOriginalPart(PART_MAIN));
  643. }
  644. }
  645. }
  646. break;
  647. // 코스츔 아이템 입었을 때 캐릭터 parts 정보 세팅. 기존 스타일대로 추가함..
  648. case ITEM_COSTUME:
  649. {
  650. DWORD toSetValue = this->GetVnum();
  651. EParts toSetPart = PART_MAX_NUM;
  652. if (GetSubType() == COSTUME_BODY)
  653. {
  654. toSetPart = PART_MAIN;
  655. if (false == bAdd)
  656. {
  657. const CItem* pArmor = m_pOwner->GetWear(WEAR_BODY);
  658. toSetValue = (NULL != pArmor) ? pArmor->GetVnum() : m_pOwner->GetOriginalPart(PART_MAIN);
  659. }
  660. }
  661. #ifdef __WEAPON_COSTUME_SYSTEM__
  662. else if (GetSubType() == COSTUME_WEAPON)
  663. {
  664. toSetPart = PART_WEAPON;
  665. if (!bAdd)
  666. {
  667. const CItem* pWeapon = m_pOwner->GetWear(WEAR_WEAPON);
  668. toSetValue = (NULL != pWeapon) ? pWeapon->GetVnum() : m_pOwner->GetPart(PART_WEAPON);
  669. }
  670. }
  671. #endif
  672. else if (GetSubType() == COSTUME_HAIR)
  673. {
  674. toSetPart = PART_HAIR;
  675. toSetValue = (true == bAdd) ? this->GetValue(3) : 0;
  676. }
  677. else if (GetSubType() == COSTUME_ACCE)
  678. {
  679. if (GetSocket(1) >= 19)
  680. {
  681. toSetValue = (true == bAdd) ? this->GetValue(3) + 100 : 0;
  682. }
  683. else
  684. {
  685. toSetValue = (true == bAdd) ? this->GetValue(3) : 0;
  686. }
  687. toSetPart = PART_ACCE;
  688. }
  689. if (PART_MAX_NUM != toSetPart)
  690. {
  691. m_pOwner->SetPart((BYTE)toSetPart, toSetValue);
  692. m_pOwner->UpdatePacket();
  693. }
  694. }
  695. break;
  696. case ITEM_UNIQUE:
  697. {
  698. if (0 != GetSIGVnum())
  699. {
  700. const CSpecialItemGroup* pItemGroup = ITEM_MANAGER::instance().GetSpecialItemGroup(GetSIGVnum());
  701. if (NULL == pItemGroup)
  702. break;
  703. DWORD dwAttrVnum = pItemGroup->GetAttrVnum(GetVnum());
  704. const CSpecialAttrGroup* pAttrGroup = ITEM_MANAGER::instance().GetSpecialAttrGroup(dwAttrVnum);
  705. if (NULL == pAttrGroup)
  706. break;
  707. for (itertype (pAttrGroup->m_vecAttrs) it = pAttrGroup->m_vecAttrs.begin(); it != pAttrGroup->m_vecAttrs.end(); it++)
  708. {
  709. m_pOwner->ApplyPoint(it->apply_type, bAdd ? it->apply_value : -it->apply_value);
  710. }
  711. }
  712. }
  713. break;
  714. }
  715. }
  716. bool CItem::IsEquipable() const
  717. {
  718. switch (this->GetType())
  719. {
  720. case ITEM_COSTUME:
  721. case ITEM_ARMOR:
  722. case ITEM_WEAPON:
  723. case ITEM_ROD:
  724. case ITEM_PICK:
  725. case ITEM_UNIQUE:
  726. case ITEM_DS:
  727. case ITEM_SPECIAL_DS:
  728. case ITEM_RING:
  729. case ITEM_BELT:
  730. return true;
  731. }
  732. return false;
  733. }
  734. // return false on error state
  735. bool CItem::EquipTo(LPCHARACTER ch, BYTE bWearCell)
  736. {
  737. if (!ch)
  738. {
  739. sys_err("EquipTo: nil character");
  740. return false;
  741. }
  742. // 용혼석 슬롯 index는 WEAR_MAX_NUM 보다 큼.
  743. if (IsDragonSoul())
  744. {
  745. if (bWearCell < WEAR_MAX_NUM || bWearCell >= WEAR_MAX_NUM + DRAGON_SOUL_DECK_MAX_NUM * DS_SLOT_MAX)
  746. {
  747. sys_err("EquipTo: invalid dragon soul cell (this: #%d %s wearflag: %d cell: %d)", GetOriginalVnum(), GetName(), GetSubType(), bWearCell - WEAR_MAX_NUM);
  748. return false;
  749. }
  750. }
  751. else
  752. {
  753. if (bWearCell >= WEAR_MAX_NUM)
  754. {
  755. sys_err("EquipTo: invalid wear cell (this: #%d %s wearflag: %d cell: %d)", GetOriginalVnum(), GetName(), GetWearFlag(), bWearCell);
  756. return false;
  757. }
  758. }
  759. if (ch->GetWear(bWearCell))
  760. {
  761. sys_err("EquipTo: item already exist (this: #%d %s cell: %d %s)", GetOriginalVnum(), GetName(), bWearCell, ch->GetWear(bWearCell)->GetName());
  762. return false;
  763. }
  764. if (GetOwner())
  765. RemoveFromCharacter();
  766. ch->SetWear(bWearCell, this); // 여기서 패킷 나감
  767. m_pOwner = ch;
  768. m_bEquipped = true;
  769. m_wCell = INVENTORY_MAX_NUM + bWearCell;
  770. DWORD dwImmuneFlag = 0;
  771. for (int i = 0; i < WEAR_MAX_NUM; ++i)
  772. if (m_pOwner->GetWear(i))
  773. SET_BIT(dwImmuneFlag, m_pOwner->GetWear(i)->m_pProto->dwImmuneFlag);
  774. m_pOwner->SetImmuneFlag(dwImmuneFlag);
  775. if (IsDragonSoul())
  776. {
  777. DSManager::instance().ActivateDragonSoul(this);
  778. }
  779. else
  780. {
  781. ModifyPoints(true);
  782. StartUniqueExpireEvent();
  783. if (-1 != GetProto()->cLimitTimerBasedOnWearIndex)
  784. StartTimerBasedOnWearExpireEvent();
  785. // ACCESSORY_REFINE
  786. StartAccessorySocketExpireEvent();
  787. // END_OF_ACCESSORY_REFINE
  788. }
  789. ch->BuffOnAttr_AddBuffsFromItem(this);
  790. m_pOwner->ComputeBattlePoints();
  791. m_pOwner->UpdatePacket();
  792. Save();
  793. return (true);
  794. }
  795. bool CItem::Unequip()
  796. {
  797. if (!m_pOwner || GetCell() < INVENTORY_MAX_NUM)
  798. {
  799. // ITEM_OWNER_INVALID_PTR_BUG
  800. sys_err("%s %u m_pOwner %p, GetCell %d",
  801. GetName(), GetID(), get_pointer(m_pOwner), GetCell());
  802. // END_OF_ITEM_OWNER_INVALID_PTR_BUG
  803. return false;
  804. }
  805. if (this != m_pOwner->GetWear(GetCell() - INVENTORY_MAX_NUM))
  806. {
  807. sys_err("m_pOwner->GetWear() != this");
  808. return false;
  809. }
  810. //신규 말 아이템 제거시 처리
  811. if (IsRideItem())
  812. ClearMountAttributeAndAffect();
  813. if (IsDragonSoul())
  814. {
  815. DSManager::instance().DeactivateDragonSoul(this);
  816. }
  817. else
  818. {
  819. ModifyPoints(false);
  820. }
  821. StopUniqueExpireEvent();
  822. if (-1 != GetProto()->cLimitTimerBasedOnWearIndex)
  823. StopTimerBasedOnWearExpireEvent();
  824. // ACCESSORY_REFINE
  825. StopAccessorySocketExpireEvent();
  826. // END_OF_ACCESSORY_REFINE
  827. m_pOwner->BuffOnAttr_RemoveBuffsFromItem(this);
  828. m_pOwner->SetWear(GetCell() - INVENTORY_MAX_NUM, NULL);
  829. DWORD dwImmuneFlag = 0;
  830. for (int i = 0; i < WEAR_MAX_NUM; ++i)
  831. if (m_pOwner->GetWear(i))
  832. SET_BIT(dwImmuneFlag, m_pOwner->GetWear(i)->m_pProto->dwImmuneFlag);
  833. m_pOwner->SetImmuneFlag(dwImmuneFlag);
  834. m_pOwner->ComputeBattlePoints();
  835. m_pOwner->UpdatePacket();
  836. m_pOwner = NULL;
  837. m_wCell = 0;
  838. m_bEquipped = false;
  839. return true;
  840. }
  841. long CItem::GetValue(DWORD idx)
  842. {
  843. assert(idx < ITEM_VALUES_MAX_NUM);
  844. return GetProto()->alValues[idx];
  845. }
  846. void CItem::SetExchanging(bool bOn)
  847. {
  848. m_bExchanging = bOn;
  849. }
  850. void CItem::Save()
  851. {
  852. if (m_bSkipSave)
  853. return;
  854. ITEM_MANAGER::instance().DelayedSave(this);
  855. }
  856. bool CItem::CreateSocket(BYTE bSlot, BYTE bGold)
  857. {
  858. assert(bSlot < ITEM_SOCKET_MAX_NUM);
  859. if (m_alSockets[bSlot] != 0)
  860. {
  861. sys_err("Item::CreateSocket : socket already exist %s %d", GetName(), bSlot);
  862. return false;
  863. }
  864. if (bGold)
  865. m_alSockets[bSlot] = 2;
  866. else
  867. m_alSockets[bSlot] = 1;
  868. UpdatePacket();
  869. Save();
  870. return true;
  871. }
  872. void CItem::SetSockets(const long * c_al)
  873. {
  874. thecore_memcpy(m_alSockets, c_al, sizeof(m_alSockets));
  875. Save();
  876. }
  877. void CItem::SetSocket(int i, long v, bool bLog)
  878. {
  879. assert(i < ITEM_SOCKET_MAX_NUM);
  880. m_alSockets[i] = v;
  881. UpdatePacket();
  882. Save();
  883. if (bLog)
  884. LogManager::instance().ItemLog(i, v, 0, GetID(), "SET_SOCKET", "", "", GetOriginalVnum());
  885. }
  886. int CItem::GetGold()
  887. {
  888. if (IS_SET(GetFlag(), ITEM_FLAG_COUNT_PER_1GOLD))
  889. {
  890. if (GetProto()->dwGold == 0)
  891. return GetCount();
  892. else
  893. return GetCount() / GetProto()->dwGold;
  894. }
  895. else
  896. return GetProto()->dwGold;
  897. }
  898. int CItem::GetShopBuyPrice()
  899. {
  900. return GetProto()->dwShopBuyPrice;
  901. }
  902. bool CItem::IsOwnership(LPCHARACTER ch)
  903. {
  904. if (!m_pkOwnershipEvent)
  905. return true;
  906. return m_dwOwnershipPID == ch->GetPlayerID() ? true : false;
  907. }
  908. EVENTFUNC(ownership_event)
  909. {
  910. item_event_info* info = dynamic_cast<item_event_info*>( event->info );
  911. if ( info == NULL )
  912. {
  913. sys_err( "ownership_event> <Factor> Null pointer" );
  914. return 0;
  915. }
  916. LPITEM pkItem = info->item;
  917. pkItem->SetOwnershipEvent(NULL);
  918. TPacketGCItemOwnership p;
  919. p.bHeader = HEADER_GC_ITEM_OWNERSHIP;
  920. p.dwVID = pkItem->GetVID();
  921. p.szName[0] = '\0';
  922. pkItem->PacketAround(&p, sizeof(p));
  923. return 0;
  924. }
  925. void CItem::SetOwnershipEvent(LPEVENT pkEvent)
  926. {
  927. m_pkOwnershipEvent = pkEvent;
  928. }
  929. void CItem::SetOwnership(LPCHARACTER ch, int iSec)
  930. {
  931. if (!ch)
  932. {
  933. if (m_pkOwnershipEvent)
  934. {
  935. event_cancel(&m_pkOwnershipEvent);
  936. m_dwOwnershipPID = 0;
  937. TPacketGCItemOwnership p;
  938. p.bHeader = HEADER_GC_ITEM_OWNERSHIP;
  939. p.dwVID = m_dwVID;
  940. p.szName[0] = '\0';
  941. PacketAround(&p, sizeof(p));
  942. }
  943. return;
  944. }
  945. if (m_pkOwnershipEvent)
  946. return;
  947. if (true == LC_IsEurope())
  948. {
  949. if (iSec <= 10)
  950. iSec = 30;
  951. }
  952. m_dwOwnershipPID = ch->GetPlayerID();
  953. item_event_info* info = AllocEventInfo<item_event_info>();
  954. strlcpy(info->szOwnerName, ch->GetName(), sizeof(info->szOwnerName));
  955. info->item = this;
  956. SetOwnershipEvent(event_create(ownership_event, info, PASSES_PER_SEC(iSec)));
  957. TPacketGCItemOwnership p;
  958. p.bHeader = HEADER_GC_ITEM_OWNERSHIP;
  959. p.dwVID = m_dwVID;
  960. strlcpy(p.szName, ch->GetName(), sizeof(p.szName));
  961. PacketAround(&p, sizeof(p));
  962. }
  963. int CItem::GetSocketCount()
  964. {
  965. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++)
  966. {
  967. if (GetSocket(i) == 0)
  968. return i;
  969. }
  970. return ITEM_SOCKET_MAX_NUM;
  971. }
  972. bool CItem::AddSocket()
  973. {
  974. int count = GetSocketCount();
  975. if (count == ITEM_SOCKET_MAX_NUM)
  976. return false;
  977. m_alSockets[count] = 1;
  978. return true;
  979. }
  980. void CItem::AlterToSocketItem(int iSocketCount)
  981. {
  982. if (iSocketCount >= ITEM_SOCKET_MAX_NUM)
  983. {
  984. sys_log(0, "Invalid Socket Count %d, set to maximum", ITEM_SOCKET_MAX_NUM);
  985. iSocketCount = ITEM_SOCKET_MAX_NUM;
  986. }
  987. for (int i = 0; i < iSocketCount; ++i)
  988. SetSocket(i, 1);
  989. }
  990. void CItem::AlterToMagicItem()
  991. {
  992. int idx = GetAttributeSetIndex();
  993. if (idx < 0)
  994. return;
  995. // Appeariance Second Third
  996. // Weapon 50 20 5
  997. // Armor 30 10 2
  998. // Acc 20 10 1
  999. int iSecondPct;
  1000. int iThirdPct;
  1001. if (g_iUseLocale)
  1002. {
  1003. switch (GetType())
  1004. {
  1005. case ITEM_WEAPON:
  1006. iSecondPct = 20;
  1007. iThirdPct = 5;
  1008. break;
  1009. case ITEM_ARMOR:
  1010. case ITEM_COSTUME:
  1011. if (GetSubType() == ARMOR_BODY)
  1012. {
  1013. iSecondPct = 10;
  1014. iThirdPct = 2;
  1015. }
  1016. else
  1017. {
  1018. iSecondPct = 10;
  1019. iThirdPct = 1;
  1020. }
  1021. break;
  1022. default:
  1023. return;
  1024. }
  1025. }
  1026. else
  1027. {
  1028. switch (GetType())
  1029. {
  1030. case ITEM_WEAPON:
  1031. iSecondPct = 30;
  1032. iThirdPct = 15;
  1033. break;
  1034. case ITEM_ARMOR:
  1035. case ITEM_COSTUME:
  1036. if (GetSubType() == ARMOR_BODY)
  1037. {
  1038. iSecondPct = 20;
  1039. iThirdPct = 10;
  1040. }
  1041. else
  1042. {
  1043. iSecondPct = 10;
  1044. iThirdPct = 5;
  1045. }
  1046. break;
  1047. default:
  1048. return;
  1049. }
  1050. }
  1051. // 100% 확률로 좋은 속성 하나
  1052. PutAttribute(aiItemMagicAttributePercentHigh);
  1053. if (number(1, 100) <= iSecondPct)
  1054. PutAttribute(aiItemMagicAttributePercentLow);
  1055. if (number(1, 100) <= iThirdPct)
  1056. PutAttribute(aiItemMagicAttributePercentLow);
  1057. }
  1058. DWORD CItem::GetRefineFromVnum()
  1059. {
  1060. return ITEM_MANAGER::instance().GetRefineFromVnum(GetVnum());
  1061. }
  1062. int CItem::GetRefineLevel()
  1063. {
  1064. const char* name = GetBaseName();
  1065. char* p = const_cast<char*>(strrchr(name, '+'));
  1066. if (!p)
  1067. return 0;
  1068. int rtn = 0;
  1069. str_to_number(rtn, p+1);
  1070. const char* locale_name = GetName();
  1071. p = const_cast<char*>(strrchr(locale_name, '+'));
  1072. if (p)
  1073. {
  1074. int locale_rtn = 0;
  1075. str_to_number(locale_rtn, p+1);
  1076. if (locale_rtn != rtn)
  1077. {
  1078. sys_err("refine_level_based_on_NAME(%d) is not equal to refine_level_based_on_LOCALE_NAME(%d).", rtn, locale_rtn);
  1079. }
  1080. }
  1081. return rtn;
  1082. }
  1083. bool CItem::IsPolymorphItem()
  1084. {
  1085. return GetType() == ITEM_POLYMORPH;
  1086. }
  1087. EVENTFUNC(unique_expire_event)
  1088. {
  1089. item_event_info* info = dynamic_cast<item_event_info*>( event->info );
  1090. if ( info == NULL )
  1091. {
  1092. sys_err( "unique_expire_event> <Factor> Null pointer" );
  1093. return 0;
  1094. }
  1095. LPITEM pkItem = info->item;
  1096. if (pkItem->GetValue(2) == 0)
  1097. {
  1098. if (pkItem->GetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME) <= 1)
  1099. {
  1100. sys_log(0, "UNIQUE_ITEM: expire %s %u", pkItem->GetName(), pkItem->GetID());
  1101. pkItem->SetUniqueExpireEvent(NULL);
  1102. ITEM_MANAGER::instance().RemoveItem(pkItem, "UNIQUE_EXPIRE");
  1103. return 0;
  1104. }
  1105. else
  1106. {
  1107. pkItem->SetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME, pkItem->GetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME) - 1);
  1108. return PASSES_PER_SEC(60);
  1109. }
  1110. }
  1111. else
  1112. {
  1113. time_t cur = get_global_time();
  1114. if (pkItem->GetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME) <= cur)
  1115. {
  1116. pkItem->SetUniqueExpireEvent(NULL);
  1117. ITEM_MANAGER::instance().RemoveItem(pkItem, "UNIQUE_EXPIRE");
  1118. return 0;
  1119. }
  1120. else
  1121. {
  1122. // 게임 내에 시간제 아이템들이 빠릿빠릿하게 사라지지 않는 버그가 있어
  1123. // 수정
  1124. // by rtsummit
  1125. if (pkItem->GetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME) - cur < 600)
  1126. return PASSES_PER_SEC(pkItem->GetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME) - cur);
  1127. else
  1128. return PASSES_PER_SEC(600);
  1129. }
  1130. }
  1131. }
  1132. // 시간 후불제
  1133. // timer를 시작할 때에 시간 차감하는 것이 아니라,
  1134. // timer가 발화할 때에 timer가 동작한 시간 만큼 시간 차감을 한다.
  1135. EVENTFUNC(timer_based_on_wear_expire_event)
  1136. {
  1137. item_event_info* info = dynamic_cast<item_event_info*>( event->info );
  1138. if ( info == NULL )
  1139. {
  1140. sys_err( "expire_event <Factor> Null pointer" );
  1141. return 0;
  1142. }
  1143. LPITEM pkItem = info->item;
  1144. int remain_time = pkItem->GetSocket(ITEM_SOCKET_REMAIN_SEC) - processing_time/passes_per_sec;
  1145. if (remain_time <= 0)
  1146. {
  1147. sys_log(0, "ITEM EXPIRED : expired %s %u", pkItem->GetName(), pkItem->GetID());
  1148. pkItem->SetTimerBasedOnWearExpireEvent(NULL);
  1149. pkItem->SetSocket(ITEM_SOCKET_REMAIN_SEC, 0);
  1150. // 일단 timer based on wear 용혼석은 시간 다 되었다고 없애지 않는다.
  1151. if (pkItem->IsDragonSoul())
  1152. {
  1153. DSManager::instance().DeactivateDragonSoul(pkItem);
  1154. }
  1155. else
  1156. {
  1157. ITEM_MANAGER::instance().RemoveItem(pkItem, "TIMER_BASED_ON_WEAR_EXPIRE");
  1158. }
  1159. return 0;
  1160. }
  1161. pkItem->SetSocket(ITEM_SOCKET_REMAIN_SEC, remain_time);
  1162. return PASSES_PER_SEC (MIN (60, remain_time));
  1163. }
  1164. void CItem::SetUniqueExpireEvent(LPEVENT pkEvent)
  1165. {
  1166. m_pkUniqueExpireEvent = pkEvent;
  1167. }
  1168. void CItem::SetTimerBasedOnWearExpireEvent(LPEVENT pkEvent)
  1169. {
  1170. m_pkTimerBasedOnWearExpireEvent = pkEvent;
  1171. }
  1172. EVENTFUNC(real_time_expire_event)
  1173. {
  1174. const item_vid_event_info* info = reinterpret_cast<const item_vid_event_info*>(event->info);
  1175. if (NULL == info)
  1176. return 0;
  1177. const LPITEM item = ITEM_MANAGER::instance().FindByVID( info->item_vid );
  1178. if (NULL == item)
  1179. return 0;
  1180. const time_t current = get_global_time();
  1181. if (current > item->GetSocket(0))
  1182. {
  1183. switch (item->GetVnum())
  1184. {
  1185. if(item->IsNewMountItem())
  1186. {
  1187. if (item->GetSocket(2) != 0)
  1188. item->ClearMountAttributeAndAffect();
  1189. }
  1190. break;
  1191. }
  1192. ITEM_MANAGER::instance().RemoveItem(item, "REAL_TIME_EXPIRE");
  1193. return 0;
  1194. }
  1195. return PASSES_PER_SEC(1);
  1196. }
  1197. void CItem::StartRealTimeExpireEvent()
  1198. {
  1199. if (m_pkRealTimeExpireEvent)
  1200. return;
  1201. for (int i=0 ; i < ITEM_LIMIT_MAX_NUM ; i++)
  1202. {
  1203. if (LIMIT_REAL_TIME == GetProto()->aLimits[i].bType || LIMIT_REAL_TIME_START_FIRST_USE == GetProto()->aLimits[i].bType)
  1204. {
  1205. item_vid_event_info* info = AllocEventInfo<item_vid_event_info>();
  1206. info->item_vid = GetVID();
  1207. m_pkRealTimeExpireEvent = event_create( real_time_expire_event, info, PASSES_PER_SEC(1));
  1208. sys_log(0, "REAL_TIME_EXPIRE: StartRealTimeExpireEvent");
  1209. return;
  1210. }
  1211. }
  1212. }
  1213. bool CItem::IsRealTimeItem()
  1214. {
  1215. if(!GetProto())
  1216. return false;
  1217. for (int i=0 ; i < ITEM_LIMIT_MAX_NUM ; i++)
  1218. {
  1219. if (LIMIT_REAL_TIME == GetProto()->aLimits[i].bType)
  1220. return true;
  1221. }
  1222. return false;
  1223. }
  1224. void CItem::StartUniqueExpireEvent()
  1225. {
  1226. if (GetType() != ITEM_UNIQUE)
  1227. return;
  1228. if (GetSubType() != COSTUME_MOUNT)
  1229. return;
  1230. if (m_pkUniqueExpireEvent)
  1231. return;
  1232. //기간제 아이템일 경우 시간제 아이템은 동작하지 않는다
  1233. if (IsRealTimeItem())
  1234. return;
  1235. // HARD CODING
  1236. if (GetVnum() == UNIQUE_ITEM_HIDE_ALIGNMENT_TITLE)
  1237. m_pOwner->ShowAlignment(false);
  1238. int iSec = GetSocket(ITEM_SOCKET_UNIQUE_SAVE_TIME);
  1239. if (iSec == 0)
  1240. iSec = 60;
  1241. else
  1242. iSec = MIN(iSec, 60);
  1243. SetSocket(ITEM_SOCKET_UNIQUE_SAVE_TIME, 0);
  1244. item_event_info* info = AllocEventInfo<item_event_info>();
  1245. info->item = this;
  1246. SetUniqueExpireEvent(event_create(unique_expire_event, info, PASSES_PER_SEC(iSec)));
  1247. }
  1248. // 시간 후불제
  1249. // timer_based_on_wear_expire_event 설명 참조
  1250. void CItem::StartTimerBasedOnWearExpireEvent()
  1251. {
  1252. if (m_pkTimerBasedOnWearExpireEvent)
  1253. return;
  1254. //기간제 아이템일 경우 시간제 아이템은 동작하지 않는다
  1255. if (IsRealTimeItem())
  1256. return;
  1257. if (-1 == GetProto()->cLimitTimerBasedOnWearIndex)
  1258. return;
  1259. int iSec = GetSocket(0);
  1260. // 남은 시간을 분단위로 끊기 위해...
  1261. if (0 != iSec)
  1262. {
  1263. iSec %= 60;
  1264. if (0 == iSec)
  1265. iSec = 60;
  1266. }
  1267. item_event_info* info = AllocEventInfo<item_event_info>();
  1268. info->item = this;
  1269. SetTimerBasedOnWearExpireEvent(event_create(timer_based_on_wear_expire_event, info, PASSES_PER_SEC(iSec)));
  1270. }
  1271. void CItem::StopUniqueExpireEvent()
  1272. {
  1273. if (!m_pkUniqueExpireEvent)
  1274. return;
  1275. if (GetValue(2) != 0) // 게임시간제 이외의 아이템은 UniqueExpireEvent를 중단할 수 없다.
  1276. return;
  1277. // HARD CODING
  1278. if (GetVnum() == UNIQUE_ITEM_HIDE_ALIGNMENT_TITLE)
  1279. m_pOwner->ShowAlignment(true);
  1280. SetSocket(ITEM_SOCKET_UNIQUE_SAVE_TIME, event_time(m_pkUniqueExpireEvent) / passes_per_sec);
  1281. event_cancel(&m_pkUniqueExpireEvent);
  1282. ITEM_MANAGER::instance().SaveSingleItem(this);
  1283. }
  1284. void CItem::StopTimerBasedOnWearExpireEvent()
  1285. {
  1286. if (!m_pkTimerBasedOnWearExpireEvent)
  1287. return;
  1288. int remain_time = GetSocket(ITEM_SOCKET_REMAIN_SEC) - event_processing_time(m_pkTimerBasedOnWearExpireEvent) / passes_per_sec;
  1289. SetSocket(ITEM_SOCKET_REMAIN_SEC, remain_time);
  1290. event_cancel(&m_pkTimerBasedOnWearExpireEvent);
  1291. ITEM_MANAGER::instance().SaveSingleItem(this);
  1292. }
  1293. void CItem::ApplyAddon(int iAddonType)
  1294. {
  1295. CItemAddonManager::instance().ApplyAddonTo(iAddonType, this);
  1296. }
  1297. int CItem::GetSpecialGroup() const
  1298. {
  1299. return ITEM_MANAGER::instance().GetSpecialGroupFromItem(GetVnum());
  1300. }
  1301. //
  1302. // 악세서리 소켓 처리.
  1303. //
  1304. bool CItem::IsAccessoryForSocket()
  1305. {
  1306. return (m_pProto->bType == ITEM_ARMOR && (m_pProto->bSubType == ARMOR_WRIST || m_pProto->bSubType == ARMOR_NECK || m_pProto->bSubType == ARMOR_EAR)) ||
  1307. (m_pProto->bType == ITEM_BELT); // 2013년 2월 새로 추가된 '벨트' 아이템의 경우 기획팀에서 악세서리 소켓 시스템을 그대로 이용하자고 함.
  1308. }
  1309. void CItem::SetAccessorySocketGrade(int iGrade)
  1310. {
  1311. SetSocket(0, MINMAX(0, iGrade, GetAccessorySocketMaxGrade()));
  1312. int iDownTime = aiAccessorySocketDegradeTime[GetAccessorySocketGrade()];
  1313. //if (test_server)
  1314. // iDownTime /= 60;
  1315. SetAccessorySocketDownGradeTime(iDownTime);
  1316. }
  1317. void CItem::SetAccessorySocketMaxGrade(int iMaxGrade)
  1318. {
  1319. SetSocket(1, MINMAX(0, iMaxGrade, ITEM_ACCESSORY_SOCKET_MAX_NUM));
  1320. }
  1321. void CItem::SetAccessorySocketDownGradeTime(DWORD time)
  1322. {
  1323. SetSocket(2, time);
  1324. if (test_server && GetOwner())
  1325. GetOwner()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s에서 소켓 빠질때까지 남은 시간 %d"), GetName(), time);
  1326. }
  1327. EVENTFUNC(accessory_socket_expire_event)
  1328. {
  1329. item_vid_event_info* info = dynamic_cast<item_vid_event_info*>( event->info );
  1330. if ( info == NULL )
  1331. {
  1332. sys_err( "accessory_socket_expire_event> <Factor> Null pointer" );
  1333. return 0;
  1334. }
  1335. LPITEM item = ITEM_MANAGER::instance().FindByVID(info->item_vid);
  1336. if (item->GetAccessorySocketDownGradeTime() <= 1)
  1337. {
  1338. degrade:
  1339. item->SetAccessorySocketExpireEvent(NULL);
  1340. item->AccessorySocketDegrade();
  1341. return 0;
  1342. }
  1343. else
  1344. {
  1345. int iTime = item->GetAccessorySocketDownGradeTime() - 60;
  1346. if (iTime <= 1)
  1347. goto degrade;
  1348. item->SetAccessorySocketDownGradeTime(iTime);
  1349. if (iTime > 60)
  1350. return PASSES_PER_SEC(60);
  1351. else
  1352. return PASSES_PER_SEC(iTime);
  1353. }
  1354. }
  1355. void CItem::StartAccessorySocketExpireEvent()
  1356. {
  1357. if (!IsAccessoryForSocket())
  1358. return;
  1359. if (m_pkAccessorySocketExpireEvent)
  1360. return;
  1361. if (GetAccessorySocketMaxGrade() == 0)
  1362. return;
  1363. if (GetAccessorySocketGrade() == 0)
  1364. return;
  1365. int iSec = GetAccessorySocketDownGradeTime();
  1366. SetAccessorySocketExpireEvent(NULL);
  1367. if (iSec <= 1)
  1368. iSec = 5;
  1369. else
  1370. iSec = MIN(iSec, 60);
  1371. item_vid_event_info* info = AllocEventInfo<item_vid_event_info>();
  1372. info->item_vid = GetVID();
  1373. SetAccessorySocketExpireEvent(event_create(accessory_socket_expire_event, info, PASSES_PER_SEC(iSec)));
  1374. }
  1375. void CItem::StopAccessorySocketExpireEvent()
  1376. {
  1377. if (!m_pkAccessorySocketExpireEvent)
  1378. return;
  1379. if (!IsAccessoryForSocket())
  1380. return;
  1381. int new_time = GetAccessorySocketDownGradeTime() - (60 - event_time(m_pkAccessorySocketExpireEvent) / passes_per_sec);
  1382. event_cancel(&m_pkAccessorySocketExpireEvent);
  1383. if (new_time <= 1)
  1384. {
  1385. AccessorySocketDegrade();
  1386. }
  1387. else
  1388. {
  1389. SetAccessorySocketDownGradeTime(new_time);
  1390. }
  1391. }
  1392. bool CItem::IsRideItem()
  1393. {
  1394. if (ITEM_UNIQUE == GetType() && UNIQUE_SPECIAL_RIDE == GetSubType())
  1395. return true;
  1396. if (ITEM_UNIQUE == GetType() && UNIQUE_SPECIAL_MOUNT_RIDE == GetSubType())
  1397. return true;
  1398. if (ITEM_COSTUME == GetType() && COSTUME_MOUNT == GetSubType())
  1399. return true;
  1400. return false;
  1401. }
  1402. bool CItem::IsRamadanRing()
  1403. {
  1404. if (GetVnum() == UNIQUE_ITEM_RAMADAN_RING)
  1405. return true;
  1406. return false;
  1407. }
  1408. void CItem::ClearMountAttributeAndAffect()
  1409. {
  1410. LPCHARACTER ch = GetOwner();
  1411. ch->RemoveAffect(AFFECT_MOUNT);
  1412. ch->RemoveAffect(AFFECT_MOUNT_BONUS);
  1413. ch->MountVnum(0);
  1414. ch->PointChange(POINT_ST, 0);
  1415. ch->PointChange(POINT_DX, 0);
  1416. ch->PointChange(POINT_HT, 0);
  1417. ch->PointChange(POINT_IQ, 0);
  1418. }
  1419. // fixme
  1420. // 이거 지금은 안쓴데... 근데 혹시나 싶어서 남겨둠.
  1421. // by rtsummit
  1422. bool CItem::IsNewMountItem()
  1423. {
  1424. switch(GetVnum())
  1425. {
  1426. case 76000: case 76001: case 76002: case 76003:
  1427. case 76004: case 76005: case 76006: case 76007:
  1428. case 76008: case 76009: case 76010: case 76011:
  1429. case 76012: case 76013: case 76014:
  1430. return true;
  1431. }
  1432. return false;
  1433. }
  1434. void CItem::SetAccessorySocketExpireEvent(LPEVENT pkEvent)
  1435. {
  1436. m_pkAccessorySocketExpireEvent = pkEvent;
  1437. }
  1438. void CItem::AccessorySocketDegrade()
  1439. {
  1440. if (GetAccessorySocketGrade() > 0)
  1441. {
  1442. LPCHARACTER ch = GetOwner();
  1443. if (ch)
  1444. {
  1445. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s에 박혀있던 보석이 사라집니다."), GetName());
  1446. }
  1447. ModifyPoints(false);
  1448. SetAccessorySocketGrade(GetAccessorySocketGrade()-1);
  1449. ModifyPoints(true);
  1450. int iDownTime = aiAccessorySocketDegradeTime[GetAccessorySocketGrade()];
  1451. if (test_server)
  1452. iDownTime /= 60;
  1453. SetAccessorySocketDownGradeTime(iDownTime);
  1454. if (iDownTime)
  1455. StartAccessorySocketExpireEvent();
  1456. }
  1457. }
  1458. // ring에 item을 박을 수 있는지 여부를 체크해서 리턴
  1459. static const bool CanPutIntoRing(LPITEM ring, LPITEM item)
  1460. {
  1461. // const DWORD vnum = item->GetVnum();
  1462. return false;
  1463. }
  1464. bool CItem::CanPutInto(LPITEM item)
  1465. {
  1466. if (item->GetType() == ITEM_BELT)
  1467. return this->GetSubType() == USE_PUT_INTO_BELT_SOCKET;
  1468. else if(item->GetType() == ITEM_RING)
  1469. return CanPutIntoRing(item, this);
  1470. else if (item->GetType() != ITEM_ARMOR)
  1471. return false;
  1472. DWORD vnum = item->GetVnum();
  1473. struct JewelAccessoryInfo
  1474. {
  1475. DWORD jewel;
  1476. DWORD wrist;
  1477. DWORD neck;
  1478. DWORD ear;
  1479. };
  1480. const static JewelAccessoryInfo infos[] = {
  1481. { 50634, 14420, 16220, 17220 },
  1482. { 50635, 14500, 16500, 17500 },
  1483. { 50636, 14520, 16520, 17520 },
  1484. { 50637, 14540, 16540, 17540 },
  1485. { 50638, 14560, 16560, 17560 },
  1486. };
  1487. DWORD item_type = (item->GetVnum() / 10) * 10;
  1488. for (unsigned int i = 0; i < sizeof(infos) / sizeof(infos[0]); i++)
  1489. {
  1490. const JewelAccessoryInfo& info = infos[i];
  1491. switch(item->GetSubType())
  1492. {
  1493. case ARMOR_WRIST:
  1494. if (info.wrist == item_type)
  1495. {
  1496. if (info.jewel == GetVnum())
  1497. {
  1498. return true;
  1499. }
  1500. else
  1501. {
  1502. return false;
  1503. }
  1504. }
  1505. break;
  1506. case ARMOR_NECK:
  1507. if (info.neck == item_type)
  1508. {
  1509. if (info.jewel == GetVnum())
  1510. {
  1511. return true;
  1512. }
  1513. else
  1514. {
  1515. return false;
  1516. }
  1517. }
  1518. break;
  1519. case ARMOR_EAR:
  1520. if (info.ear == item_type)
  1521. {
  1522. if (info.jewel == GetVnum())
  1523. {
  1524. return true;
  1525. }
  1526. else
  1527. {
  1528. return false;
  1529. }
  1530. }
  1531. break;
  1532. }
  1533. }
  1534. if (item->GetSubType() == ARMOR_WRIST)
  1535. vnum -= 14000;
  1536. else if (item->GetSubType() == ARMOR_NECK)
  1537. vnum -= 16000;
  1538. else if (item->GetSubType() == ARMOR_EAR)
  1539. vnum -= 17000;
  1540. else
  1541. return false;
  1542. DWORD type = vnum / 20;
  1543. if (type < 0 || type > 11)
  1544. {
  1545. type = (vnum - 170) / 20;
  1546. if (50623 + type != GetVnum())
  1547. return false;
  1548. else
  1549. return true;
  1550. }
  1551. else if (item->GetVnum() >= 16210 && item->GetVnum() <= 16219)
  1552. {
  1553. if (50625 != GetVnum())
  1554. return false;
  1555. else
  1556. return true;
  1557. }
  1558. else if (item->GetVnum() >= 16230 && item->GetVnum() <= 16239)
  1559. {
  1560. if (50626 != GetVnum())
  1561. return false;
  1562. else
  1563. return true;
  1564. }
  1565. return 50623 + type == GetVnum();
  1566. }
  1567. // PC_BANG_ITEM_ADD
  1568. bool CItem::IsPCBangItem()
  1569. {
  1570. for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i)
  1571. {
  1572. if (m_pProto->aLimits[i].bType == LIMIT_PCBANG)
  1573. return true;
  1574. }
  1575. return false;
  1576. }
  1577. // END_PC_BANG_ITEM_ADD
  1578. bool CItem::CheckItemUseLevel(int nLevel)
  1579. {
  1580. for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i)
  1581. {
  1582. if (this->m_pProto->aLimits[i].bType == LIMIT_LEVEL)
  1583. {
  1584. if (this->m_pProto->aLimits[i].lValue > nLevel) return false;
  1585. else return true;
  1586. }
  1587. }
  1588. return true;
  1589. }
  1590. long CItem::FindApplyValue(BYTE bApplyType)
  1591. {
  1592. if (m_pProto == NULL)
  1593. return 0;
  1594. for (int i = 0; i < ITEM_APPLY_MAX_NUM; ++i)
  1595. {
  1596. if (m_pProto->aApplies[i].bType == bApplyType)
  1597. return m_pProto->aApplies[i].lValue;
  1598. }
  1599. return 0;
  1600. }
  1601. void CItem::CopySocketTo(LPITEM pItem)
  1602. {
  1603. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  1604. {
  1605. pItem->m_alSockets[i] = m_alSockets[i];
  1606. }
  1607. }
  1608. int CItem::GetAccessorySocketGrade()
  1609. {
  1610. return MINMAX(0, GetSocket(0), GetAccessorySocketMaxGrade());
  1611. }
  1612. int CItem::GetAccessorySocketMaxGrade()
  1613. {
  1614. return MINMAX(0, GetSocket(1), ITEM_ACCESSORY_SOCKET_MAX_NUM);
  1615. }
  1616. int CItem::GetAccessorySocketDownGradeTime()
  1617. {
  1618. return MINMAX(0, GetSocket(2), aiAccessorySocketDegradeTime[GetAccessorySocketGrade()]);
  1619. }
  1620. void CItem::AttrLog()
  1621. {
  1622. const char * pszIP = NULL;
  1623. if (GetOwner() && GetOwner()->GetDesc())
  1624. pszIP = GetOwner()->GetDesc()->GetHostName();
  1625. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
  1626. {
  1627. if (m_alSockets[i])
  1628. {
  1629. LogManager::instance().ItemLog(i, m_alSockets[i], 0, GetID(), "INFO_SOCKET", "", pszIP ? pszIP : "", GetOriginalVnum());
  1630. }
  1631. }
  1632. for (int i = 0; i<ITEM_ATTRIBUTE_MAX_NUM; ++i)
  1633. {
  1634. int type = m_aAttr[i].bType;
  1635. int value = m_aAttr[i].sValue;
  1636. if (type)
  1637. LogManager::instance().ItemLog(i, type, value, GetID(), "INFO_ATTR", "", pszIP ? pszIP : "", GetOriginalVnum());
  1638. }
  1639. }
  1640. int CItem::GetLevelLimit()
  1641. {
  1642. for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i)
  1643. {
  1644. if (this->m_pProto->aLimits[i].bType == LIMIT_LEVEL)
  1645. {
  1646. return this->m_pProto->aLimits[i].lValue;
  1647. }
  1648. }
  1649. return 0;
  1650. }
  1651. bool CItem::OnAfterCreatedItem()
  1652. {
  1653. // 아이템을 한 번이라도 사용했다면, 그 이후엔 사용 중이지 않아도 시간이 차감되는 방식
  1654. if (-1 != this->GetProto()->cLimitRealTimeFirstUseIndex)
  1655. {
  1656. // Socket1에 아이템의 사용 횟수가 기록되어 있으니, 한 번이라도 사용한 아이템은 타이머를 시작한다.
  1657. if (0 != GetSocket(1))
  1658. {
  1659. StartRealTimeExpireEvent();
  1660. }
  1661. }
  1662. return true;
  1663. }
  1664. #ifdef __AUCTION__
  1665. // 경매장
  1666. // window를 경매장으로 한다.
  1667. bool CItem::MoveToAuction()
  1668. {
  1669. LPCHARACTER owner = GetOwner();
  1670. if (owner == NULL)
  1671. {
  1672. sys_err ("Item those owner is not exist cannot regist in auction");
  1673. return false;
  1674. }
  1675. if (GetWindow() == AUCTION)
  1676. {
  1677. sys_err ("Item is already in auction.");
  1678. }
  1679. SetWindow(AUCTION);
  1680. owner->SetItem(m_bCell, NULL);
  1681. Save();
  1682. ITEM_MANAGER::instance().FlushDelayedSave(this);
  1683. return true;
  1684. }
  1685. void CItem::CopyToRawData (TPlayerItem* new_item)
  1686. {
  1687. if (new_item != NULL)
  1688. return;
  1689. new_item->id = m_dwID;
  1690. new_item->window = m_bWindow;
  1691. new_item->pos = m_bCell;
  1692. new_item->count = m_dwCount;
  1693. new_item->vnum = GetVnum();
  1694. thecore_memcpy (new_item->alSockets, m_alSockets, sizeof (m_alSockets));
  1695. thecore_memcpy (new_item->aAttr, m_aAttr, sizeof (m_aAttr));
  1696. new_item->owner = m_pOwner->GetPlayerID();
  1697. }
  1698. #endif
  1699. bool CItem::IsDragonSoul()
  1700. {
  1701. return GetType() == ITEM_DS;
  1702. }
  1703. int CItem::GiveMoreTime_Per(float fPercent)
  1704. {
  1705. if (IsDragonSoul())
  1706. {
  1707. DWORD duration = DSManager::instance().GetDuration(this);
  1708. unsigned int remain_sec = GetSocket(ITEM_SOCKET_REMAIN_SEC);
  1709. int given_time = fPercent * duration / 100;
  1710. if (remain_sec == duration)
  1711. return false;
  1712. if ((given_time + remain_sec) >= duration)
  1713. {
  1714. SetSocket(ITEM_SOCKET_REMAIN_SEC, duration);
  1715. return duration - remain_sec;
  1716. }
  1717. else
  1718. {
  1719. SetSocket(ITEM_SOCKET_REMAIN_SEC, given_time + remain_sec);
  1720. return given_time;
  1721. }
  1722. }
  1723. // 우선 용혼석에 관해서만 하도록 한다.
  1724. else
  1725. return 0;
  1726. }
  1727. int CItem::GiveMoreTime_Fix(DWORD dwTime)
  1728. {
  1729. if (IsDragonSoul())
  1730. {
  1731. DWORD duration = DSManager::instance().GetDuration(this);
  1732. unsigned int remain_sec = GetSocket(ITEM_SOCKET_REMAIN_SEC);
  1733. if (remain_sec == duration)
  1734. return false;
  1735. if ((dwTime + remain_sec) >= duration)
  1736. {
  1737. SetSocket(ITEM_SOCKET_REMAIN_SEC, duration);
  1738. return duration - remain_sec;
  1739. }
  1740. else
  1741. {
  1742. SetSocket(ITEM_SOCKET_REMAIN_SEC, dwTime + remain_sec);
  1743. return dwTime;
  1744. }
  1745. }
  1746. // 우선 용혼석에 관해서만 하도록 한다.
  1747. else
  1748. return 0;
  1749. }
  1750. int CItem::GetDuration()
  1751. {
  1752. if(!GetProto())
  1753. return -1;
  1754. for (int i=0 ; i < ITEM_LIMIT_MAX_NUM ; i++)
  1755. {
  1756. if (LIMIT_REAL_TIME == GetProto()->aLimits[i].bType)
  1757. return GetProto()->aLimits[i].lValue;
  1758. }
  1759. if (-1 != GetProto()->cLimitTimerBasedOnWearIndex)
  1760. return GetProto()->aLimits[static_cast<unsigned char>(GetProto()->cLimitTimerBasedOnWearIndex)].lValue;
  1761. return -1;
  1762. }
  1763. bool CItem::IsSameSpecialGroup(const LPITEM item) const
  1764. {
  1765. // 서로 VNUM이 같다면 같은 그룹인 것으로 간주
  1766. if (this->GetVnum() == item->GetVnum())
  1767. return true;
  1768. if (GetSpecialGroup() && (item->GetSpecialGroup() == GetSpecialGroup()))
  1769. return true;
  1770. return false;
  1771. }