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