Advertisement
Guest User

Untitled

a guest
Aug 27th, 2017
197
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 229.90 KB | None | 0 0
  1. #include "stdafx.h"
  2.  
  3. #include "../../common/teen_packet.h"
  4. #include "../../common/VnumHelper.h"
  5.  
  6. #include "char.h"
  7.  
  8. #include "config.h"
  9. #include "utils.h"
  10. #include "crc32.h"
  11. #include "char_manager.h"
  12. #include "desc_client.h"
  13. #include "desc_manager.h"
  14. #include "buffer_manager.h"
  15. #include "item_manager.h"
  16. #include "motion.h"
  17. #include "vector.h"
  18. #include "packet.h"
  19. #include "cmd.h"
  20. #include "fishing.h"
  21. #include "exchange.h"
  22. #include "battle.h"
  23. #include "affect.h"
  24. #include "shop.h"
  25. #include "shop_manager.h"
  26. #include "safebox.h"
  27. #include "regen.h"
  28. #include "pvp.h"
  29. #include "party.h"
  30. #include "start_position.h"
  31. #include "questmanager.h"
  32. #include "log.h"
  33. #include "p2p.h"
  34. #include "guild.h"
  35. #include "guild_manager.h"
  36. #include "dungeon.h"
  37. #include "messenger_manager.h"
  38. #include "unique_item.h"
  39. #include "priv_manager.h"
  40. #include "war_map.h"
  41. #include "xmas_event.h"
  42. #include "banword.h"
  43. #include "target.h"
  44. #include "wedding.h"
  45. #include "mob_manager.h"
  46. #include "mining.h"
  47. #include "monarch.h"
  48. #include "castle.h"
  49. #include "arena.h"
  50. #include "dev_log.h"
  51. #include "horsename_manager.h"
  52. #include "pcbang.h"
  53. #include "gm.h"
  54. #include "map_location.h"
  55. #include "BlueDragon_Binder.h"
  56. #include "skill_power.h"
  57. #include "buff_on_attributes.h"
  58. #include "OXEvent.h"
  59. #ifdef DEATHMATCH_MODE
  60. #include "Deathmatch.h"
  61. #endif
  62. #include "spamblock.h"
  63. #include <boost/algorithm/string.hpp>
  64.  
  65. #ifdef __PET_SYSTEM__
  66. #include "PetSystem.h"
  67. #endif
  68. #ifdef ENABLE_PET_BONUS_SYSTEM
  69. #include "PetBonus.h"
  70. #endif
  71. #include "DragonSoul.h"
  72.  
  73. #ifdef __SEND_TARGET_INFO__
  74. #include <algorithm>
  75. #include <iterator>
  76. using namespace std;
  77. #endif
  78.  
  79. extern const BYTE g_aBuffOnAttrPoints;
  80. extern bool RaceToJob(unsigned race, unsigned *ret_job);
  81.  
  82. extern int g_nPortalLimitTime;
  83. extern int test_server;
  84.  
  85. extern bool IS_SUMMONABLE_ZONE(int map_index); // char_item.cpp
  86. bool CAN_ENTER_ZONE(const LPCHARACTER& ch, int map_index);
  87.  
  88. bool CAN_ENTER_ZONE(const LPCHARACTER& ch, int map_index)
  89. {
  90. switch (map_index)
  91. {
  92. case 301:
  93. case 302:
  94. case 303:
  95. case 304:
  96. if (ch->GetLevel() < 90)
  97. return false;
  98. }
  99. return true;
  100. }
  101.  
  102. // <Factor> DynamicCharacterPtr member function definitions
  103.  
  104. LPCHARACTER DynamicCharacterPtr::Get() const {
  105. LPCHARACTER p = NULL;
  106. if (is_pc) {
  107. p = CHARACTER_MANAGER::instance().FindByPID(id);
  108. } else {
  109. p = CHARACTER_MANAGER::instance().Find(id);
  110. }
  111. return p;
  112. }
  113.  
  114. DynamicCharacterPtr& DynamicCharacterPtr::operator=(LPCHARACTER character) {
  115. if (character == NULL) {
  116. Reset();
  117. return *this;
  118. }
  119. if (character->IsPC()) {
  120. is_pc = true;
  121. id = character->GetPlayerID();
  122. } else {
  123. is_pc = false;
  124. id = character->GetVID();
  125. }
  126. return *this;
  127. }
  128.  
  129. CHARACTER::CHARACTER()
  130. {
  131. m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateIdle, &CHARACTER::EndStateEmpty);
  132. m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateMove, &CHARACTER::EndStateEmpty);
  133. m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateBattle, &CHARACTER::EndStateEmpty);
  134.  
  135. Initialize();
  136. }
  137.  
  138. CHARACTER::~CHARACTER()
  139. {
  140. Destroy();
  141. }
  142.  
  143. void CHARACTER::Initialize()
  144. {
  145. CEntity::Initialize(ENTITY_CHARACTER);
  146.  
  147. m_bNoOpenedShop = true;
  148.  
  149. m_bOpeningSafebox = false;
  150.  
  151. m_fSyncTime = get_float_time()-3;
  152. m_dwPlayerID = 0;
  153. m_dwKillerPID = 0;
  154. #ifdef __SEND_TARGET_INFO__
  155. dwLastTargetInfoPulse = 0;
  156. #endif
  157. m_iLastPMPulse = 0;
  158. m_iPMCounter = 0;
  159. #ifdef OFFLINE_SHOP
  160. m_pkRefreshShopEvent = NULL;
  161. bprivShop = 0;
  162. bprivShopOwner = 0;
  163. bprivShop = 0;
  164. bprivShopOwner = 0;
  165. dw_ShopTime = 0;
  166. m_mapshops.clear();
  167. #endif
  168. #ifdef GIFT_SYSTEM
  169. m_pkGiftRefresh = NULL;
  170. m_dwLastGiftPage = 0;
  171. m_mapGiftGrid.clear();
  172. #endif
  173.  
  174. #ifdef ENABLE_PET_BONUS_SYSTEM
  175. for (int i = 0; i < 4; i++) {
  176. if (i == 3) {
  177. i_BonusTypes[i] = 0;
  178. break;
  179. }
  180.  
  181. i_BonusTypes[i] = 0;
  182. i_ItemTypes[i] = 0;
  183. }
  184. #endif
  185.  
  186. m_iMoveCount = 0;
  187.  
  188. m_pkRegen = NULL;
  189. regen_id_ = 0;
  190. m_posRegen.x = m_posRegen.y = m_posRegen.z = 0;
  191. m_posStart.x = m_posStart.y = 0;
  192. m_posDest.x = m_posDest.y = 0;
  193. m_fRegenAngle = 0.0f;
  194.  
  195. m_pkMobData = NULL;
  196. m_pkMobInst = NULL;
  197.  
  198. m_pkShop = NULL;
  199. m_pkChrShopOwner = NULL;
  200. m_pkMyShop = NULL;
  201. m_pkExchange = NULL;
  202. m_pkParty = NULL;
  203. m_pkPartyRequestEvent = NULL;
  204.  
  205. m_pGuild = NULL;
  206.  
  207. m_pkChrTarget = NULL;
  208.  
  209. m_pkMuyeongEvent = NULL;
  210.  
  211. m_pkWarpNPCEvent = NULL;
  212. m_pkDeadEvent = NULL;
  213. m_pkStunEvent = NULL;
  214. m_pkSaveEvent = NULL;
  215. m_pkRecoveryEvent = NULL;
  216. m_pkTimedEvent = NULL;
  217. m_pkFishingEvent = NULL;
  218. m_pkWarpEvent = NULL;
  219.  
  220. // MINING
  221. m_pkMiningEvent = NULL;
  222. // END_OF_MINING
  223.  
  224. m_pkPoisonEvent = NULL;
  225. m_pkFireEvent = NULL;
  226. m_pkCheckSpeedHackEvent = NULL;
  227. m_speed_hack_count = 0;
  228.  
  229. m_pkAffectEvent = NULL;
  230. m_afAffectFlag = TAffectFlag(0, 0);
  231.  
  232. m_pkDestroyWhenIdleEvent = NULL;
  233.  
  234. m_pkChrSyncOwner = NULL;
  235.  
  236. memset(&m_points, 0, sizeof(m_points));
  237. memset(&m_pointsInstant, 0, sizeof(m_pointsInstant));
  238. memset(&m_quickslot, 0, sizeof(m_quickslot));
  239.  
  240. m_bCharType = CHAR_TYPE_MONSTER;
  241.  
  242. SetPosition(POS_STANDING);
  243.  
  244. m_dwPlayStartTime = m_dwLastMoveTime = get_dword_time();
  245.  
  246. GotoState(m_stateIdle);
  247. m_dwStateDuration = 1;
  248.  
  249. m_dwLastAttackTime = get_dword_time() - 20000;
  250.  
  251. m_bAddChrState = 0;
  252.  
  253. m_pkChrStone = NULL;
  254.  
  255. m_pkSafebox = NULL;
  256. m_iSafeboxSize = -1;
  257. m_iSafeboxLoadTime = 0;
  258.  
  259. m_pkMall = NULL;
  260. m_iMallLoadTime = 0;
  261.  
  262. m_posWarp.x = m_posWarp.y = m_posWarp.z = 0;
  263. m_lWarpMapIndex = 0;
  264.  
  265. m_posExit.x = m_posExit.y = m_posExit.z = 0;
  266. m_lExitMapIndex = 0;
  267.  
  268. m_pSkillLevels = NULL;
  269.  
  270. m_dwMoveStartTime = 0;
  271. m_dwMoveDuration = 0;
  272.  
  273. m_dwFlyTargetID = 0;
  274.  
  275. m_dwNextStatePulse = 0;
  276.  
  277. m_dwLastDeadTime = get_dword_time()-180000;
  278.  
  279. m_bSkipSave = false;
  280.  
  281. m_bItemLoaded = false;
  282.  
  283. m_bHasPoisoned = false;
  284.  
  285. m_pkDungeon = NULL;
  286. m_iEventAttr = 0;
  287.  
  288. m_kAttackLog.dwVID = 0;
  289. m_kAttackLog.dwTime = 0;
  290.  
  291. m_bNowWalking = m_bWalking = false;
  292. ResetChangeAttackPositionTime();
  293.  
  294. m_bDetailLog = false;
  295. m_bMonsterLog = false;
  296.  
  297. m_bDisableCooltime = false;
  298.  
  299. m_iAlignment = 0;
  300. m_iRealAlignment = 0;
  301. #ifdef ENABLE_TITLE_SYSTEM
  302. m_iPrestige = 0;
  303. m_iRealPrestige = 0;
  304. #endif
  305.  
  306. m_iKillerModePulse = 0;
  307. m_bPKMode = PK_MODE_PEACE;
  308.  
  309. m_dwQuestNPCVID = 0;
  310. m_dwQuestByVnum = 0;
  311. m_pQuestItem = NULL;
  312.  
  313. m_szMobileAuth[0] = '\0';
  314.  
  315. m_dwUnderGuildWarInfoMessageTime = get_dword_time()-60000;
  316.  
  317. m_bUnderRefine = false;
  318.  
  319. // REFINE_NPC
  320. m_dwRefineNPCVID = 0;
  321. // END_OF_REFINE_NPC
  322.  
  323. m_dwPolymorphRace = 0;
  324.  
  325. m_bStaminaConsume = false;
  326.  
  327. ResetChainLightningIndex();
  328.  
  329. m_dwMountVnum = 0;
  330. m_chHorse = NULL;
  331. m_chRider = NULL;
  332.  
  333. m_pWarMap = NULL;
  334. m_pWeddingMap = NULL;
  335. m_bChatCounter = 0;
  336.  
  337. ResetStopTime();
  338.  
  339. m_dwLastVictimSetTime = get_dword_time() - 3000;
  340. m_iMaxAggro = -100;
  341.  
  342. m_bSendHorseLevel = 0;
  343. m_bSendHorseHealthGrade = 0;
  344. m_bSendHorseStaminaGrade = 0;
  345.  
  346. m_dwLoginPlayTime = 0;
  347.  
  348. m_pkChrMarried = NULL;
  349.  
  350. m_posSafeboxOpen.x = -1000;
  351. m_posSafeboxOpen.y = -1000;
  352.  
  353. // EQUIP_LAST_SKILL_DELAY
  354. m_dwLastSkillTime = get_dword_time();
  355. // END_OF_EQUIP_LAST_SKILL_DELAY
  356.  
  357. // MOB_SKILL_COOLTIME
  358. memset(m_adwMobSkillCooltime, 0, sizeof(m_adwMobSkillCooltime));
  359. // END_OF_MOB_SKILL_COOLTIME
  360.  
  361. m_isinPCBang = false;
  362.  
  363. // ARENA
  364. m_pArena = NULL;
  365. m_nPotionLimit = quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count");
  366. // END_ARENA
  367.  
  368. //PREVENT_TRADE_WINDOW
  369. m_isOpenSafebox = 0;
  370. //END_PREVENT_TRADE_WINDOW
  371.  
  372. //PREVENT_REFINE_HACK
  373. m_iRefineTime = 0;
  374. //END_PREVENT_REFINE_HACK
  375.  
  376. //RESTRICT_USE_SEED_OR_MOONBOTTLE
  377. m_iSeedTime = 0;
  378. //END_RESTRICT_USE_SEED_OR_MOONBOTTLE
  379. //PREVENT_PORTAL_AFTER_EXCHANGE
  380. m_iExchangeTime = 0;
  381. //END_PREVENT_PORTAL_AFTER_EXCHANGE
  382. //
  383. m_iSafeboxLoadTime = 0;
  384.  
  385. m_iMyShopTime = 0;
  386.  
  387. InitMC();
  388.  
  389. m_deposit_pulse = 0;
  390.  
  391. SET_OVER_TIME(this, OT_NONE);
  392.  
  393. m_strNewName = "";
  394.  
  395. m_known_guild.clear();
  396.  
  397. m_dwLogOffInterval = 0;
  398.  
  399. m_bComboSequence = 0;
  400. m_dwLastComboTime = 0;
  401. m_bComboIndex = 0;
  402. m_iComboHackCount = 0;
  403. m_dwSkipComboAttackByTime = 0;
  404.  
  405. m_dwMountTime = 0;
  406.  
  407. m_dwLastGoldDropTime = 0;
  408.  
  409. m_bIsLoadedAffect = false;
  410. cannot_dead = false;
  411. //costume_visible = true;
  412.  
  413. #ifdef __PET_SYSTEM__
  414. m_petSystem = 0;
  415. m_bIsPet = false;
  416. #endif
  417.  
  418. m_fAttMul = 1.0f;
  419. m_fDamMul = 1.0f;
  420.  
  421. m_pointsInstant.iDragonSoulActiveDeck = -1;
  422.  
  423. memset(&m_tvLastSyncTime, 0, sizeof(m_tvLastSyncTime));
  424. m_iSyncHackCount = 0;
  425. #ifdef __CHANGELOOK_SYSTEM__
  426. m_bChangeLook = false;
  427. #endif
  428. #ifdef __SASH_SYSTEM__
  429. m_bSashCombination = false;
  430. m_bSashAbsorption = false;
  431. #endif
  432. }
  433.  
  434.  
  435. #ifdef ENABLE_PET_BONUS_SYSTEM
  436. void CHARACTER::SendPetBonusUnsummon() {
  437. TPacketGCPetBonusUnsummon ss;
  438. ss.header = HEADER_GC_PET_UNSUMMON;
  439. GetDesc()->Packet(&ss, sizeof(TPacketGCPetBonusUnsummon));
  440. }
  441.  
  442. void CHARACTER::SendPetBonusAdd(std::string m_name, DWORD m_dwVnum) {
  443. TPacketCGPetBonusSend pkSebd;
  444. pkSebd.header = HEADER_CG_PET_BONUS_SEND;
  445. strcpy(pkSebd.name,m_name.c_str());
  446. pkSebd.race = CPetBonus::GetRace(m_dwVnum);
  447. pkSebd.bonus[0] = CPetBonus::GetBonus1(m_dwVnum);
  448. pkSebd.bonus[1] = CPetBonus::GetBonus2(m_dwVnum);
  449. pkSebd.bonus[2] = CPetBonus::GetBonus3(m_dwVnum);
  450. pkSebd.bonus[3] = CPetBonus::GetBonus4(m_dwVnum);
  451. pkSebd.bonus_value[0] = CPetBonus::GetBonusValue1(m_dwVnum);
  452. pkSebd.bonus_value[1] = CPetBonus::GetBonusValue2(m_dwVnum);
  453. pkSebd.bonus_value[2] = CPetBonus::GetBonusValue3(m_dwVnum);
  454. pkSebd.bonus_value[3] = CPetBonus::GetBonusValue4(m_dwVnum);
  455. AddBonusAffect(pkSebd.bonus[0],pkSebd.bonus[1],pkSebd.bonus[2],pkSebd.bonus[3], pkSebd.bonus_value[0],pkSebd.bonus_value[1],pkSebd.bonus_value[2],pkSebd.bonus_value[3]);
  456. GetDesc()->Packet(&pkSebd, sizeof(TPacketCGPetBonusSend));
  457. }
  458. void CHARACTER::AddPetSlot(TPacketGCPetBonusPosRecv* pkg){
  459. if (!m_petSystem)
  460. return;
  461.  
  462. CPetSystem::TPetActorMap::iterator iter = m_petSystem->m_petActorMap.begin();
  463. CPetActor* petActor = iter->second;
  464.  
  465. if (!petActor->IsSummoned())
  466. return;
  467.  
  468. TPacketGCPetAddedSlot pak;
  469. pak.header = HEADER_GC_PET_ADDED_SLOT;
  470. pak.error = 0;
  471.  
  472. for (int i = 0; i < 3; i++) {
  473. BYTE ps = pkg->pos[i];
  474. pak.pos[i] = ps;
  475.  
  476. if (ps == 0)
  477. continue;
  478.  
  479. sys_log(0, "[PET_BONUS] Item Pos %d Recv\n", ps);
  480.  
  481. TItemPos tPos(INVENTORY, ps);
  482. LPITEM pkItem = GetItem(tPos);
  483.  
  484.  
  485. if (!pkItem)
  486. continue;
  487. else if ((pkItem->GetCell() >= INVENTORY_MAX_NUM) || (pkItem->IsEquipped())|| (tPos.IsBeltInventoryPosition()) || (pkItem->IsDragonSoul()))
  488. continue;
  489. else if ((pkItem->GetType() != ITEM_BONUS_PET))
  490. continue;
  491. else if (pkItem->isLocked())
  492. {
  493. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't add locked items."));
  494. continue;
  495. }
  496. #ifdef __SOULBINDING_SYSTEM__
  497. else if ((pkItem->IsBind()) || (pkItem->IsUntilBind()))
  498. {
  499. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't add binded items."));
  500. continue;
  501. }
  502. #endif
  503.  
  504.  
  505.  
  506. else if (pkItem->GetSubType() != CPetBonus::GetRace(petActor->GetVnum()))
  507. {
  508. pak.added[i] = false;
  509. pak.error++; //Not this type (1 = Slot1, 2 = Slot2, 3= Slot3, 4 = Slot4)
  510. continue;
  511. }
  512.  
  513. pak.added[i] = true;
  514.  
  515. // CHANGE_EFFECT_STATUS
  516. for (int i = 0; i < 4; i++) {
  517. int thetype = pkItem->GetAttributeType(i);
  518. if (thetype == 0)
  519. continue;
  520.  
  521. i_ItemTypes[i] = thetype; //Store this for affect remove
  522.  
  523. AddAffect(thetype, 0 ,pkItem->GetAttributeValue(i), AFF_NONE, 999999, 0, true, false); //NOTE: TEST
  524. }
  525. }
  526.  
  527. GetDesc()->Packet(&pak, sizeof(TPacketGCPetAddedSlot));
  528. }
  529.  
  530. void CHARACTER::AddBonusAffect(int b1,int b2,int b3, int b4, int v1, int v2, int v3, int v4) {
  531. if (b1 == 0)
  532. return;
  533.  
  534. AddAffect(b1, 0, v1, AFF_NONE, 99999,0,true,false); // NOTE: TEST
  535. i_BonusTypes[0] = b1;
  536.  
  537. if (b2 == 0)
  538. return;
  539.  
  540. AddAffect(b2, 0, v2, AFF_NONE, 99999,0,true,false); // NOTE: TEST
  541. i_BonusTypes[1] = b2;
  542.  
  543. if (b3 == 0)
  544. return;
  545.  
  546. AddAffect(b3, 0, v3, AFF_NONE, 99999,0,true,false); // NOTE: TEST
  547. i_BonusTypes[2] = b3;
  548.  
  549. if (b4 == 0)
  550. return;
  551.  
  552. AddAffect(b4, 0, v4, AFF_NONE, 99999,0,true,false); // NOTE: TEST
  553. i_BonusTypes[3] = b4;
  554.  
  555. }
  556.  
  557. void CHARACTER::RemoveBonusType() {
  558. for (int i = 0; i < 4; i++) {
  559. if (i_BonusTypes[i] == 0)
  560. break;
  561.  
  562. RemoveAffect(i_BonusTypes[i]);
  563. i_BonusTypes[i] = 0;
  564. }
  565. for (int i = 0; i < 3; i++) {
  566. if (i_ItemTypes[i] == 0)
  567. break;
  568.  
  569. RemoveAffect(i_ItemTypes[i]);
  570. i_ItemTypes[i] = 0;
  571. }
  572. }
  573. #endif
  574.  
  575. void CHARACTER::Create(const char * c_pszName, DWORD vid, bool isPC)
  576. {
  577. static int s_crc = 172814;
  578.  
  579. char crc_string[128+1];
  580. snprintf(crc_string, sizeof(crc_string), "%s%p%d", c_pszName, this, ++s_crc);
  581. m_vid = VID(vid, GetCRC32(crc_string, strlen(crc_string)));
  582.  
  583. if (isPC)
  584. m_stName = c_pszName;
  585. }
  586.  
  587. void CHARACTER::Destroy()
  588. {
  589. CloseMyShop();
  590.  
  591. if (m_pkRegen)
  592. {
  593. if (m_pkDungeon) {
  594. // Dungeon regen may not be valid at this point
  595. if (m_pkDungeon->IsValidRegen(m_pkRegen, regen_id_)) {
  596. --m_pkRegen->count;
  597. }
  598. } else {
  599. // Is this really safe?
  600. --m_pkRegen->count;
  601. }
  602. m_pkRegen = NULL;
  603. }
  604.  
  605. if (m_pkDungeon)
  606. {
  607. SetDungeon(NULL);
  608. }
  609.  
  610. #ifdef GIFT_SYSTEM
  611. event_cancel(&m_pkGiftRefresh);
  612. m_dwLastGiftPage = 0;
  613. m_mapGiftGrid.clear();
  614. #endif
  615. #ifdef OFFLINE_SHOP
  616. event_cancel(&m_pkRefreshShopEvent);
  617. bprivShop = 0;
  618. bprivShopOwner = 0;
  619. dw_ShopTime = 0;
  620. m_mapshops.clear();
  621. #endif
  622.  
  623. #ifdef __PET_SYSTEM__
  624. if (m_petSystem)
  625. {
  626. m_petSystem->Destroy();
  627. delete m_petSystem;
  628.  
  629. m_petSystem = 0;
  630. }
  631. #endif
  632.  
  633. HorseSummon(false);
  634.  
  635. if (GetRider())
  636. GetRider()->ClearHorseInfo();
  637.  
  638. if (GetDesc())
  639. {
  640. GetDesc()->BindCharacter(NULL);
  641. // BindDesc(NULL);
  642. }
  643.  
  644. if (m_pkExchange)
  645. m_pkExchange->Cancel();
  646.  
  647. SetVictim(NULL);
  648.  
  649. if (GetShop())
  650. {
  651. GetShop()->RemoveGuest(this);
  652. SetShop(NULL);
  653. }
  654.  
  655. ClearStone();
  656. ClearSync();
  657. ClearTarget();
  658.  
  659. if (NULL == m_pkMobData)
  660. {
  661. DragonSoul_CleanUp();
  662. ClearItem();
  663. }
  664.  
  665. // <Factor> m_pkParty becomes NULL after CParty destructor call!
  666. LPPARTY party = m_pkParty;
  667. if (party)
  668. {
  669. if (party->GetLeaderPID() == GetVID() && !IsPC())
  670. {
  671. M2_DELETE(party);
  672. }
  673. else
  674. {
  675. party->Unlink(this);
  676.  
  677. if (!IsPC())
  678. party->Quit(GetVID());
  679. }
  680.  
  681. SetParty(NULL); // 안해도 되지만 안전하게.
  682. }
  683.  
  684. if (m_pkMobInst)
  685. {
  686. M2_DELETE(m_pkMobInst);
  687. m_pkMobInst = NULL;
  688. }
  689.  
  690. m_pkMobData = NULL;
  691.  
  692. if (m_pkSafebox)
  693. {
  694. M2_DELETE(m_pkSafebox);
  695. m_pkSafebox = NULL;
  696. }
  697.  
  698. if (m_pkMall)
  699. {
  700. M2_DELETE(m_pkMall);
  701. m_pkMall = NULL;
  702. }
  703.  
  704. m_set_pkChrSpawnedBy.clear();
  705.  
  706. StopMuyeongEvent();
  707. event_cancel(&m_pkWarpNPCEvent);
  708. event_cancel(&m_pkRecoveryEvent);
  709. event_cancel(&m_pkDeadEvent);
  710. event_cancel(&m_pkSaveEvent);
  711. event_cancel(&m_pkTimedEvent);
  712. event_cancel(&m_pkStunEvent);
  713. event_cancel(&m_pkFishingEvent);
  714. event_cancel(&m_pkPoisonEvent);
  715. event_cancel(&m_pkFireEvent);
  716. event_cancel(&m_pkPartyRequestEvent);
  717. //DELAYED_WARP
  718. event_cancel(&m_pkWarpEvent);
  719. event_cancel(&m_pkCheckSpeedHackEvent);
  720. //END_DELAYED_WARP
  721.  
  722. // RECALL_DELAY
  723. //event_cancel(&m_pkRecallEvent);
  724. // END_OF_RECALL_DELAY
  725.  
  726. // MINING
  727. event_cancel(&m_pkMiningEvent);
  728. // END_OF_MINING
  729.  
  730.  
  731. for (itertype(m_mapMobSkillEvent) it = m_mapMobSkillEvent.begin(); it != m_mapMobSkillEvent.end(); ++it)
  732. {
  733. LPEVENT pkEvent = it->second;
  734. event_cancel(&pkEvent);
  735. }
  736. m_mapMobSkillEvent.clear();
  737.  
  738. //event_cancel(&m_pkAffectEvent);
  739. ClearAffect();
  740.  
  741. for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++)
  742. {
  743. if (NULL != it->second)
  744. {
  745. M2_DELETE(it->second);
  746. }
  747. }
  748. m_map_buff_on_attrs.clear();
  749.  
  750. event_cancel(&m_pkDestroyWhenIdleEvent);
  751.  
  752. if (m_pSkillLevels)
  753. {
  754. M2_DELETE_ARRAY(m_pSkillLevels);
  755. m_pSkillLevels = NULL;
  756. }
  757.  
  758. CEntity::Destroy();
  759.  
  760. if (GetSectree())
  761. GetSectree()->RemoveEntity(this);
  762.  
  763. if (m_bMonsterLog)
  764. CHARACTER_MANAGER::instance().UnregisterForMonsterLog(this);
  765. }
  766.  
  767. void CHARACTER::SetLastPMPulse(void)
  768. {
  769. m_iLastPMPulse = thecore_pulse() + 25;
  770. }
  771.  
  772. const char * CHARACTER::GetName() const
  773. {
  774. return m_stName.empty() ? (m_pkMobData ? m_pkMobData->m_table.szLocaleName : "") : m_stName.c_str();
  775. }
  776.  
  777. void CHARACTER::OpenMyShop(const char * c_pszSign, TShopItemTable * pTable, BYTE bItemCount)
  778. {
  779. if (GetPart(PART_MAIN) > 2)
  780. {
  781. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("갑옷을 벗어야 개인 상점을 열 수 있습니다."));
  782. return;
  783. }
  784. #ifdef INVENTORY_PROTECT
  785. if (Inventory_Protect() == 1)
  786. {
  787. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Envanter kilitli pazar ac"));
  788. return;
  789. }
  790. #endif
  791.  
  792. if (GetMyShop()) // 이미 샵이 열려 있으면 닫는다.
  793. {
  794. CloseMyShop();
  795. return;
  796. }
  797.  
  798. // 진행중인 퀘스트가 있으면 상점을 열 수 없다.
  799. quest::PC * pPC = quest::CQuestManager::instance().GetPCForce(GetPlayerID());
  800.  
  801. // GetPCForce는 NULL일 수 없으므로 따로 확인하지 않음
  802. if (pPC->IsRunning())
  803. return;
  804.  
  805. if (bItemCount == 0)
  806. return;
  807.  
  808. LONGLONG llTotalMoney = 0;
  809.  
  810. for (int n = 0; n < bItemCount; ++n)
  811. llTotalMoney += static_cast<LONGLONG>((pTable+n)->price);
  812.  
  813. #ifdef __GOLD_AS_LL__
  814. llTotalMoney += GetGold();
  815. #else
  816. llTotalMoney += static_cast<LONGLONG>(GetGold());
  817. #endif
  818.  
  819. if (g_llMaxGold <= llTotalMoney)
  820. {
  821. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("20¾i ³EA≫ AE°uCI¿ⓒ ≫oA¡A≫ ¿­¼o°¡ ¾ø½A´I´U"));
  822. return;
  823. }
  824.  
  825. char szSign[SHOP_SIGN_MAX_LEN+1];
  826. strlcpy(szSign, c_pszSign, sizeof(szSign));
  827.  
  828. m_stShopSign = szSign;
  829.  
  830. if (m_stShopSign.length() == 0)
  831. return;
  832.  
  833. if (LC_IsCanada() == false)
  834. {
  835. if (CBanwordManager::instance().CheckString(m_stShopSign.c_str(), m_stShopSign.length()))
  836. {
  837. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("비속어나 은어가 포함된 상점 이름으로 상점을 열 수 없습니다."));
  838. return;
  839. }
  840. }
  841.  
  842. // MYSHOP_PRICE_LIST
  843. std::map<DWORD, DWORD> itemkind; // 아이템 종류별 가격, first: vnum, second: 단일 수량 가격
  844. // END_OF_MYSHOP_PRICE_LIST
  845.  
  846. std::set<TItemPos> cont;
  847. for (BYTE i = 0; i < bItemCount; ++i)
  848. {
  849. if (cont.find((pTable + i)->pos) != cont.end())
  850. {
  851. sys_err("MYSHOP: duplicate shop item detected! (name: %s)", GetName());
  852. return;
  853. }
  854.  
  855. // ANTI_GIVE, ANTI_MYSHOP check
  856. LPITEM pkItem = GetItem((pTable + i)->pos);
  857.  
  858. if (pkItem)
  859. {
  860. const TItemTable * item_table = pkItem->GetProto();
  861.  
  862. if (item_table && (IS_SET(item_table->dwAntiFlags, ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_MYSHOP)))
  863. {
  864. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("유료화 아이템은 개인상점에서 판매할 수 없습니다."));
  865. return;
  866. }
  867.  
  868. if (pkItem->IsEquipped() == true)
  869. {
  870. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("장비중인 아이템은 개인상점에서 판매할 수 없습니다."));
  871. return;
  872. }
  873.  
  874. if (true == pkItem->isLocked())
  875. {
  876. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("사용중인 아이템은 개인상점에서 판매할 수 없습니다."));
  877. return;
  878. }
  879.  
  880. // MYSHOP_PRICE_LIST
  881. itemkind[pkItem->GetVnum()] = (pTable + i)->price / pkItem->GetCount();
  882. // END_OF_MYSHOP_PRICE_LIST
  883. }
  884.  
  885. cont.insert((pTable + i)->pos);
  886. }
  887.  
  888. // MYSHOP_PRICE_LIST
  889. // 보따리 개수를 감소시킨다.
  890. if (CountSpecifyItem(71049)) { // 비단 보따리는 없애지 않고 가격정보를 저장한다.
  891.  
  892. //
  893. // 아이템 가격정보를 저장하기 위해 아이템 가격정보 패킷을 만들어 DB 캐시에 보낸다.
  894. //
  895. TPacketMyshopPricelistHeader header;
  896. TItemPriceInfo info;
  897.  
  898. header.dwOwnerID = GetPlayerID();
  899. header.byCount = itemkind.size();
  900.  
  901. TEMP_BUFFER buf;
  902. buf.write(&header, sizeof(header));
  903.  
  904. for (itertype(itemkind) it = itemkind.begin(); it != itemkind.end(); ++it)
  905. {
  906. info.dwVnum = it->first;
  907. info.dwPrice = it->second;
  908.  
  909. buf.write(&info, sizeof(info));
  910. }
  911.  
  912. db_clientdesc->DBPacket(HEADER_GD_MYSHOP_PRICELIST_UPDATE, 0, buf.read_peek(), buf.size());
  913. }
  914. // END_OF_MYSHOP_PRICE_LIST
  915. else if (CountSpecifyItem(50200))
  916. RemoveSpecifyItem(50200, 1);
  917. else
  918. return; // 보따리가 없으면 중단.
  919.  
  920. if (m_pkExchange)
  921. m_pkExchange->Cancel();
  922.  
  923. TPacketGCShopSign p;
  924.  
  925. p.bHeader = HEADER_GC_SHOP_SIGN;
  926. p.dwVID = GetVID();
  927. strlcpy(p.szSign, c_pszSign, sizeof(p.szSign));
  928.  
  929. PacketAround(&p, sizeof(TPacketGCShopSign));
  930.  
  931. m_pkMyShop = CShopManager::instance().CreatePCShop(this, pTable, bItemCount);
  932.  
  933. if (IsPolymorphed() == true)
  934. {
  935. RemoveAffect(AFFECT_POLYMORPH);
  936. }
  937.  
  938. if (GetHorse())
  939. {
  940. HorseSummon( false, true );
  941. }
  942. // new mount 이용 중에, 개인 상점 열면 자동 unmount
  943. // StopRiding으로 뉴마운트까지 처리하면 좋은데 왜 그렇게 안해놨는지 알 수 없다.
  944. else if (GetMountVnum())
  945. {
  946. RemoveAffect(AFFECT_MOUNT);
  947. RemoveAffect(AFFECT_MOUNT_BONUS);
  948. }
  949. //if (!LC_IsNewCIBN())
  950. SetPolymorph(30000, true);
  951.  
  952. }
  953.  
  954. void CHARACTER::CloseMyShop()
  955. {
  956. if (GetMyShop())
  957. {
  958.  
  959. m_stShopSign.clear();
  960. CShopManager::instance().DestroyPCShop(this);
  961. m_pkMyShop = NULL;
  962.  
  963. TPacketGCShopSign p;
  964.  
  965. p.bHeader = HEADER_GC_SHOP_SIGN;
  966. p.dwVID = GetVID();
  967. p.szSign[0] = '\0';
  968.  
  969. PacketAround(&p, sizeof(p));
  970.  
  971. #ifdef OFFLINE_SHOP
  972. if (IsPrivShop())
  973. {
  974. M2_DESTROY_CHARACTER(this);
  975. return;
  976. }
  977. #endif
  978. SetPolymorph(GetJob(), true);
  979. }
  980. }
  981.  
  982. void EncodeMovePacket(TPacketGCMove & pack, DWORD dwVID, BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime, BYTE bRot)
  983. {
  984. pack.bHeader = HEADER_GC_MOVE;
  985. pack.bFunc = bFunc;
  986. pack.bArg = bArg;
  987. pack.dwVID = dwVID;
  988. pack.dwTime = dwTime ? dwTime : get_dword_time();
  989. pack.bRot = bRot;
  990. pack.lX = x;
  991. pack.lY = y;
  992. pack.dwDuration = dwDuration;
  993. }
  994.  
  995. void CHARACTER::RestartAtSamePos()
  996. {
  997. if (m_bIsObserver)
  998. return;
  999.  
  1000. EncodeRemovePacket(this);
  1001. EncodeInsertPacket(this);
  1002.  
  1003. ENTITY_MAP::iterator it = m_map_view.begin();
  1004.  
  1005. while (it != m_map_view.end())
  1006. {
  1007. LPENTITY entity = (it++)->first;
  1008.  
  1009. EncodeRemovePacket(entity);
  1010. if (!m_bIsObserver)
  1011. EncodeInsertPacket(entity);
  1012.  
  1013. if( entity->IsType(ENTITY_CHARACTER) )
  1014. {
  1015. LPCHARACTER lpChar = (LPCHARACTER)entity;
  1016. if( lpChar->IsPC() || lpChar->IsNPC() || lpChar->IsMonster() )
  1017. {
  1018. if (!entity->IsObserverMode())
  1019. entity->EncodeInsertPacket(this);
  1020. }
  1021. }
  1022. else
  1023. {
  1024. if( !entity->IsObserverMode())
  1025. {
  1026. entity->EncodeInsertPacket(this);
  1027. }
  1028. }
  1029. }
  1030. }
  1031.  
  1032.  
  1033. // Entity에 내가 나타났다고 패킷을 보낸다.
  1034. void CHARACTER::EncodeInsertPacket(LPENTITY entity)
  1035. {
  1036.  
  1037. LPDESC d;
  1038.  
  1039. if (!(d = entity->GetDesc()))
  1040. return;
  1041.  
  1042. // 길드이름 버그 수정 코드
  1043. LPCHARACTER ch = (LPCHARACTER) entity;
  1044. ch->SendGuildName(GetGuild());
  1045. // 길드이름 버그 수정 코드
  1046.  
  1047. TPacketGCCharacterAdd pack;
  1048.  
  1049. pack.header = HEADER_GC_CHARACTER_ADD;
  1050. pack.dwVID = m_vid;
  1051. pack.bType = GetCharType();
  1052. pack.angle = GetRotation();
  1053. pack.x = GetX();
  1054. pack.y = GetY();
  1055. pack.z = GetZ();
  1056. pack.wRaceNum = GetRaceNum();
  1057. if (IsPet())
  1058. {
  1059. pack.bMovingSpeed = 150;
  1060. }
  1061. else
  1062. {
  1063. pack.bMovingSpeed = GetLimitPoint(POINT_MOV_SPEED);
  1064. }
  1065. pack.bAttackSpeed = GetLimitPoint(POINT_ATT_SPEED);
  1066. pack.dwAffectFlag[0] = m_afAffectFlag.bits[0];
  1067. pack.dwAffectFlag[1] = m_afAffectFlag.bits[1];
  1068.  
  1069. pack.bStateFlag = m_bAddChrState;
  1070.  
  1071. int iDur = 0;
  1072.  
  1073. if (m_posDest.x != pack.x || m_posDest.y != pack.y)
  1074. {
  1075. iDur = (m_dwMoveStartTime + m_dwMoveDuration) - get_dword_time();
  1076.  
  1077. if (iDur <= 0)
  1078. {
  1079. pack.x = m_posDest.x;
  1080. pack.y = m_posDest.y;
  1081. }
  1082. }
  1083.  
  1084. d->Packet(&pack, sizeof(pack));
  1085.  
  1086. if (IsPC() == true || m_bCharType == CHAR_TYPE_NPC)
  1087. {
  1088. TPacketGCCharacterAdditionalInfo addPacket;
  1089. memset(&addPacket, 0, sizeof(TPacketGCCharacterAdditionalInfo));
  1090.  
  1091. addPacket.header = HEADER_GC_CHAR_ADDITIONAL_INFO;
  1092. addPacket.dwVID = m_vid;
  1093.  
  1094. addPacket.awPart[CHR_EQUIPPART_ARMOR] = GetPart(PART_MAIN);
  1095. addPacket.awPart[CHR_EQUIPPART_WEAPON] = GetPart(PART_WEAPON);
  1096. addPacket.awPart[CHR_EQUIPPART_HEAD] = GetPart(PART_HEAD);
  1097. addPacket.awPart[CHR_EQUIPPART_HAIR] = GetPart(PART_HAIR);
  1098. #ifdef __SASH_SYSTEM__
  1099. addPacket.awPart[CHR_EQUIPPART_SASH] = GetPart(PART_SASH);
  1100. #endif
  1101.  
  1102. addPacket.bPKMode = m_bPKMode;
  1103. addPacket.dwMountVnum = GetMountVnum();
  1104. addPacket.bEmpire = m_bEmpire;
  1105.  
  1106. if (IsPC() == true && (LC_IsEurope() == true || LC_IsCanada() == true || LC_IsSingapore() == true))
  1107. {
  1108. addPacket.dwLevel = GetLevel();
  1109. }
  1110. else
  1111. {
  1112. #ifdef __PET_LEVEL__
  1113. if (IsPet())
  1114. {
  1115. addPacket.dwLevel = GetLevel();
  1116. }
  1117. else
  1118. {
  1119. addPacket.dwLevel = 0;
  1120. }
  1121. #else
  1122. addPacket.dwLevel = 0;
  1123. #endif
  1124. }
  1125.  
  1126. if (false)
  1127. {
  1128. LPCHARACTER ch = (LPCHARACTER) entity;
  1129.  
  1130. if (GetEmpire() == ch->GetEmpire() || ch->GetGMLevel() > GM_PLAYER || m_bCharType == CHAR_TYPE_NPC)
  1131. {
  1132. goto show_all_info;
  1133. }
  1134. else
  1135. {
  1136. memset(addPacket.name, 0, CHARACTER_NAME_MAX_LEN);
  1137. addPacket.dwGuildID = 0;
  1138. addPacket.sAlignment = 0;
  1139. #ifdef ENABLE_TITLE_SYSTEM
  1140. addPacket.sPrestige = 0;
  1141. #endif
  1142. }
  1143. }
  1144. else
  1145. {
  1146. show_all_info:
  1147. #if defined(SHOP_HIDE_NAME) && defined(OFFLINE_SHOP)
  1148. if (IsPC() || GetRaceNum() != 30000 && !IsPrivShop())
  1149. #endif
  1150. strlcpy(addPacket.name, GetName(), sizeof(addPacket.name));
  1151.  
  1152. if (GetGuild() != NULL)
  1153. {
  1154. addPacket.dwGuildID = GetGuild()->GetID();
  1155. }
  1156. else
  1157. {
  1158. addPacket.dwGuildID = 0;
  1159. }
  1160.  
  1161. addPacket.sAlignment = m_iAlignment / 10;
  1162. #ifdef ENABLE_TITLE_SYSTEM
  1163. addPacket.sPrestige = m_iPrestige;
  1164. #endif
  1165. }
  1166.  
  1167. d->Packet(&addPacket, sizeof(TPacketGCCharacterAdditionalInfo));
  1168. }
  1169.  
  1170. if (iDur)
  1171. {
  1172. TPacketGCMove pack;
  1173. EncodeMovePacket(pack, GetVID(), FUNC_MOVE, 0, m_posDest.x, m_posDest.y, iDur, 0, (BYTE) (GetRotation() / 5));
  1174. d->Packet(&pack, sizeof(pack));
  1175.  
  1176. TPacketGCWalkMode p;
  1177. p.vid = GetVID();
  1178. p.header = HEADER_GC_WALK_MODE;
  1179. p.mode = m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN;
  1180.  
  1181. d->Packet(&p, sizeof(p));
  1182. }
  1183.  
  1184. if (entity->IsType(ENTITY_CHARACTER) && GetDesc())
  1185. {
  1186. LPCHARACTER ch = (LPCHARACTER) entity;
  1187. if (ch->IsWalking())
  1188. {
  1189. TPacketGCWalkMode p;
  1190. p.vid = ch->GetVID();
  1191. p.header = HEADER_GC_WALK_MODE;
  1192. p.mode = ch->m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN;
  1193. GetDesc()->Packet(&p, sizeof(p));
  1194. }
  1195. }
  1196.  
  1197. if (GetMyShop())
  1198. {
  1199. TPacketGCShopSign p;
  1200.  
  1201. p.bHeader = HEADER_GC_SHOP_SIGN;
  1202. p.dwVID = GetVID();
  1203. strlcpy(p.szSign, m_stShopSign.c_str(), sizeof(p.szSign));
  1204.  
  1205. d->Packet(&p, sizeof(TPacketGCShopSign));
  1206. }
  1207.  
  1208. if (entity->IsType(ENTITY_CHARACTER))
  1209. {
  1210. sys_log(3, "EntityInsert %s (RaceNum %d) (%d %d) TO %s",
  1211. GetName(), GetRaceNum(), GetX() / SECTREE_SIZE, GetY() / SECTREE_SIZE, ((LPCHARACTER)entity)->GetName());
  1212. }
  1213. }
  1214.  
  1215. void CHARACTER::EncodeRemovePacket(LPENTITY entity)
  1216. {
  1217. if (entity->GetType() != ENTITY_CHARACTER)
  1218. return;
  1219.  
  1220. LPDESC d;
  1221.  
  1222. if (!(d = entity->GetDesc()))
  1223. return;
  1224.  
  1225. TPacketGCCharacterDelete pack;
  1226.  
  1227. pack.header = HEADER_GC_CHARACTER_DEL;
  1228. pack.id = m_vid;
  1229.  
  1230. d->Packet(&pack, sizeof(TPacketGCCharacterDelete));
  1231.  
  1232. if (entity->IsType(ENTITY_CHARACTER))
  1233. sys_log(3, "EntityRemove %s(%d) FROM %s", GetName(), (DWORD) m_vid, ((LPCHARACTER) entity)->GetName());
  1234. }
  1235.  
  1236. void CHARACTER::UpdatePacket()
  1237. {
  1238. if (GetSectree() == NULL) return;
  1239.  
  1240. TPacketGCCharacterUpdate pack;
  1241. TPacketGCCharacterUpdate pack2;
  1242.  
  1243. pack.header = HEADER_GC_CHARACTER_UPDATE;
  1244. pack.dwVID = m_vid;
  1245.  
  1246. pack.awPart[CHR_EQUIPPART_ARMOR] = GetPart(PART_MAIN);
  1247. pack.awPart[CHR_EQUIPPART_WEAPON] = GetPart(PART_WEAPON);
  1248. pack.awPart[CHR_EQUIPPART_HEAD] = GetPart(PART_HEAD);
  1249. pack.awPart[CHR_EQUIPPART_HAIR] = GetPart(PART_HAIR);
  1250. #ifdef __SASH_SYSTEM__
  1251. pack.awPart[CHR_EQUIPPART_SASH] = GetPart(PART_SASH);
  1252. #endif
  1253.  
  1254. pack.bMovingSpeed = GetLimitPoint(POINT_MOV_SPEED);
  1255. pack.bAttackSpeed = GetLimitPoint(POINT_ATT_SPEED);
  1256. pack.bStateFlag = m_bAddChrState;
  1257. pack.dwAffectFlag[0] = m_afAffectFlag.bits[0];
  1258. pack.dwAffectFlag[1] = m_afAffectFlag.bits[1];
  1259. pack.dwGuildID = 0;
  1260. pack.sAlignment = m_iAlignment / 10;
  1261. #ifdef ENABLE_TITLE_SYSTEM
  1262. pack.sPrestige = m_iPrestige;
  1263. #endif
  1264. pack.bPKMode = m_bPKMode;
  1265. #ifdef DEATHMATCH_MODE
  1266. if (GetMapIndex() == DEATMATCH_MAP_INDEX){
  1267. if (IsGM())
  1268. m_bPKMode = PK_MODE_PROTECT;
  1269. else
  1270. m_bPKMode = PK_MODE_FREE;
  1271. }
  1272. #endif
  1273.  
  1274. if (GetGuild())
  1275. pack.dwGuildID = GetGuild()->GetID();
  1276.  
  1277. pack.dwMountVnum = GetMountVnum();
  1278.  
  1279. pack2 = pack;
  1280. pack2.dwGuildID = 0;
  1281. pack2.sAlignment = 0;
  1282. #ifdef ENABLE_TITLE_SYSTEM
  1283. pack2.sPrestige = 0;
  1284. #endif
  1285.  
  1286. if (false)
  1287. {
  1288. if (m_bIsObserver != true)
  1289. {
  1290. for (ENTITY_MAP::iterator iter = m_map_view.begin(); iter != m_map_view.end(); iter++)
  1291. {
  1292. LPENTITY pEntity = iter->first;
  1293.  
  1294. if (pEntity != NULL)
  1295. {
  1296. if (pEntity->IsType(ENTITY_CHARACTER) == true)
  1297. {
  1298. if (pEntity->GetDesc() != NULL)
  1299. {
  1300. LPCHARACTER pChar = (LPCHARACTER)pEntity;
  1301.  
  1302. if (GetEmpire() == pChar->GetEmpire() || pChar->GetGMLevel() > GM_PLAYER)
  1303. {
  1304. pEntity->GetDesc()->Packet(&pack, sizeof(pack));
  1305. }
  1306. else
  1307. {
  1308. pEntity->GetDesc()->Packet(&pack2, sizeof(pack2));
  1309. }
  1310. }
  1311. }
  1312. else
  1313. {
  1314. if (pEntity->GetDesc() != NULL)
  1315. {
  1316. pEntity->GetDesc()->Packet(&pack, sizeof(pack));
  1317. }
  1318. }
  1319. }
  1320. }
  1321. }
  1322.  
  1323. if (GetDesc() != NULL)
  1324. {
  1325. GetDesc()->Packet(&pack, sizeof(pack));
  1326. }
  1327. }
  1328. else
  1329. {
  1330. PacketAround(&pack, sizeof(pack));
  1331. }
  1332. }
  1333.  
  1334. LPCHARACTER CHARACTER::FindCharacterInView(const char * c_pszName, bool bFindPCOnly)
  1335. {
  1336. ENTITY_MAP::iterator it = m_map_view.begin();
  1337.  
  1338. for (; it != m_map_view.end(); ++it)
  1339. {
  1340. if (!it->first->IsType(ENTITY_CHARACTER))
  1341. continue;
  1342.  
  1343. LPCHARACTER tch = (LPCHARACTER) it->first;
  1344.  
  1345. if (bFindPCOnly && tch->IsNPC())
  1346. continue;
  1347.  
  1348. if (!strcasecmp(tch->GetName(), c_pszName))
  1349. return (tch);
  1350. }
  1351.  
  1352. return NULL;
  1353. }
  1354.  
  1355. void CHARACTER::SetPosition(int pos)
  1356. {
  1357. if (pos == POS_STANDING)
  1358. {
  1359. REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_DEAD);
  1360. REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);
  1361.  
  1362. event_cancel(&m_pkDeadEvent);
  1363. event_cancel(&m_pkStunEvent);
  1364. }
  1365. else if (pos == POS_DEAD)
  1366. SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_DEAD);
  1367.  
  1368. if (!IsStone())
  1369. {
  1370. switch (pos)
  1371. {
  1372. case POS_FIGHTING:
  1373. if (!IsState(m_stateBattle))
  1374. MonsterLog("[BATTLE] 싸우는 상태");
  1375.  
  1376. GotoState(m_stateBattle);
  1377. break;
  1378.  
  1379. default:
  1380. if (!IsState(m_stateIdle))
  1381. MonsterLog("[IDLE] 쉬는 상태");
  1382.  
  1383. GotoState(m_stateIdle);
  1384. break;
  1385. }
  1386. }
  1387.  
  1388. m_pointsInstant.position = pos;
  1389. }
  1390.  
  1391. void CHARACTER::Save()
  1392. {
  1393. if (!m_bSkipSave)
  1394. CHARACTER_MANAGER::instance().DelayedSave(this);
  1395. }
  1396.  
  1397. void CHARACTER::CreatePlayerProto(TPlayerTable & tab)
  1398. {
  1399. memset(&tab, 0, sizeof(TPlayerTable));
  1400.  
  1401. if (GetNewName().empty())
  1402. {
  1403. strlcpy(tab.name, GetName(), sizeof(tab.name));
  1404. }
  1405. else
  1406. {
  1407. strlcpy(tab.name, GetNewName().c_str(), sizeof(tab.name));
  1408. }
  1409.  
  1410. strlcpy(tab.ip, GetDesc()->GetHostName(), sizeof(tab.ip));
  1411.  
  1412. tab.id = m_dwPlayerID;
  1413. tab.voice = GetPoint(POINT_VOICE);
  1414. tab.level = GetLevel();
  1415. tab.level_step = GetPoint(POINT_LEVEL_STEP);
  1416. tab.exp = GetExp();
  1417. tab.gold = GetGold();
  1418. tab.job = m_points.job;
  1419. tab.part_base = m_pointsInstant.bBasePart;
  1420. tab.skill_group = m_points.skill_group;
  1421.  
  1422. DWORD dwPlayedTime = (get_dword_time() - m_dwPlayStartTime);
  1423.  
  1424. if (dwPlayedTime > 60000)
  1425. {
  1426. if (GetSectree() && !GetSectree()->IsAttr(GetX(), GetY(), ATTR_BANPK))
  1427. {
  1428. if (GetRealAlignment() < 0)
  1429. {
  1430. if (IsEquipUniqueItem(UNIQUE_ITEM_FASTER_ALIGNMENT_UP_BY_TIME))
  1431. UpdateAlignment(120 * (dwPlayedTime / 60000));
  1432. else
  1433. UpdateAlignment(60 * (dwPlayedTime / 60000));
  1434. }
  1435. else
  1436. UpdateAlignment(5 * (dwPlayedTime / 60000));
  1437. }
  1438.  
  1439. SetRealPoint(POINT_PLAYTIME, GetRealPoint(POINT_PLAYTIME) + dwPlayedTime / 60000);
  1440. ResetPlayTime(dwPlayedTime % 60000);
  1441. }
  1442.  
  1443. tab.playtime = GetRealPoint(POINT_PLAYTIME);
  1444. tab.lAlignment = m_iRealAlignment;
  1445. #ifdef ENABLE_TITLE_SYSTEM
  1446. tab.lPrestige = m_iRealPrestige;
  1447. #endif
  1448.  
  1449. if (m_posWarp.x != 0 || m_posWarp.y != 0)
  1450. {
  1451. tab.x = m_posWarp.x;
  1452. tab.y = m_posWarp.y;
  1453. tab.z = 0;
  1454. tab.lMapIndex = m_lWarpMapIndex;
  1455. }
  1456. else
  1457. {
  1458. tab.x = GetX();
  1459. tab.y = GetY();
  1460. tab.z = GetZ();
  1461. tab.lMapIndex = GetMapIndex();
  1462. }
  1463.  
  1464. if (m_lExitMapIndex == 0)
  1465. {
  1466. tab.lExitMapIndex = tab.lMapIndex;
  1467. tab.lExitX = tab.x;
  1468. tab.lExitY = tab.y;
  1469. }
  1470. else
  1471. {
  1472. tab.lExitMapIndex = m_lExitMapIndex;
  1473. tab.lExitX = m_posExit.x;
  1474. tab.lExitY = m_posExit.y;
  1475. }
  1476.  
  1477. sys_log(0, "SAVE: %s %dx%d", GetName(), tab.x, tab.y);
  1478.  
  1479. tab.st = GetRealPoint(POINT_ST);
  1480. tab.ht = GetRealPoint(POINT_HT);
  1481. tab.dx = GetRealPoint(POINT_DX);
  1482. tab.iq = GetRealPoint(POINT_IQ);
  1483.  
  1484. tab.stat_point = GetPoint(POINT_STAT);
  1485. tab.skill_point = GetPoint(POINT_SKILL);
  1486. tab.sub_skill_point = GetPoint(POINT_SUB_SKILL);
  1487. tab.horse_skill_point = GetPoint(POINT_HORSE_SKILL);
  1488.  
  1489. tab.stat_reset_count = GetPoint(POINT_STAT_RESET_COUNT);
  1490.  
  1491. tab.hp = GetHP();
  1492. tab.sp = GetSP();
  1493.  
  1494. tab.stamina = GetStamina();
  1495.  
  1496. tab.sRandomHP = m_points.iRandomHP;
  1497. tab.sRandomSP = m_points.iRandomSP;
  1498.  
  1499. for (int i = 0; i < QUICKSLOT_MAX_NUM; ++i)
  1500. tab.quickslot[i] = m_quickslot[i];
  1501.  
  1502. if (m_stMobile.length() && !*m_szMobileAuth)
  1503. strlcpy(tab.szMobile, m_stMobile.c_str(), sizeof(tab.szMobile));
  1504.  
  1505. thecore_memcpy(tab.parts, m_pointsInstant.parts, sizeof(tab.parts));
  1506.  
  1507. // REMOVE_REAL_SKILL_LEVLES
  1508. thecore_memcpy(tab.skills, m_pSkillLevels, sizeof(TPlayerSkill) * SKILL_MAX_NUM);
  1509. // END_OF_REMOVE_REAL_SKILL_LEVLES
  1510.  
  1511. tab.horse = GetHorseData();
  1512. }
  1513.  
  1514.  
  1515. void CHARACTER::SaveReal()
  1516. {
  1517. if (m_bSkipSave)
  1518. return;
  1519.  
  1520. if (!GetDesc())
  1521. {
  1522. sys_err("Character::Save : no descriptor when saving (name: %s)", GetName());
  1523. return;
  1524. }
  1525.  
  1526. TPlayerTable table;
  1527. CreatePlayerProto(table);
  1528.  
  1529. db_clientdesc->DBPacket(HEADER_GD_PLAYER_SAVE, GetDesc()->GetHandle(), &table, sizeof(TPlayerTable));
  1530.  
  1531. quest::PC * pkQuestPC = quest::CQuestManager::instance().GetPCForce(GetPlayerID());
  1532.  
  1533. if (!pkQuestPC)
  1534. sys_err("CHARACTER::Save : null quest::PC pointer! (name %s)", GetName());
  1535. else
  1536. {
  1537. pkQuestPC->Save();
  1538. }
  1539.  
  1540. marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID());
  1541. if (pMarriage)
  1542. pMarriage->Save();
  1543. }
  1544.  
  1545. void CHARACTER::FlushDelayedSaveItem()
  1546. {
  1547. // 저장 안된 소지품을 전부 저장시킨다.
  1548. LPITEM item;
  1549.  
  1550. for (int i = 0; i < INVENTORY_AND_EQUIP_SLOT_MAX; ++i)
  1551. if ((item = GetInventoryItem(i)))
  1552. ITEM_MANAGER::instance().FlushDelayedSave(item);
  1553. }
  1554.  
  1555. void CHARACTER::Disconnect(const char * c_pszReason)
  1556. {
  1557. assert(GetDesc() != NULL);
  1558.  
  1559. sys_log(0, "DISCONNECT: %s (%s)", GetName(), c_pszReason ? c_pszReason : "unset" );
  1560.  
  1561. if (GetShop())
  1562. {
  1563. GetShop()->RemoveGuest(this);
  1564. SetShop(NULL);
  1565. }
  1566.  
  1567. if (GetArena() != NULL)
  1568. {
  1569. GetArena()->OnDisconnect(GetPlayerID());
  1570. }
  1571.  
  1572. if (GetParty() != NULL)
  1573. {
  1574. GetParty()->UpdateOfflineState(GetPlayerID());
  1575. }
  1576.  
  1577. marriage::CManager::instance().Logout(this);
  1578.  
  1579. // P2P Logout
  1580. TPacketGGLogout p;
  1581. p.bHeader = HEADER_GG_LOGOUT;
  1582. strlcpy(p.szName, GetName(), sizeof(p.szName));
  1583. P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGLogout));
  1584. char buf[51];
  1585. snprintf(buf, sizeof(buf),
  1586. #ifdef __GOLD_AS_LL__
  1587. "%s %lld %d %ld %d"
  1588. #ifdef ENABLE_TITLE_SYSTEM
  1589. " %d"
  1590. #endif
  1591. ,
  1592. #else
  1593. "%s %d %d %ld %d"
  1594. #ifdef ENABLE_TITLE_SYSTEM
  1595. " %d"
  1596. #endif
  1597. ,
  1598. #endif
  1599. inet_ntoa(GetDesc()->GetAddr().sin_addr), GetGold(), g_bChannel, GetMapIndex(), GetAlignment()
  1600. #ifdef ENABLE_TITLE_SYSTEM
  1601. ,GetTitle()
  1602. #endif
  1603. );
  1604.  
  1605. LogManager::instance().CharLog(this, 0, "LOGOUT", buf);
  1606.  
  1607. if (LC_IsYMIR() || LC_IsKorea() || LC_IsBrazil())
  1608. {
  1609. long playTime = GetRealPoint(POINT_PLAYTIME) - m_dwLoginPlayTime;
  1610. LogManager::instance().LoginLog(false, GetDesc()->GetAccountTable().id, GetPlayerID(), GetLevel(), GetJob(), playTime);
  1611.  
  1612. if (LC_IsBrazil() != true)
  1613. CPCBangManager::instance().Log(GetDesc()->GetHostName(), GetPlayerID(), playTime);
  1614. }
  1615.  
  1616. if (m_pWarMap)
  1617. SetWarMap(NULL);
  1618.  
  1619. if (m_pWeddingMap)
  1620. {
  1621. SetWeddingMap(NULL);
  1622. }
  1623.  
  1624. if (GetGuild())
  1625. GetGuild()->LogoutMember(this);
  1626.  
  1627. quest::CQuestManager::instance().LogoutPC(this);
  1628.  
  1629. if (GetParty())
  1630. GetParty()->Unlink(this);
  1631.  
  1632. // 죽었을 때 접속끊으면 경험치 줄게 하기
  1633. if (IsStun() || IsDead())
  1634. {
  1635. DeathPenalty(0);
  1636. PointChange(POINT_HP, 50 - GetHP());
  1637. }
  1638.  
  1639.  
  1640. if (!CHARACTER_MANAGER::instance().FlushDelayedSave(this))
  1641. {
  1642. SaveReal();
  1643. }
  1644.  
  1645. FlushDelayedSaveItem();
  1646.  
  1647. SaveAffect();
  1648. m_bIsLoadedAffect = false;
  1649.  
  1650. m_bSkipSave = true; // 이 이후에는 더이상 저장하면 안된다.
  1651.  
  1652. quest::CQuestManager::instance().DisconnectPC(this);
  1653.  
  1654. CloseSafebox();
  1655.  
  1656. CloseMall();
  1657.  
  1658. CPVPManager::instance().Disconnect(this);
  1659.  
  1660. CTargetManager::instance().Logout(GetPlayerID());
  1661.  
  1662. MessengerManager::instance().Logout(GetName());
  1663. #ifdef DEATHMATCH_MODE
  1664. if (GetMapIndex() == DEATMATCH_MAP_INDEX){
  1665. SetQuestFlag("qDeathMatchPanel.is_disconnected", 1); // Set as disconnected
  1666. if (!IsObserverMode())
  1667. CDeathMatchManager::instance().RemoveFromEvent(this); // Remove from attender list
  1668.  
  1669. RemoveBadAffect(); // Remove stun affect
  1670. }
  1671. #endif
  1672.  
  1673. if (GetMapIndex() == OXEVENT_MAP_INDEX)
  1674. COXEventManager::instance().RemoveFromIpList(GetDesc()->GetHostName());
  1675.  
  1676. if (g_TeenDesc)
  1677. {
  1678. int offset = 0;
  1679. char buf[245] = {0};
  1680.  
  1681. buf[0] = HEADER_GT_LOGOUT;
  1682. offset += 1;
  1683.  
  1684. memset(buf+offset, 0x00, 2);
  1685. offset += 2;
  1686.  
  1687. TAccountTable &acc_table = GetDesc()->GetAccountTable();
  1688. memcpy(buf+offset, &acc_table.id, 4);
  1689. offset += 4;
  1690.  
  1691. g_TeenDesc->Packet(buf, offset);
  1692. }
  1693.  
  1694. if (GetDesc())
  1695. {
  1696. GetDesc()->BindCharacter(NULL);
  1697. // BindDesc(NULL);
  1698. }
  1699.  
  1700.  
  1701. M2_DESTROY_CHARACTER(this);
  1702. }
  1703.  
  1704. bool CHARACTER::Show(long lMapIndex, long x, long y, long z, bool bShowSpawnMotion/* = false */)
  1705. {
  1706. LPSECTREE sectree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y);
  1707.  
  1708. if (!sectree)
  1709. {
  1710. sys_log(0, "cannot find sectree by %dx%d mapindex %d", x, y, lMapIndex);
  1711. return false;
  1712. }
  1713.  
  1714. SetMapIndex(lMapIndex);
  1715.  
  1716. bool bChangeTree = false;
  1717.  
  1718. if (!GetSectree() || GetSectree() != sectree)
  1719. bChangeTree = true;
  1720.  
  1721. if (bChangeTree)
  1722. {
  1723. if (GetSectree())
  1724. GetSectree()->RemoveEntity(this);
  1725.  
  1726. ViewCleanup();
  1727. }
  1728.  
  1729. if (!IsNPC())
  1730. {
  1731. sys_log(0, "SHOW: %s %dx%dx%d", GetName(), x, y, z);
  1732. if (GetStamina() < GetMaxStamina())
  1733. StartAffectEvent();
  1734. }
  1735. else if (m_pkMobData)
  1736. {
  1737. m_pkMobInst->m_posLastAttacked.x = x;
  1738. m_pkMobInst->m_posLastAttacked.y = y;
  1739. m_pkMobInst->m_posLastAttacked.z = z;
  1740. }
  1741.  
  1742. if (bShowSpawnMotion)
  1743. {
  1744. SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
  1745. m_afAffectFlag.Set(AFF_SPAWN);
  1746. }
  1747.  
  1748. SetXYZ(x, y, z);
  1749.  
  1750. m_posDest.x = x;
  1751. m_posDest.y = y;
  1752. m_posDest.z = z;
  1753.  
  1754. m_posStart.x = x;
  1755. m_posStart.y = y;
  1756. m_posStart.z = z;
  1757.  
  1758. if (bChangeTree)
  1759. {
  1760. EncodeInsertPacket(this);
  1761. sectree->InsertEntity(this);
  1762.  
  1763. UpdateSectree();
  1764. }
  1765. else
  1766. {
  1767. ViewReencode();
  1768. sys_log(0, " in same sectree");
  1769. }
  1770.  
  1771. REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
  1772.  
  1773. SetValidComboInterval(0);
  1774. return true;
  1775. }
  1776.  
  1777. // BGM_INFO
  1778. struct BGMInfo
  1779. {
  1780. std::string name;
  1781. float vol;
  1782. };
  1783.  
  1784. typedef std::map<unsigned, BGMInfo> BGMInfoMap;
  1785.  
  1786. static BGMInfoMap gs_bgmInfoMap;
  1787. static bool gs_bgmVolEnable = false;
  1788.  
  1789. void CHARACTER_SetBGMVolumeEnable()
  1790. {
  1791. gs_bgmVolEnable = true;
  1792. sys_log(0, "bgm_info.set_bgm_volume_enable");
  1793. }
  1794.  
  1795. void CHARACTER_AddBGMInfo(unsigned mapIndex, const char* name, float vol)
  1796. {
  1797. BGMInfo newInfo;
  1798. newInfo.name = name;
  1799. newInfo.vol = vol;
  1800.  
  1801. gs_bgmInfoMap[mapIndex] = newInfo;
  1802.  
  1803. sys_log(0, "bgm_info.add_info(%d, '%s', %f)", mapIndex, name, vol);
  1804. }
  1805.  
  1806. const BGMInfo& CHARACTER_GetBGMInfo(unsigned mapIndex)
  1807. {
  1808. BGMInfoMap::iterator f = gs_bgmInfoMap.find(mapIndex);
  1809. if (gs_bgmInfoMap.end() == f)
  1810. {
  1811. static BGMInfo s_empty = {"", 0.0f};
  1812. return s_empty;
  1813. }
  1814. return f->second;
  1815. }
  1816.  
  1817. bool CHARACTER_IsBGMVolumeEnable()
  1818. {
  1819. return gs_bgmVolEnable;
  1820. }
  1821. // END_OF_BGM_INFO
  1822.  
  1823. void CHARACTER::MainCharacterPacket()
  1824. {
  1825. const unsigned mapIndex = GetMapIndex();
  1826. const BGMInfo& bgmInfo = CHARACTER_GetBGMInfo(mapIndex);
  1827.  
  1828. // SUPPORT_BGM
  1829. if (!bgmInfo.name.empty())
  1830. {
  1831. if (CHARACTER_IsBGMVolumeEnable())
  1832. {
  1833. sys_log(1, "bgm_info.play_bgm_vol(%d, name='%s', vol=%f)", mapIndex, bgmInfo.name.c_str(), bgmInfo.vol);
  1834. TPacketGCMainCharacter4_BGM_VOL mainChrPacket;
  1835. mainChrPacket.header = HEADER_GC_MAIN_CHARACTER4_BGM_VOL;
  1836. mainChrPacket.dwVID = m_vid;
  1837. mainChrPacket.wRaceNum = GetRaceNum();
  1838. mainChrPacket.lx = GetX();
  1839. mainChrPacket.ly = GetY();
  1840. mainChrPacket.lz = GetZ();
  1841. mainChrPacket.empire = GetDesc()->GetEmpire();
  1842. mainChrPacket.skill_group = GetSkillGroup();
  1843. strlcpy(mainChrPacket.szChrName, GetName(), sizeof(mainChrPacket.szChrName));
  1844.  
  1845. mainChrPacket.fBGMVol = bgmInfo.vol;
  1846. strlcpy(mainChrPacket.szBGMName, bgmInfo.name.c_str(), sizeof(mainChrPacket.szBGMName));
  1847. GetDesc()->Packet(&mainChrPacket, sizeof(TPacketGCMainCharacter4_BGM_VOL));
  1848. }
  1849. else
  1850. {
  1851. sys_log(1, "bgm_info.play(%d, '%s')", mapIndex, bgmInfo.name.c_str());
  1852. TPacketGCMainCharacter3_BGM mainChrPacket;
  1853. mainChrPacket.header = HEADER_GC_MAIN_CHARACTER3_BGM;
  1854. mainChrPacket.dwVID = m_vid;
  1855. mainChrPacket.wRaceNum = GetRaceNum();
  1856. mainChrPacket.lx = GetX();
  1857. mainChrPacket.ly = GetY();
  1858. mainChrPacket.lz = GetZ();
  1859. mainChrPacket.empire = GetDesc()->GetEmpire();
  1860. mainChrPacket.skill_group = GetSkillGroup();
  1861. strlcpy(mainChrPacket.szChrName, GetName(), sizeof(mainChrPacket.szChrName));
  1862. strlcpy(mainChrPacket.szBGMName, bgmInfo.name.c_str(), sizeof(mainChrPacket.szBGMName));
  1863. GetDesc()->Packet(&mainChrPacket, sizeof(TPacketGCMainCharacter3_BGM));
  1864. }
  1865. //if (m_stMobile.length())
  1866. // ChatPacket(CHAT_TYPE_COMMAND, "sms");
  1867. }
  1868. // END_OF_SUPPORT_BGM
  1869. else
  1870. {
  1871. sys_log(0, "bgm_info.play(%d, DEFAULT_BGM_NAME)", mapIndex);
  1872.  
  1873. TPacketGCMainCharacter pack;
  1874. pack.header = HEADER_GC_MAIN_CHARACTER;
  1875. pack.dwVID = m_vid;
  1876. pack.wRaceNum = GetRaceNum();
  1877. pack.lx = GetX();
  1878. pack.ly = GetY();
  1879. pack.lz = GetZ();
  1880. pack.empire = GetDesc()->GetEmpire();
  1881. pack.skill_group = GetSkillGroup();
  1882. strlcpy(pack.szName, GetName(), sizeof(pack.szName));
  1883. GetDesc()->Packet(&pack, sizeof(TPacketGCMainCharacter));
  1884.  
  1885. if (m_stMobile.length())
  1886. ChatPacket(CHAT_TYPE_COMMAND, "sms");
  1887. }
  1888. }
  1889.  
  1890. void CHARACTER::PointsPacket()
  1891. {
  1892. if (!GetDesc())
  1893. return;
  1894.  
  1895. TPacketGCPoints pack;
  1896.  
  1897. pack.header = HEADER_GC_CHARACTER_POINTS;
  1898.  
  1899. pack.points[POINT_LEVEL] = GetLevel();
  1900. pack.points[POINT_EXP] = GetExp();
  1901. pack.points[POINT_NEXT_EXP] = GetNextExp();
  1902. pack.points[POINT_HP] = GetHP();
  1903. pack.points[POINT_MAX_HP] = GetMaxHP();
  1904. pack.points[POINT_SP] = GetSP();
  1905. pack.points[POINT_MAX_SP] = GetMaxSP();
  1906. pack.points[POINT_GOLD] = GetGold();
  1907. pack.points[POINT_STAMINA] = GetStamina();
  1908. pack.points[POINT_MAX_STAMINA] = GetMaxStamina();
  1909.  
  1910. for (int i = POINT_ST; i < POINT_MAX_NUM; ++i)
  1911. pack.points[i] = GetPoint(i);
  1912.  
  1913. GetDesc()->Packet(&pack, sizeof(TPacketGCPoints));
  1914. }
  1915.  
  1916. bool CHARACTER::ChangeSex()
  1917. {
  1918. int src_race = GetRaceNum();
  1919.  
  1920. switch (src_race)
  1921. {
  1922. case MAIN_RACE_WARRIOR_M:
  1923. m_points.job = MAIN_RACE_WARRIOR_W;
  1924. break;
  1925.  
  1926. case MAIN_RACE_WARRIOR_W:
  1927. m_points.job = MAIN_RACE_WARRIOR_M;
  1928. break;
  1929.  
  1930. case MAIN_RACE_ASSASSIN_M:
  1931. m_points.job = MAIN_RACE_ASSASSIN_W;
  1932. break;
  1933.  
  1934. case MAIN_RACE_ASSASSIN_W:
  1935. m_points.job = MAIN_RACE_ASSASSIN_M;
  1936. break;
  1937.  
  1938. case MAIN_RACE_SURA_M:
  1939. m_points.job = MAIN_RACE_SURA_W;
  1940. break;
  1941.  
  1942. case MAIN_RACE_SURA_W:
  1943. m_points.job = MAIN_RACE_SURA_M;
  1944. break;
  1945.  
  1946. case MAIN_RACE_SHAMAN_M:
  1947. m_points.job = MAIN_RACE_SHAMAN_W;
  1948. break;
  1949.  
  1950. case MAIN_RACE_SHAMAN_W:
  1951. m_points.job = MAIN_RACE_SHAMAN_M;
  1952. break;
  1953.  
  1954. default:
  1955. sys_err("CHANGE_SEX: %s unknown race %d", GetName(), src_race);
  1956. return false;
  1957. }
  1958.  
  1959. sys_log(0, "CHANGE_SEX: %s (%d -> %d)", GetName(), src_race, m_points.job);
  1960. return true;
  1961. }
  1962.  
  1963. WORD CHARACTER::GetRaceNum() const
  1964. {
  1965. if (m_dwPolymorphRace)
  1966. return m_dwPolymorphRace;
  1967.  
  1968. if (m_pkMobData)
  1969. return m_pkMobData->m_table.dwVnum;
  1970.  
  1971. return m_points.job;
  1972. }
  1973.  
  1974. void CHARACTER::SetRace(BYTE race)
  1975. {
  1976. if (race >= MAIN_RACE_MAX_NUM)
  1977. {
  1978. sys_err("CHARACTER::SetRace(name=%s, race=%d).OUT_OF_RACE_RANGE", GetName(), race);
  1979. return;
  1980. }
  1981.  
  1982. m_points.job = race;
  1983. }
  1984.  
  1985. BYTE CHARACTER::GetJob() const
  1986. {
  1987. unsigned race = m_points.job;
  1988. unsigned job;
  1989.  
  1990. if (RaceToJob(race, &job))
  1991. return job;
  1992.  
  1993. sys_err("CHARACTER::GetJob(name=%s, race=%d).OUT_OF_RACE_RANGE", GetName(), race);
  1994. return JOB_WARRIOR;
  1995. }
  1996.  
  1997. void CHARACTER::PlayerPunish(bool PowerPunish, int Duration)
  1998. {
  1999. if (!PowerPunish)
  2000. {
  2001. sys_log(0, "%s[%d] has been chatbanned because of spamming/writing words which are in the spamlist.txt", GetName(), GetPlayerID());
  2002. }
  2003. else
  2004. {
  2005. sys_log(0, "%s[%d] has been banned because of saying blacklisted word", GetName(), GetPlayerID());
  2006. GetDesc()->DelayedDisconnect(5);
  2007. }
  2008. }
  2009.  
  2010. bool CHARACTER::SpamAllowBuf(const char *Message)
  2011. {
  2012. if (!strcmp(Message, "(?˛´c)") || !strcmp(Message, "(μ·)") || !strcmp(Message, "(±a≫Y)") || !strcmp(Message, "(AA??)") || !strcmp(Message, "(≫c¶?)") || !strcmp(Message, "(?đłe)") || !strcmp(Message, "(??C?)") || !strcmp(Message, "(????)") || !strcmp(Message, "(AE??)"))
  2013. {
  2014. return true;
  2015. }
  2016.  
  2017. return false;
  2018. }
  2019.  
  2020. bool CHARACTER::SpamListCheck(const char *Message)
  2021. {
  2022. for (size_t i = 0; i < SpamBlockListArray.size(); i++)
  2023. {
  2024. if (!strcmp(Message, SpamBlockListArray[i].c_str()))
  2025. {
  2026. return false;
  2027. }
  2028. }
  2029.  
  2030. return true;
  2031. }
  2032.  
  2033. bool CHARACTER::BannListCheck(const char *Message)
  2034. {
  2035. for (size_t i = 0; i < SpamBannListArray.size(); i++)
  2036. {
  2037. if (!strcmp(Message, SpamBannListArray[i].c_str()))
  2038. {
  2039. return false;
  2040. }
  2041. }
  2042.  
  2043. return true;
  2044. }
  2045. #ifdef __LEVEL_AS_INT__
  2046. void CHARACTER::SetLevel(int level)
  2047. #else
  2048. void CHARACTER::SetLevel(BYTE level)
  2049. #endif
  2050. {
  2051. m_points.level = level;
  2052.  
  2053. if (IsPC())
  2054. {
  2055. if (level < PK_PROTECT_LEVEL)
  2056. SetPKMode(PK_MODE_PROTECT);
  2057. else if (GetGMLevel() != GM_PLAYER)
  2058. SetPKMode(PK_MODE_PROTECT);
  2059. else if (m_bPKMode == PK_MODE_PROTECT)
  2060. SetPKMode(PK_MODE_PEACE);
  2061. }
  2062. }
  2063.  
  2064. void CHARACTER::SetEmpire(BYTE bEmpire)
  2065. {
  2066. m_bEmpire = bEmpire;
  2067. }
  2068.  
  2069. void CHARACTER::SetPlayerProto(const TPlayerTable * t)
  2070. {
  2071. if (!GetDesc() || !*GetDesc()->GetHostName())
  2072. sys_err("cannot get desc or hostname");
  2073. else
  2074. SetGMLevel();
  2075.  
  2076. m_bCharType = CHAR_TYPE_PC;
  2077.  
  2078. m_dwPlayerID = t->id;
  2079.  
  2080. m_iAlignment = t->lAlignment;
  2081. m_iRealAlignment = t->lAlignment;
  2082. #ifdef ENABLE_TITLE_SYSTEM
  2083. m_iPrestige = t->lPrestige;
  2084. m_iRealPrestige = t->lPrestige;
  2085. #endif
  2086.  
  2087. m_points.voice = t->voice;
  2088.  
  2089. m_points.skill_group = t->skill_group;
  2090.  
  2091. m_pointsInstant.bBasePart = t->part_base;
  2092. SetPart(PART_HAIR, t->parts[PART_HAIR]);
  2093. #ifdef __SASH_SYSTEM__
  2094. SetPart(PART_SASH, t->parts[PART_SASH]);
  2095. #endif
  2096.  
  2097. m_points.iRandomHP = t->sRandomHP;
  2098. m_points.iRandomSP = t->sRandomSP;
  2099.  
  2100. // REMOVE_REAL_SKILL_LEVLES
  2101. if (m_pSkillLevels)
  2102. M2_DELETE_ARRAY(m_pSkillLevels);
  2103.  
  2104. m_pSkillLevels = M2_NEW TPlayerSkill[SKILL_MAX_NUM];
  2105. thecore_memcpy(m_pSkillLevels, t->skills, sizeof(TPlayerSkill) * SKILL_MAX_NUM);
  2106. // END_OF_REMOVE_REAL_SKILL_LEVLES
  2107.  
  2108. if (t->lMapIndex >= 10000)
  2109. {
  2110. m_posWarp.x = t->lExitX;
  2111. m_posWarp.y = t->lExitY;
  2112. m_lWarpMapIndex = t->lExitMapIndex;
  2113. }
  2114.  
  2115. SetRealPoint(POINT_PLAYTIME, t->playtime);
  2116. m_dwLoginPlayTime = t->playtime;
  2117. SetRealPoint(POINT_ST, t->st);
  2118. SetRealPoint(POINT_HT, t->ht);
  2119. SetRealPoint(POINT_DX, t->dx);
  2120. SetRealPoint(POINT_IQ, t->iq);
  2121.  
  2122. SetPoint(POINT_ST, t->st);
  2123. SetPoint(POINT_HT, t->ht);
  2124. SetPoint(POINT_DX, t->dx);
  2125. SetPoint(POINT_IQ, t->iq);
  2126.  
  2127. SetPoint(POINT_STAT, t->stat_point);
  2128. SetPoint(POINT_SKILL, t->skill_point);
  2129. SetPoint(POINT_SUB_SKILL, t->sub_skill_point);
  2130. SetPoint(POINT_HORSE_SKILL, t->horse_skill_point);
  2131.  
  2132. SetPoint(POINT_STAT_RESET_COUNT, t->stat_reset_count);
  2133.  
  2134. SetPoint(POINT_LEVEL_STEP, t->level_step);
  2135. SetRealPoint(POINT_LEVEL_STEP, t->level_step);
  2136.  
  2137. SetRace(t->job);
  2138.  
  2139. SetLevel(t->level);
  2140. SetExp(t->exp);
  2141. SetGold(t->gold);
  2142.  
  2143. SetMapIndex(t->lMapIndex);
  2144. SetXYZ(t->x, t->y, t->z);
  2145.  
  2146. ComputePoints();
  2147.  
  2148. SetHP(GetMaxHP());
  2149. SetSP(GetMaxSP());
  2150. SetStamina(t->stamina);
  2151.  
  2152. //GM일때 보호모드
  2153. if (!test_server)
  2154. {
  2155. if (GetGMLevel() > GM_LOW_WIZARD)
  2156. {
  2157. #ifdef __DIF_GM__
  2158.  
  2159. #else
  2160. m_afAffectFlag.Set(AFF_YMIR);
  2161. #endif
  2162. m_bPKMode = PK_MODE_PROTECT;
  2163. }
  2164. }
  2165.  
  2166. if (GetLevel() < PK_PROTECT_LEVEL)
  2167. m_bPKMode = PK_MODE_PROTECT;
  2168.  
  2169. m_stMobile = t->szMobile;
  2170.  
  2171. SetHorseData(t->horse);
  2172.  
  2173. if (GetHorseLevel() > 0)
  2174. UpdateHorseDataByLogoff(t->logoff_interval);
  2175.  
  2176. thecore_memcpy(m_aiPremiumTimes, t->aiPremiumTimes, sizeof(t->aiPremiumTimes));
  2177.  
  2178. m_dwLogOffInterval = t->logoff_interval;
  2179.  
  2180. sys_log(0, "PLAYER_LOAD: %s PREMIUM %d %d, LOGGOFF_INTERVAL %u PTR: %p", t->name, m_aiPremiumTimes[0], m_aiPremiumTimes[1], t->logoff_interval, this);
  2181.  
  2182. if (GetGMLevel() != GM_PLAYER)
  2183. {
  2184. LogManager::instance().CharLog(this, GetGMLevel(), "GM_LOGIN", "");
  2185. sys_log(0, "GM_LOGIN(gmlevel=%d, name=%s(%d), pos=(%d, %d)", GetGMLevel(), GetName(), GetPlayerID(), GetX(), GetY());
  2186. }
  2187.  
  2188. #ifdef __PET_SYSTEM__
  2189. // NOTE: 일단 캐릭터가 PC인 경우에만 PetSystem을 갖도록 함. 유럽 머신당 메모리 사용률때문에 NPC까지 하긴 좀..
  2190. if (m_petSystem)
  2191. {
  2192. m_petSystem->Destroy();
  2193. delete m_petSystem;
  2194. }
  2195.  
  2196. m_petSystem = M2_NEW CPetSystem(this);
  2197. #endif
  2198. }
  2199.  
  2200. EVENTFUNC(kill_ore_load_event)
  2201. {
  2202. char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  2203. if ( info == NULL )
  2204. {
  2205. sys_err( "kill_ore_load_even> <Factor> Null pointer" );
  2206. return 0;
  2207. }
  2208.  
  2209. LPCHARACTER ch = info->ch;
  2210. if (ch == NULL) { // <Factor>
  2211. return 0;
  2212. }
  2213.  
  2214. ch->m_pkMiningEvent = NULL;
  2215. M2_DESTROY_CHARACTER(ch);
  2216. return 0;
  2217. }
  2218.  
  2219. void CHARACTER::SetProto(const CMob * pkMob)
  2220. {
  2221. if (m_pkMobInst)
  2222. M2_DELETE(m_pkMobInst);
  2223.  
  2224. m_pkMobData = pkMob;
  2225. m_pkMobInst = M2_NEW CMobInstance;
  2226.  
  2227. m_bPKMode = PK_MODE_FREE;
  2228.  
  2229. const TMobTable * t = &m_pkMobData->m_table;
  2230.  
  2231. m_bCharType = t->bType;
  2232.  
  2233. SetLevel(t->bLevel);
  2234. SetEmpire(t->bEmpire);
  2235.  
  2236. SetExp(t->dwExp);
  2237. SetRealPoint(POINT_ST, t->bStr);
  2238. SetRealPoint(POINT_DX, t->bDex);
  2239. SetRealPoint(POINT_HT, t->bCon);
  2240. SetRealPoint(POINT_IQ, t->bInt);
  2241.  
  2242. ComputePoints();
  2243.  
  2244. SetHP(GetMaxHP());
  2245. SetSP(GetMaxSP());
  2246.  
  2247. ////////////////////
  2248. m_pointsInstant.dwAIFlag = t->dwAIFlag;
  2249. SetImmuneFlag(t->dwImmuneFlag);
  2250.  
  2251. AssignTriggers(t);
  2252.  
  2253. ApplyMobAttribute(t);
  2254.  
  2255. if (IsStone())
  2256. {
  2257. DetermineDropMetinStone();
  2258. }
  2259.  
  2260. if (IsWarp() || IsGoto())
  2261. {
  2262. StartWarpNPCEvent();
  2263. }
  2264.  
  2265. CHARACTER_MANAGER::instance().RegisterRaceNumMap(this);
  2266.  
  2267. // XXX X-mas santa hardcoding
  2268. if (GetRaceNum() == xmas::MOB_SANTA_VNUM)
  2269. {
  2270. SetPoint(POINT_ATT_GRADE_BONUS, 10);
  2271. if (g_iUseLocale)
  2272. SetPoint(POINT_DEF_GRADE_BONUS, 6);
  2273. else
  2274. SetPoint(POINT_DEF_GRADE_BONUS, 15);
  2275.  
  2276. //산타용
  2277. //m_dwPlayStartTime = get_dword_time() + 10 * 60 * 1000;
  2278. //신선자 노해
  2279. m_dwPlayStartTime = get_dword_time() + 30 * 1000;
  2280. if (test_server)
  2281. m_dwPlayStartTime = get_dword_time() + 30 * 1000;
  2282. }
  2283.  
  2284. // XXX CTF GuildWar hardcoding
  2285. if (warmap::IsWarFlag(GetRaceNum()))
  2286. {
  2287. m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty);
  2288. m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty);
  2289. m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty);
  2290. }
  2291.  
  2292. if (warmap::IsWarFlagBase(GetRaceNum()))
  2293. {
  2294. m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty);
  2295. m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty);
  2296. m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty);
  2297. }
  2298.  
  2299. if (m_bCharType == CHAR_TYPE_HORSE ||
  2300. GetRaceNum() == 20101 ||
  2301. GetRaceNum() == 20102 ||
  2302. GetRaceNum() == 20103 ||
  2303. GetRaceNum() == 20104 ||
  2304. GetRaceNum() == 20105 ||
  2305. GetRaceNum() == 20106 ||
  2306. GetRaceNum() == 20107 ||
  2307. GetRaceNum() == 20108 ||
  2308. GetRaceNum() == 20109
  2309. )
  2310. {
  2311. m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateHorse, &CHARACTER::EndStateEmpty);
  2312. m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateMove, &CHARACTER::EndStateEmpty);
  2313. m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateHorse, &CHARACTER::EndStateEmpty);
  2314. }
  2315.  
  2316. // MINING
  2317. if (mining::IsVeinOfOre (GetRaceNum()))
  2318. {
  2319. char_event_info* info = AllocEventInfo<char_event_info>();
  2320.  
  2321. info->ch = this;
  2322.  
  2323. m_pkMiningEvent = event_create(kill_ore_load_event, info, PASSES_PER_SEC(number(7 * 60, 15 * 60)));
  2324. }
  2325. // END_OF_MINING
  2326. }
  2327.  
  2328. const TMobTable & CHARACTER::GetMobTable() const
  2329. {
  2330. return m_pkMobData->m_table;
  2331. }
  2332.  
  2333. bool CHARACTER::IsRaceFlag(DWORD dwBit) const
  2334. {
  2335. return m_pkMobData ? IS_SET(m_pkMobData->m_table.dwRaceFlag, dwBit) : 0;
  2336. }
  2337.  
  2338. DWORD CHARACTER::GetMobDamageMin() const
  2339. {
  2340. return m_pkMobData->m_table.dwDamageRange[0];
  2341. }
  2342.  
  2343. DWORD CHARACTER::GetMobDamageMax() const
  2344. {
  2345. return m_pkMobData->m_table.dwDamageRange[1];
  2346. }
  2347.  
  2348. float CHARACTER::GetMobDamageMultiply() const
  2349. {
  2350. float fDamMultiply = GetMobTable().fDamMultiply;
  2351.  
  2352. if (IsBerserk())
  2353. fDamMultiply = fDamMultiply * 2.0f; // BALANCE: 광폭화 시 두배
  2354.  
  2355. return fDamMultiply;
  2356. }
  2357.  
  2358. DWORD CHARACTER::GetMobDropItemVnum() const
  2359. {
  2360. return m_pkMobData->m_table.dwDropItemVnum;
  2361. }
  2362.  
  2363. bool CHARACTER::IsSummonMonster() const
  2364. {
  2365. return GetSummonVnum() != 0;
  2366. }
  2367.  
  2368. DWORD CHARACTER::GetSummonVnum() const
  2369. {
  2370. return m_pkMobData ? m_pkMobData->m_table.dwSummonVnum : 0;
  2371. }
  2372.  
  2373. DWORD CHARACTER::GetPolymorphItemVnum() const
  2374. {
  2375. return m_pkMobData ? m_pkMobData->m_table.dwPolymorphItemVnum : 0;
  2376. }
  2377.  
  2378. DWORD CHARACTER::GetMonsterDrainSPPoint() const
  2379. {
  2380. return m_pkMobData ? m_pkMobData->m_table.dwDrainSP : 0;
  2381. }
  2382.  
  2383. BYTE CHARACTER::GetMobRank() const
  2384. {
  2385. if (!m_pkMobData)
  2386. return MOB_RANK_KNIGHT; // PC일 경우 KNIGHT급
  2387.  
  2388. return m_pkMobData->m_table.bRank;
  2389. }
  2390.  
  2391. BYTE CHARACTER::GetMobSize() const
  2392. {
  2393. if (!m_pkMobData)
  2394. return MOBSIZE_MEDIUM;
  2395.  
  2396. return m_pkMobData->m_table.bSize;
  2397. }
  2398.  
  2399. WORD CHARACTER::GetMobAttackRange() const
  2400. {
  2401. switch (GetMobBattleType())
  2402. {
  2403. case BATTLE_TYPE_RANGE:
  2404. case BATTLE_TYPE_MAGIC:
  2405. return m_pkMobData->m_table.wAttackRange + GetPoint(POINT_BOW_DISTANCE);
  2406. default:
  2407. return m_pkMobData->m_table.wAttackRange;
  2408. }
  2409. }
  2410.  
  2411. BYTE CHARACTER::GetMobBattleType() const
  2412. {
  2413. if (!m_pkMobData)
  2414. return BATTLE_TYPE_MELEE;
  2415.  
  2416. return (m_pkMobData->m_table.bBattleType);
  2417. }
  2418.  
  2419. void CHARACTER::ComputeBattlePoints()
  2420. {
  2421. if (IsPolymorphed())
  2422. {
  2423. DWORD dwMobVnum = GetPolymorphVnum();
  2424. const CMob * pMob = CMobManager::instance().Get(dwMobVnum);
  2425. int iAtt = 0;
  2426. int iDef = 0;
  2427.  
  2428. if (pMob)
  2429. {
  2430. iAtt = GetLevel() * 2 + GetPolymorphPoint(POINT_ST) * 2;
  2431. // lev + con
  2432. iDef = GetLevel() + GetPolymorphPoint(POINT_HT) + pMob->m_table.wDef;
  2433. }
  2434.  
  2435. SetPoint(POINT_ATT_GRADE, iAtt);
  2436. SetPoint(POINT_DEF_GRADE, iDef);
  2437. SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE));
  2438. SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE));
  2439. }
  2440. else if (IsPC())
  2441. {
  2442. SetPoint(POINT_ATT_GRADE, 0);
  2443. SetPoint(POINT_DEF_GRADE, 0);
  2444. SetPoint(POINT_CLIENT_DEF_GRADE, 0);
  2445. SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE));
  2446. SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE));
  2447.  
  2448. //
  2449. // 기본 ATK = 2lev + 2str, 직업에 마다 2str은 바뀔 수 있음
  2450. //
  2451. int iAtk = GetLevel() * 2;
  2452. int iStatAtk = 0;
  2453.  
  2454. switch (GetJob())
  2455. {
  2456. case JOB_WARRIOR:
  2457. case JOB_SURA:
  2458. iStatAtk = (2 * GetPoint(POINT_ST));
  2459. break;
  2460.  
  2461. case JOB_ASSASSIN:
  2462. iStatAtk = (4 * GetPoint(POINT_ST) + 2 * GetPoint(POINT_DX)) / 3;
  2463. break;
  2464.  
  2465. case JOB_SHAMAN:
  2466. iStatAtk = (4 * GetPoint(POINT_ST) + 2 * GetPoint(POINT_IQ)) / 3;
  2467. break;
  2468.  
  2469. default:
  2470. sys_err("invalid job %d", GetJob());
  2471. iStatAtk = (2 * GetPoint(POINT_ST));
  2472. break;
  2473. }
  2474.  
  2475. // 말을 타고 있고, 스탯으로 인한 공격력이 ST*2 보다 낮으면 ST*2로 한다.
  2476. // 스탯을 잘못 찍은 사람 공격력이 더 낮지 않게 하기 위해서다.
  2477. if (GetMountVnum() && iStatAtk < 2 * GetPoint(POINT_ST))
  2478. iStatAtk = (2 * GetPoint(POINT_ST));
  2479.  
  2480. iAtk += iStatAtk;
  2481.  
  2482. // 승마(말) : 검수라 데미지 감소
  2483. if (GetMountVnum())
  2484. {
  2485. if (GetJob() == JOB_SURA && GetSkillGroup() == 1)
  2486. {
  2487. iAtk += (iAtk * GetHorseLevel()) / 60;
  2488. }
  2489. else
  2490. {
  2491. iAtk += (iAtk * GetHorseLevel()) / 30;
  2492. }
  2493. }
  2494.  
  2495. //
  2496. // ATK Setting
  2497. //
  2498. iAtk += GetPoint(POINT_ATT_GRADE_BONUS);
  2499.  
  2500. PointChange(POINT_ATT_GRADE, iAtk);
  2501.  
  2502. // DEF = LEV + CON + ARMOR
  2503. int iShowDef = GetLevel() + GetPoint(POINT_HT); // For Ymir(천마)
  2504. int iDef = GetLevel() + (int) (GetPoint(POINT_HT) / 1.25); // For Other
  2505. int iArmor = 0;
  2506.  
  2507. LPITEM pkItem;
  2508.  
  2509. for (int i = 0; i < WEAR_MAX_NUM; ++i)
  2510. if ((pkItem = GetWear(i)) && pkItem->GetType() == ITEM_ARMOR)
  2511. {
  2512. if (pkItem->GetSubType() == ARMOR_BODY || pkItem->GetSubType() == ARMOR_HEAD || pkItem->GetSubType() == ARMOR_FOOTS || pkItem->GetSubType() == ARMOR_SHIELD)
  2513. {
  2514. iArmor += pkItem->GetValue(1);
  2515. iArmor += (2 * pkItem->GetValue(5));
  2516. }
  2517. }
  2518.  
  2519. // 말 타고 있을 때 방어력이 말의 기준 방어력보다 낮으면 기준 방어력으로 설정
  2520. if( true == IsHorseRiding() )
  2521. {
  2522. if (iArmor < GetHorseArmor())
  2523. iArmor = GetHorseArmor();
  2524.  
  2525. const char* pHorseName = CHorseNameManager::instance().GetHorseName(GetPlayerID());
  2526.  
  2527. if (pHorseName != NULL && strlen(pHorseName))
  2528. {
  2529. iArmor += 20;
  2530. }
  2531. }
  2532.  
  2533. iArmor += GetPoint(POINT_DEF_GRADE_BONUS);
  2534. iArmor += GetPoint(POINT_PARTY_DEFENDER_BONUS);
  2535.  
  2536. // INTERNATIONAL_VERSION
  2537. if (LC_IsYMIR())
  2538. {
  2539. PointChange(POINT_DEF_GRADE, iShowDef + iArmor);
  2540. }
  2541. else
  2542. {
  2543. PointChange(POINT_DEF_GRADE, iDef + iArmor);
  2544. PointChange(POINT_CLIENT_DEF_GRADE, (iShowDef + iArmor) - GetPoint(POINT_DEF_GRADE));
  2545. }
  2546. // END_OF_INTERNATIONAL_VERSION
  2547.  
  2548. PointChange(POINT_MAGIC_ATT_GRADE, GetLevel() * 2 + GetPoint(POINT_IQ) * 2 + GetPoint(POINT_MAGIC_ATT_GRADE_BONUS));
  2549. PointChange(POINT_MAGIC_DEF_GRADE, GetLevel() + (GetPoint(POINT_IQ) * 3 + GetPoint(POINT_HT)) / 3 + iArmor / 2 + GetPoint(POINT_MAGIC_DEF_GRADE_BONUS));
  2550. }
  2551. else
  2552. {
  2553. // 2lev + str * 2
  2554. int iAtt = GetLevel() * 2 + GetPoint(POINT_ST) * 2;
  2555. // lev + con
  2556. int iDef = GetLevel() + GetPoint(POINT_HT) + GetMobTable().wDef;
  2557.  
  2558. SetPoint(POINT_ATT_GRADE, iAtt);
  2559. SetPoint(POINT_DEF_GRADE, iDef);
  2560. SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE));
  2561. SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE));
  2562. }
  2563. }
  2564.  
  2565. void CHARACTER::ComputePoints()
  2566. {
  2567. long lStat = GetPoint(POINT_STAT);
  2568. long lStatResetCount = GetPoint(POINT_STAT_RESET_COUNT);
  2569. long lSkillActive = GetPoint(POINT_SKILL);
  2570. long lSkillSub = GetPoint(POINT_SUB_SKILL);
  2571. long lSkillHorse = GetPoint(POINT_HORSE_SKILL);
  2572. long lLevelStep = GetPoint(POINT_LEVEL_STEP);
  2573.  
  2574. long lAttackerBonus = GetPoint(POINT_PARTY_ATTACKER_BONUS);
  2575. long lTankerBonus = GetPoint(POINT_PARTY_TANKER_BONUS);
  2576. long lBufferBonus = GetPoint(POINT_PARTY_BUFFER_BONUS);
  2577. long lSkillMasterBonus = GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);
  2578. long lHasteBonus = GetPoint(POINT_PARTY_HASTE_BONUS);
  2579. long lDefenderBonus = GetPoint(POINT_PARTY_DEFENDER_BONUS);
  2580.  
  2581. long lHPRecovery = GetPoint(POINT_HP_RECOVERY);
  2582. long lSPRecovery = GetPoint(POINT_SP_RECOVERY);
  2583. #ifdef INVENTORY_PROTECT
  2584. long envanter_kilidi = Inventory_Protect();
  2585. #endif
  2586.  
  2587. memset(m_pointsInstant.points, 0, sizeof(m_pointsInstant.points));
  2588. BuffOnAttr_ClearAll();
  2589. m_SkillDamageBonus.clear();
  2590.  
  2591. SetPoint(POINT_STAT, lStat);
  2592. SetPoint(POINT_SKILL, lSkillActive);
  2593. SetPoint(POINT_SUB_SKILL, lSkillSub);
  2594. SetPoint(POINT_HORSE_SKILL, lSkillHorse);
  2595. SetPoint(POINT_LEVEL_STEP, lLevelStep);
  2596. SetPoint(POINT_STAT_RESET_COUNT, lStatResetCount);
  2597.  
  2598. SetPoint(POINT_ST, GetRealPoint(POINT_ST));
  2599. SetPoint(POINT_HT, GetRealPoint(POINT_HT));
  2600. SetPoint(POINT_DX, GetRealPoint(POINT_DX));
  2601. SetPoint(POINT_IQ, GetRealPoint(POINT_IQ));
  2602.  
  2603. SetPart(PART_MAIN, GetOriginalPart(PART_MAIN));
  2604. SetPart(PART_WEAPON, GetOriginalPart(PART_WEAPON));
  2605. SetPart(PART_HEAD, GetOriginalPart(PART_HEAD));
  2606. SetPart(PART_HAIR, GetOriginalPart(PART_HAIR));
  2607. #ifdef __SASH_SYSTEM__
  2608. SetPart(PART_SASH, GetOriginalPart(PART_SASH));
  2609. #endif
  2610.  
  2611. SetPoint(POINT_PARTY_ATTACKER_BONUS, lAttackerBonus);
  2612. SetPoint(POINT_PARTY_TANKER_BONUS, lTankerBonus);
  2613. SetPoint(POINT_PARTY_BUFFER_BONUS, lBufferBonus);
  2614. SetPoint(POINT_PARTY_SKILL_MASTER_BONUS, lSkillMasterBonus);
  2615. SetPoint(POINT_PARTY_HASTE_BONUS, lHasteBonus);
  2616. SetPoint(POINT_PARTY_DEFENDER_BONUS, lDefenderBonus);
  2617.  
  2618. SetPoint(POINT_HP_RECOVERY, lHPRecovery);
  2619. SetPoint(POINT_SP_RECOVERY, lSPRecovery);
  2620.  
  2621. // PC_BANG_ITEM_ADD
  2622. SetPoint(POINT_PC_BANG_EXP_BONUS, 0);
  2623. SetPoint(POINT_PC_BANG_DROP_BONUS, 0);
  2624. #ifdef INVENTORY_PROTECT
  2625. SetPoint(ENVANTER, envanter_kilidi);
  2626. #endif
  2627. // END_PC_BANG_ITEM_ADD
  2628.  
  2629. int iMaxHP, iMaxSP;
  2630. int iMaxStamina;
  2631.  
  2632. if (IsPC())
  2633. {
  2634. // 최대 생명력/정신력
  2635. iMaxHP = JobInitialPoints[GetJob()].max_hp + m_points.iRandomHP + GetPoint(POINT_HT) * JobInitialPoints[GetJob()].hp_per_ht;
  2636. iMaxSP = JobInitialPoints[GetJob()].max_sp + m_points.iRandomSP + GetPoint(POINT_IQ) * JobInitialPoints[GetJob()].sp_per_iq;
  2637. iMaxStamina = JobInitialPoints[GetJob()].max_stamina + GetPoint(POINT_HT) * JobInitialPoints[GetJob()].stamina_per_con;
  2638.  
  2639. {
  2640. CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_ADD_HP);
  2641.  
  2642. if (NULL != pkSk)
  2643. {
  2644. pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_ADD_HP) / 100.0f);
  2645.  
  2646. iMaxHP += static_cast<int>(pkSk->kPointPoly.Eval());
  2647. }
  2648. }
  2649.  
  2650. // 기본 값들
  2651. SetPoint(POINT_MOV_SPEED, 100);
  2652. SetPoint(POINT_ATT_SPEED, 100);
  2653. PointChange(POINT_ATT_SPEED, GetPoint(POINT_PARTY_HASTE_BONUS));
  2654. SetPoint(POINT_CASTING_SPEED, 100);
  2655. }
  2656. else
  2657. {
  2658. iMaxHP = m_pkMobData->m_table.dwMaxHP;
  2659. iMaxSP = 0;
  2660. iMaxStamina = 0;
  2661.  
  2662. SetPoint(POINT_ATT_SPEED, m_pkMobData->m_table.sAttackSpeed);
  2663. SetPoint(POINT_MOV_SPEED, m_pkMobData->m_table.sMovingSpeed);
  2664. SetPoint(POINT_CASTING_SPEED, m_pkMobData->m_table.sAttackSpeed);
  2665. }
  2666.  
  2667. if (IsPC())
  2668. {
  2669. // 말 타고 있을 때는 기본 스탯이 말의 기준 스탯보다 낮으면 높게 만든다.
  2670. // 따라서 말의 기준 스탯이 무사 기준이므로, 수라/무당은 전체 스탯 합이
  2671. // 대채적으로 더 올라가게 될 것이다.
  2672. if (GetMountVnum())
  2673. {
  2674. if (GetHorseST() > GetPoint(POINT_ST))
  2675. PointChange(POINT_ST, GetHorseST() - GetPoint(POINT_ST));
  2676.  
  2677. if (GetHorseDX() > GetPoint(POINT_DX))
  2678. PointChange(POINT_DX, GetHorseDX() - GetPoint(POINT_DX));
  2679.  
  2680. if (GetHorseHT() > GetPoint(POINT_HT))
  2681. PointChange(POINT_HT, GetHorseHT() - GetPoint(POINT_HT));
  2682.  
  2683. if (GetHorseIQ() > GetPoint(POINT_IQ))
  2684. PointChange(POINT_IQ, GetHorseIQ() - GetPoint(POINT_IQ));
  2685. }
  2686.  
  2687. }
  2688.  
  2689. ComputeBattlePoints();
  2690.  
  2691. // 기본 HP/SP 설정
  2692. if (iMaxHP != GetMaxHP())
  2693. {
  2694. SetRealPoint(POINT_MAX_HP, iMaxHP); // 기본HP를 RealPoint에 저장해 놓는다.
  2695. }
  2696.  
  2697. PointChange(POINT_MAX_HP, 0);
  2698.  
  2699. if (iMaxSP != GetMaxSP())
  2700. {
  2701. SetRealPoint(POINT_MAX_SP, iMaxSP); // 기본SP를 RealPoint에 저장해 놓는다.
  2702. }
  2703.  
  2704. PointChange(POINT_MAX_SP, 0);
  2705.  
  2706. SetMaxStamina(iMaxStamina);
  2707.  
  2708. m_pointsInstant.dwImmuneFlag = 0;
  2709.  
  2710. for (int i = 0 ; i < WEAR_MAX_NUM; i++)
  2711. {
  2712. LPITEM pItem = GetWear(i);
  2713. if (pItem)
  2714. {
  2715. pItem->ModifyPoints(true);
  2716. SET_BIT(m_pointsInstant.dwImmuneFlag, GetWear(i)->GetImmuneFlag());
  2717. }
  2718. }
  2719.  
  2720. // 용혼석 시스템
  2721. // ComputePoints에서는 케릭터의 모든 속성값을 초기화하고,
  2722. // 아이템, 버프 등에 관련된 모든 속성값을 재계산하기 때문에,
  2723. // 용혼석 시스템도 ActiveDeck에 있는 모든 용혼석의 속성값을 다시 적용시켜야 한다.
  2724. if (DragonSoul_IsDeckActivated())
  2725. {
  2726. for (int i = WEAR_MAX_NUM + DS_SLOT_MAX * DragonSoul_GetActiveDeck();
  2727. i < WEAR_MAX_NUM + DS_SLOT_MAX * (DragonSoul_GetActiveDeck() + 1); i++)
  2728. {
  2729. LPITEM pItem = GetWear(i);
  2730. if (pItem)
  2731. {
  2732. if (DSManager::instance().IsTimeLeftDragonSoul(pItem))
  2733. pItem->ModifyPoints(true);
  2734. }
  2735. }
  2736. }
  2737.  
  2738. if (GetHP() > GetMaxHP())
  2739. PointChange(POINT_HP, GetMaxHP() - GetHP());
  2740.  
  2741. if (GetSP() > GetMaxSP())
  2742. PointChange(POINT_SP, GetMaxSP() - GetSP());
  2743.  
  2744. ComputeSkillPoints();
  2745.  
  2746. RefreshAffect();
  2747. CPetSystem* pPetSystem = GetPetSystem();
  2748. if (NULL != pPetSystem)
  2749. {
  2750. pPetSystem->RefreshBuff();
  2751. }
  2752.  
  2753. for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++)
  2754. {
  2755. it->second->GiveAllAttributes();
  2756. }
  2757. if (GetHP() > GetMaxHP())
  2758. PointChange(POINT_HP, GetMaxHP() - GetHP());
  2759.  
  2760. if (GetSP() > GetMaxSP())
  2761. PointChange(POINT_SP, GetMaxSP() - GetSP());
  2762.  
  2763. UpdatePacket();
  2764. }
  2765.  
  2766. // m_dwPlayStartTime의 단위는 milisecond다. 데이터베이스에는 분단위로 기록하기
  2767. // 때문에 플레이시간을 계산할 때 / 60000 으로 나눠서 하는데, 그 나머지 값이 남았
  2768. // 을 때 여기에 dwTimeRemain으로 넣어서 제대로 계산되도록 해주어야 한다.
  2769. void CHARACTER::ResetPlayTime(DWORD dwTimeRemain)
  2770. {
  2771. m_dwPlayStartTime = get_dword_time() - dwTimeRemain;
  2772. }
  2773.  
  2774. const int aiRecoveryPercents[10] = { 1, 5, 5, 5, 5, 5, 5, 5, 5, 5 };
  2775.  
  2776. EVENTFUNC(recovery_event)
  2777. {
  2778. char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  2779. if ( info == NULL )
  2780. {
  2781. sys_err( "recovery_event> <Factor> Null pointer" );
  2782. return 0;
  2783. }
  2784.  
  2785. LPCHARACTER ch = info->ch;
  2786.  
  2787. if (ch == NULL) { // <Factor>
  2788. return 0;
  2789. }
  2790.  
  2791. if (!ch->IsPC())
  2792. {
  2793. //
  2794. // 몬스터 회복
  2795. //
  2796. if (ch->IsAffectFlag(AFF_POISON))
  2797. return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle));
  2798.  
  2799. if (2493 == ch->GetMobTable().dwVnum)
  2800. {
  2801. int regenPct = BlueDragon_GetRangeFactor("hp_regen", ch->GetHPPct());
  2802. regenPct += ch->GetMobTable().bRegenPercent;
  2803.  
  2804. for (int i=1 ; i <= 4 ; ++i)
  2805. {
  2806. if (REGEN_PECT_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
  2807. {
  2808. DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
  2809. size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val");
  2810. size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( ch->GetMapIndex(), dwDragonStoneID );
  2811.  
  2812. regenPct += (val*cnt);
  2813.  
  2814. break;
  2815. }
  2816. }
  2817.  
  2818. ch->PointChange(POINT_HP, MAX(1, (ch->GetMaxHP() * regenPct) / 100));
  2819. }
  2820. else if (!ch->IsDoor())
  2821. {
  2822. ch->MonsterLog("HP_REGEN +%d", MAX(1, (ch->GetMaxHP() * ch->GetMobTable().bRegenPercent) / 100));
  2823. ch->PointChange(POINT_HP, MAX(1, (ch->GetMaxHP() * ch->GetMobTable().bRegenPercent) / 100));
  2824. }
  2825.  
  2826. if (ch->GetHP() >= ch->GetMaxHP())
  2827. {
  2828. ch->m_pkRecoveryEvent = NULL;
  2829. return 0;
  2830. }
  2831.  
  2832. if (2493 == ch->GetMobTable().dwVnum)
  2833. {
  2834. for (int i=1 ; i <= 4 ; ++i)
  2835. {
  2836. if (REGEN_TIME_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
  2837. {
  2838. DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
  2839. size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val");
  2840. size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( ch->GetMapIndex(), dwDragonStoneID );
  2841.  
  2842. return PASSES_PER_SEC(MAX(1, (ch->GetMobTable().bRegenCycle - (val*cnt))));
  2843. }
  2844. }
  2845. }
  2846.  
  2847. return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle));
  2848. }
  2849. else
  2850. {
  2851. //
  2852. // PC 회복
  2853. //
  2854. ch->CheckTarget();
  2855. //ch->UpdateSectree(); // 여기서 이걸 왜하지?
  2856. ch->UpdateKillerMode();
  2857.  
  2858. if (ch->IsAffectFlag(AFF_POISON) == true)
  2859. {
  2860. // 중독인 경우 자동회복 금지
  2861. // 파법술인 경우 자동회복 금지
  2862. return 3;
  2863. }
  2864.  
  2865. int iSec = (get_dword_time() - ch->GetLastMoveTime()) / 3000;
  2866.  
  2867. // SP 회복 루틴.
  2868. // 왜 이걸로 해서 함수로 빼놨는가 ?!
  2869. ch->DistributeSP(ch);
  2870.  
  2871. if (ch->GetMaxHP() <= ch->GetHP())
  2872. return PASSES_PER_SEC(3);
  2873.  
  2874. int iPercent = 0;
  2875. int iAmount = 0;
  2876.  
  2877. {
  2878. iPercent = aiRecoveryPercents[MIN(9, iSec)];
  2879. iAmount = 15 + (ch->GetMaxHP() * iPercent) / 100;
  2880. }
  2881.  
  2882. iAmount += (iAmount * ch->GetPoint(POINT_HP_REGEN)) / 100;
  2883.  
  2884. sys_log(1, "RECOVERY_EVENT: %s %d HP_REGEN %d HP +%d", ch->GetName(), iPercent, ch->GetPoint(POINT_HP_REGEN), iAmount);
  2885.  
  2886. ch->PointChange(POINT_HP, iAmount, false);
  2887. return PASSES_PER_SEC(3);
  2888. }
  2889. }
  2890.  
  2891. void CHARACTER::StartRecoveryEvent()
  2892. {
  2893. if (m_pkRecoveryEvent)
  2894. return;
  2895.  
  2896. if (IsDead() || IsStun())
  2897. return;
  2898.  
  2899. if (IsNPC() && GetHP() >= GetMaxHP()) // 몬스터는 체력이 다 차있으면 시작 안한다.
  2900. return;
  2901.  
  2902. char_event_info* info = AllocEventInfo<char_event_info>();
  2903.  
  2904. info->ch = this;
  2905.  
  2906. int iSec = IsPC() ? 3 : (MAX(1, GetMobTable().bRegenCycle));
  2907. m_pkRecoveryEvent = event_create(recovery_event, info, PASSES_PER_SEC(iSec));
  2908. }
  2909.  
  2910. void CHARACTER::Standup()
  2911. {
  2912. struct packet_position pack_position;
  2913.  
  2914. if (!IsPosition(POS_SITTING))
  2915. return;
  2916.  
  2917. SetPosition(POS_STANDING);
  2918.  
  2919. sys_log(1, "STANDUP: %s", GetName());
  2920.  
  2921. pack_position.header = HEADER_GC_CHARACTER_POSITION;
  2922. pack_position.vid = GetVID();
  2923. pack_position.position = POSITION_GENERAL;
  2924.  
  2925. PacketAround(&pack_position, sizeof(pack_position));
  2926. }
  2927.  
  2928. void CHARACTER::Sitdown(int is_ground)
  2929. {
  2930. struct packet_position pack_position;
  2931.  
  2932. if (IsPosition(POS_SITTING))
  2933. return;
  2934.  
  2935. SetPosition(POS_SITTING);
  2936. sys_log(1, "SITDOWN: %s", GetName());
  2937.  
  2938. pack_position.header = HEADER_GC_CHARACTER_POSITION;
  2939. pack_position.vid = GetVID();
  2940. pack_position.position = POSITION_SITTING_GROUND;
  2941. PacketAround(&pack_position, sizeof(pack_position));
  2942. }
  2943.  
  2944. void CHARACTER::SetRotation(float fRot)
  2945. {
  2946. m_pointsInstant.fRot = fRot;
  2947. }
  2948.  
  2949. // x, y 방향으로 보고 선다.
  2950. void CHARACTER::SetRotationToXY(long x, long y)
  2951. {
  2952. SetRotation(GetDegreeFromPositionXY(GetX(), GetY(), x, y));
  2953. }
  2954.  
  2955. bool CHARACTER::CannotMoveByAffect() const
  2956. {
  2957. return (IsAffectFlag(AFF_STUN));
  2958. }
  2959.  
  2960. bool CHARACTER::CanMove() const
  2961. {
  2962. if (CannotMoveByAffect())
  2963. return false;
  2964.  
  2965. if (GetMyShop()) // 상점 연 상태에서는 움직일 수 없음
  2966. return false;
  2967.  
  2968. // 0.2초 전이라면 움직일 수 없다.
  2969. /*
  2970. if (get_float_time() - m_fSyncTime < 0.2f)
  2971. return false;
  2972. */
  2973. return true;
  2974. }
  2975.  
  2976. // 무조건 x, y 위치로 이동 시킨다.
  2977.  
  2978. bool CHARACTER::Sync(long x, long y)
  2979. {
  2980. if (!GetSectree())
  2981. return false;
  2982.  
  2983. LPSECTREE new_tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), x, y);
  2984.  
  2985. if (!new_tree)
  2986. {
  2987. if (!GetDesc())
  2988. {
  2989. sys_err("No tree %s %ld %ld %ld", GetName(), x, y, GetMapIndex());
  2990. Dead();
  2991. return false;
  2992. }
  2993.  
  2994. x = GetX();
  2995. y = GetY();
  2996. new_tree = GetSectree();
  2997. return false;
  2998. }
  2999.  
  3000. SetRotationToXY(x, y);
  3001. SetXYZ(x, y, 0);
  3002.  
  3003. if (GetDungeon())
  3004. {
  3005. // 던젼용 이벤트 속성 변화
  3006. int iLastEventAttr = m_iEventAttr;
  3007. m_iEventAttr = new_tree->GetEventAttribute(x, y);
  3008.  
  3009. if (m_iEventAttr != iLastEventAttr)
  3010. {
  3011. if (GetParty())
  3012. {
  3013. quest::CQuestManager::instance().AttrOut(GetParty()->GetLeaderPID(), this, iLastEventAttr);
  3014. quest::CQuestManager::instance().AttrIn(GetParty()->GetLeaderPID(), this, m_iEventAttr);
  3015. }
  3016. else
  3017. {
  3018. quest::CQuestManager::instance().AttrOut(GetPlayerID(), this, iLastEventAttr);
  3019. quest::CQuestManager::instance().AttrIn(GetPlayerID(), this, m_iEventAttr);
  3020. }
  3021. }
  3022. }
  3023.  
  3024. if (GetSectree() != new_tree)
  3025. {
  3026. if (!IsNPC())
  3027. {
  3028. SECTREEID id = new_tree->GetID();
  3029. SECTREEID old_id = GetSectree()->GetID();
  3030.  
  3031. sys_log(0, "SECTREE DIFFER: %s %dx%d was %dx%d",
  3032. GetName(),
  3033. id.coord.x,
  3034. id.coord.y,
  3035. old_id.coord.x,
  3036. old_id.coord.y);
  3037. }
  3038.  
  3039. new_tree->InsertEntity(this);
  3040. }
  3041.  
  3042. return true;
  3043. }
  3044.  
  3045. void CHARACTER::Stop()
  3046. {
  3047. if (!IsState(m_stateIdle))
  3048. MonsterLog("[IDLE] 정지");
  3049.  
  3050. GotoState(m_stateIdle);
  3051.  
  3052. m_posDest.x = m_posStart.x = GetX();
  3053. m_posDest.y = m_posStart.y = GetY();
  3054. }
  3055.  
  3056. bool CHARACTER::Goto(long x, long y)
  3057. {
  3058. // TODO 거리체크 필요
  3059. // 같은 위치면 이동할 필요 없음 (자동 성공)
  3060. if (GetX() == x && GetY() == y)
  3061. return false;
  3062.  
  3063. if (m_posDest.x == x && m_posDest.y == y)
  3064. {
  3065. if (!IsState(m_stateMove))
  3066. {
  3067. m_dwStateDuration = 4;
  3068. GotoState(m_stateMove);
  3069. }
  3070. return false;
  3071. }
  3072.  
  3073. m_posDest.x = x;
  3074. m_posDest.y = y;
  3075.  
  3076. CalculateMoveDuration();
  3077.  
  3078. m_dwStateDuration = 4;
  3079.  
  3080.  
  3081. if (!IsState(m_stateMove))
  3082. {
  3083. MonsterLog("[MOVE] %s", GetVictim() ? "대상추적" : "그냥이동");
  3084.  
  3085. if (GetVictim())
  3086. {
  3087. //MonsterChat(MONSTER_CHAT_CHASE);
  3088. MonsterChat(MONSTER_CHAT_ATTACK);
  3089. }
  3090. }
  3091.  
  3092. GotoState(m_stateMove);
  3093.  
  3094. return true;
  3095. }
  3096.  
  3097.  
  3098. DWORD CHARACTER::GetMotionMode() const
  3099. {
  3100. DWORD dwMode = MOTION_MODE_GENERAL;
  3101.  
  3102. if (IsPolymorphed())
  3103. return dwMode;
  3104.  
  3105. LPITEM pkItem;
  3106.  
  3107. if ((pkItem = GetWear(WEAR_WEAPON)))
  3108. {
  3109. switch (pkItem->GetProto()->bSubType)
  3110. {
  3111. case WEAPON_SWORD:
  3112. dwMode = MOTION_MODE_ONEHAND_SWORD;
  3113. break;
  3114.  
  3115. case WEAPON_TWO_HANDED:
  3116. dwMode = MOTION_MODE_TWOHAND_SWORD;
  3117. break;
  3118.  
  3119. case WEAPON_DAGGER:
  3120. dwMode = MOTION_MODE_DUALHAND_SWORD;
  3121. break;
  3122.  
  3123. case WEAPON_BOW:
  3124. dwMode = MOTION_MODE_BOW;
  3125. break;
  3126.  
  3127. case WEAPON_BELL:
  3128. dwMode = MOTION_MODE_BELL;
  3129. break;
  3130.  
  3131. case WEAPON_FAN:
  3132. dwMode = MOTION_MODE_FAN;
  3133. break;
  3134. }
  3135. }
  3136. return dwMode;
  3137. }
  3138.  
  3139. float CHARACTER::GetMoveMotionSpeed() const
  3140. {
  3141. DWORD dwMode = GetMotionMode();
  3142.  
  3143. const CMotion * pkMotion = NULL;
  3144.  
  3145. if (!GetMountVnum())
  3146. pkMotion = CMotionManager::instance().GetMotion(GetRaceNum(), MAKE_MOTION_KEY(dwMode, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN));
  3147. else
  3148. {
  3149. pkMotion = CMotionManager::instance().GetMotion(GetMountVnum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN));
  3150.  
  3151. if (!pkMotion)
  3152. pkMotion = CMotionManager::instance().GetMotion(GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_HORSE, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN));
  3153. }
  3154.  
  3155. if (pkMotion)
  3156. return -pkMotion->GetAccumVector().y / pkMotion->GetDuration();
  3157. else
  3158. {
  3159. sys_err("cannot find motion (name %s race %d mode %d)", GetName(), GetRaceNum(), dwMode);
  3160. return 300.0f;
  3161. }
  3162. }
  3163.  
  3164. float CHARACTER::GetMoveSpeed() const
  3165. {
  3166. return GetMoveMotionSpeed() * 10000 / CalculateDuration(GetLimitPoint(POINT_MOV_SPEED), 10000);
  3167. }
  3168.  
  3169. void CHARACTER::CalculateMoveDuration()
  3170. {
  3171. m_posStart.x = GetX();
  3172. m_posStart.y = GetY();
  3173.  
  3174. float fDist = DISTANCE_SQRT(m_posStart.x - m_posDest.x, m_posStart.y - m_posDest.y);
  3175.  
  3176. float motionSpeed = GetMoveMotionSpeed();
  3177.  
  3178. m_dwMoveDuration = CalculateDuration(GetLimitPoint(POINT_MOV_SPEED),
  3179. (int) ((fDist / motionSpeed) * 1000.0f));
  3180.  
  3181. if (IsNPC())
  3182. sys_log(1, "%s: GOTO: distance %f, spd %u, duration %u, motion speed %f pos %d %d -> %d %d",
  3183. GetName(), fDist, GetLimitPoint(POINT_MOV_SPEED), m_dwMoveDuration, motionSpeed,
  3184. m_posStart.x, m_posStart.y, m_posDest.x, m_posDest.y);
  3185.  
  3186. m_dwMoveStartTime = get_dword_time();
  3187. }
  3188.  
  3189. // x y 위치로 이동 한다. (이동할 수 있는 가 없는 가를 확인 하고 Sync 메소드로 실제 이동 한다)
  3190. // 서버는 char의 x, y 값을 바로 바꾸지만,
  3191. // 클라에서는 이전 위치에서 바꾼 x, y까지 interpolation한다.
  3192. // 걷거나 뛰는 것은 char의 m_bNowWalking에 달려있다.
  3193. // Warp를 의도한 것이라면 Show를 사용할 것.
  3194. bool CHARACTER::Move(long x, long y)
  3195. {
  3196. // 같은 위치면 이동할 필요 없음 (자동 성공)
  3197. if (GetX() == x && GetY() == y)
  3198. return true;
  3199.  
  3200. if (test_server)
  3201. if (m_bDetailLog)
  3202. sys_log(0, "%s position %u %u", GetName(), x, y);
  3203.  
  3204. OnMove();
  3205. return Sync(x, y);
  3206. }
  3207.  
  3208. void CHARACTER::SendMovePacket(BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime, int iRot)
  3209. {
  3210. TPacketGCMove pack;
  3211.  
  3212. if (bFunc == FUNC_WAIT)
  3213. {
  3214. x = m_posDest.x;
  3215. y = m_posDest.y;
  3216. dwDuration = m_dwMoveDuration;
  3217. }
  3218.  
  3219. EncodeMovePacket(pack, GetVID(), bFunc, bArg, x, y, dwDuration, dwTime, iRot == -1 ? (int) GetRotation() / 5 : iRot);
  3220. PacketView(&pack, sizeof(TPacketGCMove), this);
  3221. }
  3222.  
  3223. int CHARACTER::GetRealPoint(BYTE type) const
  3224. {
  3225. return m_points.points[type];
  3226. }
  3227.  
  3228. void CHARACTER::SetRealPoint(BYTE type, int val)
  3229. {
  3230. m_points.points[type] = val;
  3231. }
  3232.  
  3233. int CHARACTER::GetPolymorphPoint(BYTE type) const
  3234. {
  3235. if (IsPolymorphed() && !IsPolyMaintainStat())
  3236. {
  3237. DWORD dwMobVnum = GetPolymorphVnum();
  3238. const CMob * pMob = CMobManager::instance().Get(dwMobVnum);
  3239. int iPower = GetPolymorphPower();
  3240.  
  3241. if (pMob)
  3242. {
  3243. switch (type)
  3244. {
  3245. case POINT_ST:
  3246. if (GetJob() == JOB_SHAMAN || GetJob() == JOB_SURA && GetSkillGroup() == 2)
  3247. return pMob->m_table.bStr * iPower / 100 + GetPoint(POINT_IQ);
  3248. return pMob->m_table.bStr * iPower / 100 + GetPoint(POINT_ST);
  3249.  
  3250. case POINT_HT:
  3251. return pMob->m_table.bCon * iPower / 100 + GetPoint(POINT_HT);
  3252.  
  3253. case POINT_IQ:
  3254. return pMob->m_table.bInt * iPower / 100 + GetPoint(POINT_IQ);
  3255.  
  3256. case POINT_DX:
  3257. return pMob->m_table.bDex * iPower / 100 + GetPoint(POINT_DX);
  3258. }
  3259. }
  3260. }
  3261.  
  3262. return GetPoint(type);
  3263. }
  3264.  
  3265. int CHARACTER::GetPoint(BYTE type) const
  3266. {
  3267. if (type >= POINT_MAX_NUM)
  3268. {
  3269. sys_err("Point type overflow (type %u)", type);
  3270. return 0;
  3271. }
  3272.  
  3273. int val = m_pointsInstant.points[type];
  3274. int max_val = INT_MAX;
  3275.  
  3276. switch (type)
  3277. {
  3278. case POINT_STEAL_HP:
  3279. case POINT_STEAL_SP:
  3280. max_val = 50;
  3281. break;
  3282. }
  3283.  
  3284. if (val > max_val)
  3285. sys_err("POINT_ERROR: %s type %d val %d (max: %d)", GetName(), val, max_val);
  3286.  
  3287. return (val);
  3288. }
  3289.  
  3290. int CHARACTER::GetLimitPoint(BYTE type) const
  3291. {
  3292. if (type >= POINT_MAX_NUM)
  3293. {
  3294. sys_err("Point type overflow (type %u)", type);
  3295. return 0;
  3296. }
  3297.  
  3298. int val = m_pointsInstant.points[type];
  3299. int max_val = INT_MAX;
  3300. int limit = INT_MAX;
  3301. int min_limit = -INT_MAX;
  3302.  
  3303. switch (type)
  3304. {
  3305. case POINT_ATT_SPEED:
  3306. min_limit = 0;
  3307.  
  3308. if (IsPC())
  3309. limit = 170;
  3310. else
  3311. limit = 250;
  3312. break;
  3313.  
  3314. case POINT_MOV_SPEED:
  3315. min_limit = 0;
  3316.  
  3317. if (IsPC())
  3318. limit = 200;
  3319. else
  3320. limit = 250;
  3321. break;
  3322.  
  3323. case POINT_STEAL_HP:
  3324. case POINT_STEAL_SP:
  3325. limit = 50;
  3326. max_val = 50;
  3327. break;
  3328.  
  3329. case POINT_MALL_ATTBONUS:
  3330. case POINT_MALL_DEFBONUS:
  3331. limit = 20;
  3332. max_val = 50;
  3333. break;
  3334. }
  3335.  
  3336. if (val > max_val)
  3337. sys_err("POINT_ERROR: %s type %d val %d (max: %d)", GetName(), val, max_val);
  3338.  
  3339. if (val > limit)
  3340. val = limit;
  3341.  
  3342. if (val < min_limit)
  3343. val = min_limit;
  3344.  
  3345. return (val);
  3346. }
  3347.  
  3348. void CHARACTER::SetPoint(BYTE type, int val)
  3349. {
  3350. if (type >= POINT_MAX_NUM)
  3351. {
  3352. sys_err("Point type overflow (type %u)", type);
  3353. return;
  3354. }
  3355.  
  3356. m_pointsInstant.points[type] = val;
  3357.  
  3358. // 아직 이동이 다 안끝났다면 이동 시간 계산을 다시 해야 한다.
  3359. if (type == POINT_MOV_SPEED && get_dword_time() < m_dwMoveStartTime + m_dwMoveDuration)
  3360. {
  3361. CalculateMoveDuration();
  3362. }
  3363. }
  3364.  
  3365. #ifdef __GOLD_AS_LL__
  3366. LONGLONG CHARACTER::GetAllowedGold() const
  3367. #else
  3368. INT CHARACTER::GetAllowedGold() const
  3369. #endif
  3370. {
  3371. if (GetLevel() <= 10)
  3372. return 100000;
  3373. else if (GetLevel() <= 20)
  3374. return 500000;
  3375. else
  3376. return 50000000;
  3377. }
  3378.  
  3379. void CHARACTER::CheckMaximumPoints()
  3380. {
  3381. if (GetMaxHP() < GetHP())
  3382. PointChange(POINT_HP, GetMaxHP() - GetHP());
  3383.  
  3384. if (GetMaxSP() < GetSP())
  3385. PointChange(POINT_SP, GetMaxSP() - GetSP());
  3386. }
  3387.  
  3388. #ifdef __GOLD_AS_LL__
  3389. void CHARACTER::PointChange(BYTE type, char amount, bool bAmount, bool bBroadcast) { PointChange(type, (LONGLONG)amount, bAmount, bBroadcast); }
  3390. void CHARACTER::PointChange(BYTE type, unsigned char amount, bool bAmount, bool bBroadcast) { PointChange(type, (LONGLONG)amount, bAmount, bBroadcast); }
  3391. void CHARACTER::PointChange(BYTE type, short amount, bool bAmount, bool bBroadcast) { PointChange(type, (LONGLONG)amount, bAmount, bBroadcast); }
  3392. void CHARACTER::PointChange(BYTE type, unsigned short amount, bool bAmount, bool bBroadcast) { PointChange(type, (LONGLONG)amount, bAmount, bBroadcast); }
  3393. void CHARACTER::PointChange(BYTE type, int amount, bool bAmount, bool bBroadcast) { PointChange(type, (LONGLONG)amount, bAmount, bBroadcast); }
  3394. void CHARACTER::PointChange(BYTE type, unsigned int amount, bool bAmount, bool bBroadcast) { PointChange(type, (LONGLONG)amount, bAmount, bBroadcast); }
  3395. void CHARACTER::PointChange(BYTE type, long amount, bool bAmount, bool bBroadcast) { PointChange(type, (LONGLONG)amount, bAmount, bBroadcast); }
  3396. void CHARACTER::PointChange(BYTE type, unsigned long amount, bool bAmount, bool bBroadcast) { PointChange(type, (LONGLONG)amount, bAmount, bBroadcast); }
  3397. void CHARACTER::PointChange(BYTE type, ULONGLONG amount, bool bAmount, bool bBroadcast) { PointChange(type, (LONGLONG)amount, bAmount, bBroadcast); }
  3398.  
  3399. void CHARACTER::PointChange(BYTE type, LONGLONG amount, bool bAmount, bool bBroadcast)
  3400. {
  3401. LONGLONG val = 0;
  3402. #else
  3403. void CHARACTER::PointChange(BYTE type, int amount, bool bAmount, bool bBroadcast)
  3404. {
  3405. int val = 0;
  3406. #endif
  3407.  
  3408. //sys_log(0, "PointChange %d %d | %d -> %d cHP %d mHP %d", type, amount, GetPoint(type), GetPoint(type)+amount, GetHP(), GetMaxHP());
  3409.  
  3410. switch (type)
  3411. {
  3412. case POINT_NONE:
  3413. return;
  3414.  
  3415. case POINT_LEVEL:
  3416. if (IsPC() && ((GetLevel() + amount) > gPlayerMaxLevel))
  3417. return;
  3418.  
  3419. SetLevel(GetLevel() + amount);
  3420. val = GetLevel();
  3421.  
  3422. if (IsPC())
  3423. {
  3424. sys_log(0, "LEVELUP: %s %d NEXT EXP %d", GetName(), GetLevel(), GetNextExp());
  3425.  
  3426. PointChange(POINT_NEXT_EXP, GetNextExp(), false);
  3427.  
  3428. if (amount)
  3429. {
  3430. quest::CQuestManager::instance().LevelUp(GetPlayerID());
  3431.  
  3432. LogManager::instance().LevelLog(this, val, GetRealPoint(POINT_PLAYTIME) + (get_dword_time() - m_dwPlayStartTime) / 60000);
  3433.  
  3434. if (GetGuild())
  3435. GetGuild()->LevelChange(GetPlayerID(), GetLevel());
  3436.  
  3437. if (GetParty())
  3438. GetParty()->RequestSetMemberLevel(GetPlayerID(), GetLevel());
  3439. }
  3440. }
  3441. break;
  3442.  
  3443. case POINT_NEXT_EXP:
  3444. val = GetNextExp();
  3445. bAmount = false; // 무조건 bAmount는 false 여야 한다.
  3446. break;
  3447.  
  3448. case POINT_EXP:
  3449. {
  3450. DWORD exp = GetExp();
  3451. DWORD next_exp = GetNextExp();
  3452.  
  3453. // 청소년보호
  3454. if (LC_IsNewCIBN())
  3455. {
  3456. if (IsOverTime(OT_NONE))
  3457. {
  3458. dev_log(LOG_DEB0, "<EXP_LOG> %s = NONE", GetName());
  3459. }
  3460. else if (IsOverTime(OT_3HOUR))
  3461. {
  3462. amount = (amount / 2);
  3463. dev_log(LOG_DEB0, "<EXP_LOG> %s = 3HOUR", GetName());
  3464. }
  3465. else if (IsOverTime(OT_5HOUR))
  3466. {
  3467. amount = 0;
  3468. dev_log(LOG_DEB0, "<EXP_LOG> %s = 5HOUR", GetName());
  3469. }
  3470. }
  3471.  
  3472. // exp가 0 이하로 가지 않도록 한다
  3473. if (amount < 0 && exp < -amount)
  3474. {
  3475. sys_log(1, "%s AMOUNT < 0 %d, CUR EXP: %d", GetName(), -amount, exp);
  3476. amount = -exp;
  3477.  
  3478. SetExp(exp + amount);
  3479. val = GetExp();
  3480. }
  3481. else
  3482. {
  3483. if (gPlayerMaxLevel <= GetLevel())
  3484. return;
  3485.  
  3486. if (block_exp && amount > 0)
  3487. {
  3488. return;
  3489. }
  3490.  
  3491. if (test_server)
  3492. ChatPacket(CHAT_TYPE_INFO, "You have gained %d exp.", amount);
  3493.  
  3494. DWORD iExpBalance = 0;
  3495.  
  3496. // 레벨 업!
  3497. if (exp + amount >= next_exp)
  3498. {
  3499. iExpBalance = (exp + amount) - next_exp;
  3500. amount = next_exp - exp;
  3501.  
  3502. SetExp(0);
  3503. exp = next_exp;
  3504. }
  3505. else
  3506. {
  3507. SetExp(exp + amount);
  3508. exp = GetExp();
  3509. }
  3510.  
  3511. DWORD q = DWORD(next_exp / 4.0f);
  3512. int iLevStep = GetRealPoint(POINT_LEVEL_STEP);
  3513.  
  3514. // iLevStep이 4 이상이면 레벨이 올랐어야 하므로 여기에 올 수 없는 값이다.
  3515. if (iLevStep >= 4)
  3516. {
  3517. sys_err("%s LEVEL_STEP bigger than 4! (%d)", GetName(), iLevStep);
  3518. iLevStep = 4;
  3519. }
  3520.  
  3521. if (exp >= next_exp && iLevStep < 4)
  3522. {
  3523. for (int i = 0; i < 4 - iLevStep; ++i)
  3524. PointChange(POINT_LEVEL_STEP, 1, false, true);
  3525. }
  3526. else if (exp >= q * 3 && iLevStep < 3)
  3527. {
  3528. for (int i = 0; i < 3 - iLevStep; ++i)
  3529. PointChange(POINT_LEVEL_STEP, 1, false, true);
  3530. }
  3531. else if (exp >= q * 2 && iLevStep < 2)
  3532. {
  3533. for (int i = 0; i < 2 - iLevStep; ++i)
  3534. PointChange(POINT_LEVEL_STEP, 1, false, true);
  3535. }
  3536. else if (exp >= q && iLevStep < 1)
  3537. PointChange(POINT_LEVEL_STEP, 1);
  3538.  
  3539. if (iExpBalance)
  3540. {
  3541. PointChange(POINT_EXP, iExpBalance);
  3542. }
  3543.  
  3544. val = GetExp();
  3545. }
  3546. }
  3547. break;
  3548.  
  3549. case POINT_LEVEL_STEP:
  3550. if (amount > 0)
  3551. {
  3552. val = GetPoint(POINT_LEVEL_STEP) + amount;
  3553.  
  3554. switch (val)
  3555. {
  3556. case 1:
  3557. case 2:
  3558. case 3:
  3559. //if (GetLevel() < 100) PointChange(POINT_STAT, 1);
  3560. if (GetLevel() <= g_iMaxLevelPoints) PointChange(POINT_STAT, 1);
  3561. break;
  3562.  
  3563. case 4:
  3564. {
  3565. int iHP = number(JobInitialPoints[GetJob()].hp_per_lv_begin, JobInitialPoints[GetJob()].hp_per_lv_end);
  3566. int iSP = number(JobInitialPoints[GetJob()].sp_per_lv_begin, JobInitialPoints[GetJob()].sp_per_lv_end);
  3567.  
  3568. m_points.iRandomHP += iHP;
  3569. m_points.iRandomSP += iSP;
  3570.  
  3571. if (GetSkillGroup())
  3572. {
  3573. if (GetLevel() >= 5)
  3574. PointChange(POINT_SKILL, 1);
  3575.  
  3576. if (GetLevel() >= 9)
  3577. PointChange(POINT_SUB_SKILL, 1);
  3578. }
  3579.  
  3580. PointChange(POINT_MAX_HP, iHP);
  3581. PointChange(POINT_MAX_SP, iSP);
  3582. PointChange(POINT_LEVEL, 1, false, true);
  3583.  
  3584. val = 0;
  3585. }
  3586. break;
  3587. }
  3588.  
  3589. PointChange(POINT_HP, GetMaxHP() - GetHP());
  3590. PointChange(POINT_SP, GetMaxSP() - GetSP());
  3591. PointChange(POINT_STAMINA, GetMaxStamina() - GetStamina());
  3592.  
  3593. SetPoint(POINT_LEVEL_STEP, val);
  3594. SetRealPoint(POINT_LEVEL_STEP, val);
  3595.  
  3596. Save();
  3597. }
  3598. else
  3599. val = GetPoint(POINT_LEVEL_STEP);
  3600.  
  3601. break;
  3602.  
  3603. case POINT_HP:
  3604. {
  3605. if (IsDead() || IsStun())
  3606. return;
  3607.  
  3608. int prev_hp = GetHP();
  3609.  
  3610. amount = MIN(GetMaxHP() - GetHP(), amount);
  3611. SetHP(GetHP() + amount);
  3612. val = GetHP();
  3613.  
  3614. BroadcastTargetPacket();
  3615.  
  3616. if (GetParty() && IsPC() && val != prev_hp)
  3617. GetParty()->SendPartyInfoOneToAll(this);
  3618. }
  3619. break;
  3620.  
  3621. case POINT_SP:
  3622. {
  3623. if (IsDead() || IsStun())
  3624. return;
  3625.  
  3626. amount = MIN(GetMaxSP() - GetSP(), amount);
  3627. SetSP(GetSP() + amount);
  3628. val = GetSP();
  3629. }
  3630. break;
  3631.  
  3632. case POINT_STAMINA:
  3633. {
  3634. if (IsDead() || IsStun())
  3635. return;
  3636.  
  3637. int prev_val = GetStamina();
  3638. amount = MIN(GetMaxStamina() - GetStamina(), amount);
  3639. SetStamina(GetStamina() + amount);
  3640. val = GetStamina();
  3641.  
  3642. if (val == 0)
  3643. {
  3644. // Stamina가 없으니 걷자!
  3645. SetNowWalking(true);
  3646. }
  3647. else if (prev_val == 0)
  3648. {
  3649. // 없던 스테미나가 생겼으니 이전 모드 복귀
  3650. ResetWalking();
  3651. }
  3652.  
  3653. if (amount < 0 && val != 0) // 감소는 보내지않는다.
  3654. return;
  3655. }
  3656. break;
  3657.  
  3658. case POINT_MAX_HP:
  3659. {
  3660. SetPoint(type, GetPoint(type) + amount);
  3661.  
  3662. //SetMaxHP(GetMaxHP() + amount);
  3663. // 최대 생명력 = (기본 최대 생명력 + 추가) * 최대생명력%
  3664. int hp = GetRealPoint(POINT_MAX_HP);
  3665. int add_hp = MIN(3500, hp * GetPoint(POINT_MAX_HP_PCT) / 100);
  3666. add_hp += GetPoint(POINT_MAX_HP);
  3667. add_hp += GetPoint(POINT_PARTY_TANKER_BONUS);
  3668.  
  3669. SetMaxHP(hp + add_hp);
  3670.  
  3671. val = GetMaxHP();
  3672. }
  3673. break;
  3674.  
  3675. case POINT_MAX_SP:
  3676. {
  3677. SetPoint(type, GetPoint(type) + amount);
  3678.  
  3679. //SetMaxSP(GetMaxSP() + amount);
  3680. // 최대 정신력 = (기본 최대 정신력 + 추가) * 최대정신력%
  3681. int sp = GetRealPoint(POINT_MAX_SP);
  3682. int add_sp = MIN(800, sp * GetPoint(POINT_MAX_SP_PCT) / 100);
  3683. add_sp += GetPoint(POINT_MAX_SP);
  3684. add_sp += GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);
  3685.  
  3686. SetMaxSP(sp + add_sp);
  3687.  
  3688. val = GetMaxSP();
  3689. }
  3690. break;
  3691.  
  3692. case POINT_MAX_HP_PCT:
  3693. SetPoint(type, GetPoint(type) + amount);
  3694. val = GetPoint(type);
  3695.  
  3696. PointChange(POINT_MAX_HP, 0);
  3697. break;
  3698.  
  3699. case POINT_MAX_SP_PCT:
  3700. SetPoint(type, GetPoint(type) + amount);
  3701. val = GetPoint(type);
  3702.  
  3703. PointChange(POINT_MAX_SP, 0);
  3704. break;
  3705.  
  3706. case POINT_MAX_STAMINA:
  3707. SetMaxStamina(GetMaxStamina() + amount);
  3708. val = GetMaxStamina();
  3709. break;
  3710.  
  3711. #ifdef INVENTORY_PROTECT
  3712. case ENVANTER:
  3713. {
  3714. const int64_t Envantertoplami = static_cast<int64_t>(Inventory_Protect()) + static_cast<int64_t>(amount);
  3715. if (Envantertoplami > 1 or Envantertoplami < 0)
  3716. {
  3717. sys_err("[Envanter Kilidi] SINIR HATASI isim:%s id:%u deger: %d", GetName(), GetPlayerID(), Envantertoplami);
  3718. return;
  3719. }
  3720. Set_Envanter_Kilidi(amount);
  3721. val = Inventory_Protect();
  3722. }
  3723. break;
  3724. #endif
  3725.  
  3726. case POINT_GOLD:
  3727. {
  3728. #ifdef __GOLD_AS_LL__
  3729. if ((GetGold() + amount) > g_llMaxGold) // Overflow
  3730. return;
  3731. #else
  3732. const int64_t nTotalMoney = static_cast<int64_t>(GetGold()) + static_cast<int64_t>(amount);
  3733.  
  3734. if (g_llMaxGold <= nTotalMoney)
  3735. {
  3736. sys_err("[OVERFLOW_GOLD] OriGold %d AddedGold %d id %u Name %s ", GetGold(), amount, GetPlayerID(), GetName());
  3737. LogManager::instance().CharLog(this, GetGold() + amount, "OVERFLOW_GOLD", "");
  3738. return;
  3739. }
  3740. #endif
  3741.  
  3742. SetGold(GetGold() + amount);
  3743. val = GetGold();
  3744. }
  3745. break;
  3746.  
  3747. case POINT_SKILL:
  3748. case POINT_STAT:
  3749. case POINT_SUB_SKILL:
  3750. case POINT_STAT_RESET_COUNT:
  3751. case POINT_HORSE_SKILL:
  3752. SetPoint(type, GetPoint(type) + amount);
  3753. val = GetPoint(type);
  3754.  
  3755. SetRealPoint(type, val);
  3756. break;
  3757.  
  3758. case POINT_DEF_GRADE:
  3759. SetPoint(type, GetPoint(type) + amount);
  3760. val = GetPoint(type);
  3761.  
  3762. PointChange(POINT_CLIENT_DEF_GRADE, amount);
  3763. break;
  3764.  
  3765. case POINT_CLIENT_DEF_GRADE:
  3766. SetPoint(type, GetPoint(type) + amount);
  3767. val = GetPoint(type);
  3768. break;
  3769.  
  3770. case POINT_ST:
  3771. case POINT_HT:
  3772. case POINT_DX:
  3773. case POINT_IQ:
  3774. case POINT_HP_REGEN:
  3775. case POINT_SP_REGEN:
  3776. case POINT_ATT_SPEED:
  3777. case POINT_ATT_GRADE:
  3778. case POINT_MOV_SPEED:
  3779. case POINT_CASTING_SPEED:
  3780. case POINT_MAGIC_ATT_GRADE:
  3781. case POINT_MAGIC_DEF_GRADE:
  3782. case POINT_BOW_DISTANCE:
  3783. case POINT_HP_RECOVERY:
  3784. case POINT_SP_RECOVERY:
  3785.  
  3786. case POINT_ATTBONUS_HUMAN: // 42 인간에게 강함
  3787. case POINT_ATTBONUS_ANIMAL: // 43 동물에게 데미지 % 증가
  3788. case POINT_ATTBONUS_ORC: // 44 웅귀에게 데미지 % 증가
  3789. case POINT_ATTBONUS_MILGYO: // 45 밀교에게 데미지 % 증가
  3790. case POINT_ATTBONUS_UNDEAD: // 46 시체에게 데미지 % 증가
  3791. case POINT_ATTBONUS_DEVIL: // 47 마귀(악마)에게 데미지 % 증가
  3792.  
  3793. case POINT_ATTBONUS_MONSTER:
  3794. case POINT_ATTBONUS_SURA:
  3795. case POINT_ATTBONUS_ASSASSIN:
  3796. case POINT_ATTBONUS_WARRIOR:
  3797. case POINT_ATTBONUS_SHAMAN:
  3798.  
  3799. case POINT_POISON_PCT:
  3800. case POINT_STUN_PCT:
  3801. case POINT_SLOW_PCT:
  3802.  
  3803. case POINT_BLOCK:
  3804. case POINT_DODGE:
  3805.  
  3806. case POINT_CRITICAL_PCT:
  3807. case POINT_RESIST_CRITICAL:
  3808. case POINT_PENETRATE_PCT:
  3809. case POINT_RESIST_PENETRATE:
  3810. case POINT_CURSE_PCT:
  3811.  
  3812. case POINT_STEAL_HP: // 48 생명력 흡수
  3813. case POINT_STEAL_SP: // 49 정신력 흡수
  3814.  
  3815. case POINT_MANA_BURN_PCT: // 50 마나 번
  3816. case POINT_DAMAGE_SP_RECOVER: // 51 공격당할 시 정신력 회복 확률
  3817. case POINT_RESIST_NORMAL_DAMAGE:
  3818. case POINT_RESIST_SWORD:
  3819. case POINT_RESIST_TWOHAND:
  3820. case POINT_RESIST_DAGGER:
  3821. case POINT_RESIST_BELL:
  3822. case POINT_RESIST_FAN:
  3823. case POINT_RESIST_BOW:
  3824. case POINT_RESIST_FIRE:
  3825. case POINT_RESIST_ELEC:
  3826. case POINT_RESIST_MAGIC:
  3827. case POINT_RESIST_WIND:
  3828. case POINT_RESIST_ICE:
  3829. case POINT_RESIST_EARTH:
  3830. case POINT_RESIST_DARK:
  3831. case POINT_REFLECT_MELEE: // 67 공격 반사
  3832. case POINT_REFLECT_CURSE: // 68 저주 반사
  3833. case POINT_POISON_REDUCE: // 69 독데미지 감소
  3834. case POINT_KILL_SP_RECOVER: // 70 적 소멸시 MP 회복
  3835. case POINT_KILL_HP_RECOVERY: // 75
  3836. case POINT_HIT_HP_RECOVERY:
  3837. case POINT_HIT_SP_RECOVERY:
  3838. case POINT_MANASHIELD:
  3839. case POINT_ATT_BONUS:
  3840. case POINT_DEF_BONUS:
  3841. case POINT_SKILL_DAMAGE_BONUS:
  3842. case POINT_NORMAL_HIT_DAMAGE_BONUS:
  3843.  
  3844. // DEPEND_BONUS_ATTRIBUTES
  3845. case POINT_SKILL_DEFEND_BONUS:
  3846. case POINT_NORMAL_HIT_DEFEND_BONUS:
  3847. SetPoint(type, GetPoint(type) + amount);
  3848. val = GetPoint(type);
  3849. break;
  3850. // END_OF_DEPEND_BONUS_ATTRIBUTES
  3851.  
  3852. case POINT_PARTY_ATTACKER_BONUS:
  3853. case POINT_PARTY_TANKER_BONUS:
  3854. case POINT_PARTY_BUFFER_BONUS:
  3855. case POINT_PARTY_SKILL_MASTER_BONUS:
  3856. case POINT_PARTY_HASTE_BONUS:
  3857. case POINT_PARTY_DEFENDER_BONUS:
  3858.  
  3859. case POINT_RESIST_WARRIOR :
  3860. case POINT_RESIST_ASSASSIN :
  3861. case POINT_RESIST_SURA :
  3862. case POINT_RESIST_SHAMAN :
  3863.  
  3864. SetPoint(type, GetPoint(type) + amount);
  3865. val = GetPoint(type);
  3866. break;
  3867.  
  3868. case POINT_MALL_ATTBONUS:
  3869. case POINT_MALL_DEFBONUS:
  3870. case POINT_MALL_EXPBONUS:
  3871. case POINT_MALL_ITEMBONUS:
  3872. case POINT_MALL_GOLDBONUS:
  3873. case POINT_MELEE_MAGIC_ATT_BONUS_PER:
  3874. if (GetPoint(type) + amount > 100)
  3875. {
  3876. sys_err("MALL_BONUS exceeded over 100!! point type: %d name: %s amount %d", type, GetName(), amount);
  3877. amount = 100 - GetPoint(type);
  3878. }
  3879.  
  3880. SetPoint(type, GetPoint(type) + amount);
  3881. val = GetPoint(type);
  3882. break;
  3883.  
  3884. // PC_BANG_ITEM_ADD
  3885. case POINT_PC_BANG_EXP_BONUS :
  3886. case POINT_PC_BANG_DROP_BONUS :
  3887. case POINT_RAMADAN_CANDY_BONUS_EXP:
  3888. SetPoint(type, amount);
  3889. val = GetPoint(type);
  3890. break;
  3891. // END_PC_BANG_ITEM_ADD
  3892.  
  3893. case POINT_EXP_DOUBLE_BONUS: // 71
  3894. case POINT_GOLD_DOUBLE_BONUS: // 72
  3895. case POINT_ITEM_DROP_BONUS: // 73
  3896. case POINT_POTION_BONUS: // 74
  3897. if (GetPoint(type) + amount > 100)
  3898. {
  3899. sys_err("BONUS exceeded over 100!! point type: %d name: %s amount %d", type, GetName(), amount);
  3900. amount = 100 - GetPoint(type);
  3901. }
  3902.  
  3903. SetPoint(type, GetPoint(type) + amount);
  3904. val = GetPoint(type);
  3905. break;
  3906.  
  3907. case POINT_IMMUNE_STUN: // 76
  3908. SetPoint(type, GetPoint(type) + amount);
  3909. val = GetPoint(type);
  3910. if (val)
  3911. {
  3912. SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_STUN);
  3913. }
  3914. else
  3915. {
  3916. REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_STUN);
  3917. }
  3918. break;
  3919.  
  3920. case POINT_IMMUNE_SLOW: // 77
  3921. SetPoint(type, GetPoint(type) + amount);
  3922. val = GetPoint(type);
  3923. if (val)
  3924. {
  3925. SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_SLOW);
  3926. }
  3927. else
  3928. {
  3929. REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_SLOW);
  3930. }
  3931. break;
  3932.  
  3933. case POINT_IMMUNE_FALL: // 78
  3934. SetPoint(type, GetPoint(type) + amount);
  3935. val = GetPoint(type);
  3936. if (val)
  3937. {
  3938. SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_FALL);
  3939. }
  3940. else
  3941. {
  3942. REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_FALL);
  3943. }
  3944. break;
  3945.  
  3946. case POINT_ATT_GRADE_BONUS:
  3947. SetPoint(type, GetPoint(type) + amount);
  3948. PointChange(POINT_ATT_GRADE, amount);
  3949. val = GetPoint(type);
  3950. break;
  3951.  
  3952. case POINT_DEF_GRADE_BONUS:
  3953. SetPoint(type, GetPoint(type) + amount);
  3954. PointChange(POINT_DEF_GRADE, amount);
  3955. val = GetPoint(type);
  3956. break;
  3957.  
  3958. case POINT_MAGIC_ATT_GRADE_BONUS:
  3959. SetPoint(type, GetPoint(type) + amount);
  3960. PointChange(POINT_MAGIC_ATT_GRADE, amount);
  3961. val = GetPoint(type);
  3962. break;
  3963.  
  3964. case POINT_MAGIC_DEF_GRADE_BONUS:
  3965. SetPoint(type, GetPoint(type) + amount);
  3966. PointChange(POINT_MAGIC_DEF_GRADE, amount);
  3967. val = GetPoint(type);
  3968. break;
  3969.  
  3970. case POINT_VOICE:
  3971. case POINT_EMPIRE_POINT:
  3972. //sys_err("CHARACTER::PointChange: %s: point cannot be changed. use SetPoint instead (type: %d)", GetName(), type);
  3973. val = GetRealPoint(type);
  3974. break;
  3975.  
  3976. case POINT_POLYMORPH:
  3977. SetPoint(type, GetPoint(type) + amount);
  3978. val = GetPoint(type);
  3979. SetPolymorph(val);
  3980. break;
  3981.  
  3982. case POINT_MOUNT:
  3983. SetPoint(type, GetPoint(type) + amount);
  3984. val = GetPoint(type);
  3985. MountVnum(val);
  3986. break;
  3987.  
  3988. case POINT_ENERGY:
  3989. case POINT_COSTUME_ATTR_BONUS:
  3990. {
  3991. int old_val = GetPoint(type);
  3992. SetPoint(type, old_val + amount);
  3993. val = GetPoint(type);
  3994. BuffOnAttr_ValueChange(type, old_val, val);
  3995. }
  3996. break;
  3997.  
  3998. default:
  3999. sys_err("CHARACTER::PointChange: %s: unknown point change type %d", GetName(), type);
  4000. return;
  4001. }
  4002.  
  4003. switch (type)
  4004. {
  4005. case POINT_LEVEL:
  4006. case POINT_ST:
  4007. case POINT_DX:
  4008. case POINT_IQ:
  4009. case POINT_HT:
  4010. ComputeBattlePoints();
  4011. break;
  4012. case POINT_MAX_HP:
  4013. case POINT_MAX_SP:
  4014. case POINT_MAX_STAMINA:
  4015. break;
  4016. }
  4017.  
  4018. if (type == POINT_HP && amount == 0)
  4019. return;
  4020.  
  4021.  
  4022. if (GetDesc() || bBroadcast)
  4023. {
  4024. struct packet_point_change pack;
  4025.  
  4026. pack.header = HEADER_GC_CHARACTER_POINT_CHANGE;
  4027. pack.dwVID = m_vid;
  4028. pack.type = type;
  4029. pack.value = val;
  4030.  
  4031. if (bAmount)
  4032. pack.amount = amount;
  4033. else
  4034. pack.amount = 0;
  4035.  
  4036. if (!bBroadcast)
  4037. GetDesc()->Packet(&pack, sizeof(struct packet_point_change));
  4038. else
  4039. PacketAround(&pack, sizeof(pack));
  4040. }
  4041. }
  4042.  
  4043. void CHARACTER::ApplyPoint(BYTE bApplyType, int iVal)
  4044. {
  4045. switch (bApplyType)
  4046. {
  4047. case APPLY_NONE: // 0
  4048. break;;
  4049.  
  4050. case APPLY_CON:
  4051. PointChange(POINT_HT, iVal);
  4052. PointChange(POINT_MAX_HP, (iVal * JobInitialPoints[GetJob()].hp_per_ht));
  4053. PointChange(POINT_MAX_STAMINA, (iVal * JobInitialPoints[GetJob()].stamina_per_con));
  4054. break;
  4055.  
  4056. case APPLY_INT:
  4057. PointChange(POINT_IQ, iVal);
  4058. PointChange(POINT_MAX_SP, (iVal * JobInitialPoints[GetJob()].sp_per_iq));
  4059. break;
  4060.  
  4061. case APPLY_SKILL:
  4062. // SKILL_DAMAGE_BONUS
  4063. {
  4064. // 최상위 비트 기준으로 8비트 vnum, 9비트 add, 15비트 change
  4065. // 00000000 00000000 00000000 00000000
  4066. // ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^
  4067. // vnum ^ add change
  4068. BYTE bSkillVnum = (BYTE) (((DWORD)iVal) >> 24);
  4069. int iAdd = iVal & 0x00800000;
  4070. int iChange = iVal & 0x007fffff;
  4071.  
  4072. sys_log(1, "APPLY_SKILL skill %d add? %d change %d", bSkillVnum, iAdd ? 1 : 0, iChange);
  4073.  
  4074. if (0 == iAdd)
  4075. iChange = -iChange;
  4076.  
  4077. boost::unordered_map<BYTE, int>::iterator iter = m_SkillDamageBonus.find(bSkillVnum);
  4078.  
  4079. if (iter == m_SkillDamageBonus.end())
  4080. m_SkillDamageBonus.insert(std::make_pair(bSkillVnum, iChange));
  4081. else
  4082. iter->second += iChange;
  4083. }
  4084. // END_OF_SKILL_DAMAGE_BONUS
  4085. break;
  4086.  
  4087. case APPLY_STR:
  4088. case APPLY_DEX:
  4089. case APPLY_MAX_HP:
  4090. case APPLY_MAX_SP:
  4091. case APPLY_MAX_HP_PCT:
  4092. case APPLY_MAX_SP_PCT:
  4093. case APPLY_ATT_SPEED:
  4094. case APPLY_MOV_SPEED:
  4095. case APPLY_CAST_SPEED:
  4096. case APPLY_HP_REGEN:
  4097. case APPLY_SP_REGEN:
  4098. case APPLY_POISON_PCT:
  4099. case APPLY_STUN_PCT:
  4100. case APPLY_SLOW_PCT:
  4101. case APPLY_CRITICAL_PCT:
  4102. case APPLY_PENETRATE_PCT:
  4103. case APPLY_ATTBONUS_HUMAN:
  4104. case APPLY_ATTBONUS_ANIMAL:
  4105. case APPLY_ATTBONUS_ORC:
  4106. case APPLY_ATTBONUS_MILGYO:
  4107. case APPLY_ATTBONUS_UNDEAD:
  4108. case APPLY_ATTBONUS_DEVIL:
  4109. case APPLY_ATTBONUS_WARRIOR: // 59
  4110. case APPLY_ATTBONUS_ASSASSIN: // 60
  4111. case APPLY_ATTBONUS_SURA: // 61
  4112. case APPLY_ATTBONUS_SHAMAN: // 62
  4113. case APPLY_ATTBONUS_MONSTER: // 63
  4114. case APPLY_STEAL_HP:
  4115. case APPLY_STEAL_SP:
  4116. case APPLY_MANA_BURN_PCT:
  4117. case APPLY_DAMAGE_SP_RECOVER:
  4118. case APPLY_BLOCK:
  4119. case APPLY_DODGE:
  4120. case APPLY_RESIST_SWORD:
  4121. case APPLY_RESIST_TWOHAND:
  4122. case APPLY_RESIST_DAGGER:
  4123. case APPLY_RESIST_BELL:
  4124. case APPLY_RESIST_FAN:
  4125. case APPLY_RESIST_BOW:
  4126. case APPLY_RESIST_FIRE:
  4127. case APPLY_RESIST_ELEC:
  4128. case APPLY_RESIST_MAGIC:
  4129. case APPLY_RESIST_WIND:
  4130. case APPLY_RESIST_ICE:
  4131. case APPLY_RESIST_EARTH:
  4132. case APPLY_RESIST_DARK:
  4133. case APPLY_REFLECT_MELEE:
  4134. case APPLY_REFLECT_CURSE:
  4135. case APPLY_ANTI_CRITICAL_PCT:
  4136. case APPLY_ANTI_PENETRATE_PCT:
  4137. case APPLY_POISON_REDUCE:
  4138. case APPLY_KILL_SP_RECOVER:
  4139. case APPLY_EXP_DOUBLE_BONUS:
  4140. case APPLY_GOLD_DOUBLE_BONUS:
  4141. case APPLY_ITEM_DROP_BONUS:
  4142. case APPLY_POTION_BONUS:
  4143. case APPLY_KILL_HP_RECOVER:
  4144. case APPLY_IMMUNE_STUN:
  4145. case APPLY_IMMUNE_SLOW:
  4146. case APPLY_IMMUNE_FALL:
  4147. case APPLY_BOW_DISTANCE:
  4148. case APPLY_ATT_GRADE_BONUS:
  4149. case APPLY_DEF_GRADE_BONUS:
  4150. case APPLY_MAGIC_ATT_GRADE:
  4151. case APPLY_MAGIC_DEF_GRADE:
  4152. case APPLY_CURSE_PCT:
  4153. case APPLY_MAX_STAMINA:
  4154. case APPLY_MALL_ATTBONUS:
  4155. case APPLY_MALL_DEFBONUS:
  4156. case APPLY_MALL_EXPBONUS:
  4157. case APPLY_MALL_ITEMBONUS:
  4158. case APPLY_MALL_GOLDBONUS:
  4159. case APPLY_SKILL_DAMAGE_BONUS:
  4160. case APPLY_NORMAL_HIT_DAMAGE_BONUS:
  4161.  
  4162. // DEPEND_BONUS_ATTRIBUTES
  4163. case APPLY_SKILL_DEFEND_BONUS:
  4164. case APPLY_NORMAL_HIT_DEFEND_BONUS:
  4165. // END_OF_DEPEND_BONUS_ATTRIBUTES
  4166.  
  4167. case APPLY_PC_BANG_EXP_BONUS :
  4168. case APPLY_PC_BANG_DROP_BONUS :
  4169.  
  4170. case APPLY_RESIST_WARRIOR :
  4171. case APPLY_RESIST_ASSASSIN :
  4172. case APPLY_RESIST_SURA :
  4173. case APPLY_RESIST_SHAMAN :
  4174. case APPLY_ENERGY: // 82 기력
  4175. case APPLY_DEF_GRADE: // 83 방어력. DEF_GRADE_BONUS는 클라에서 두배로 보여지는 의도된 버그(...)가 있다.
  4176. case APPLY_COSTUME_ATTR_BONUS: // 84 코스튬 아이템에 붙은 속성치 보너스
  4177. case APPLY_MAGIC_ATTBONUS_PER: // 85 마법 공격력 +x%
  4178. case APPLY_MELEE_MAGIC_ATTBONUS_PER: // 86 마법 + 밀리 공격력 +x%
  4179. PointChange(aApplyInfo[bApplyType].bPointType, iVal);
  4180. break;
  4181.  
  4182. default:
  4183. sys_err("Unknown apply type %d name %s", bApplyType, GetName());
  4184. break;
  4185. }
  4186. }
  4187.  
  4188. void CHARACTER::MotionPacketEncode(BYTE motion, LPCHARACTER victim, struct packet_motion * packet)
  4189. {
  4190. packet->header = HEADER_GC_MOTION;
  4191. packet->vid = m_vid;
  4192. packet->motion = motion;
  4193.  
  4194. if (victim)
  4195. packet->victim_vid = victim->GetVID();
  4196. else
  4197. packet->victim_vid = 0;
  4198. }
  4199.  
  4200. void CHARACTER::Motion(BYTE motion, LPCHARACTER victim)
  4201. {
  4202. struct packet_motion pack_motion;
  4203. MotionPacketEncode(motion, victim, &pack_motion);
  4204. PacketAround(&pack_motion, sizeof(struct packet_motion));
  4205. }
  4206.  
  4207. EVENTFUNC(save_event)
  4208. {
  4209. char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  4210. if ( info == NULL )
  4211. {
  4212. sys_err( "save_event> <Factor> Null pointer" );
  4213. return 0;
  4214. }
  4215.  
  4216. LPCHARACTER ch = info->ch;
  4217.  
  4218. if (ch == NULL) { // <Factor>
  4219. return 0;
  4220. }
  4221. sys_log(1, "SAVE_EVENT: %s", ch->GetName());
  4222. ch->Save();
  4223. ch->FlushDelayedSaveItem();
  4224. return (save_event_second_cycle);
  4225. }
  4226.  
  4227. void CHARACTER::StartSaveEvent()
  4228. {
  4229. if (m_pkSaveEvent)
  4230. return;
  4231.  
  4232. char_event_info* info = AllocEventInfo<char_event_info>();
  4233.  
  4234. info->ch = this;
  4235. m_pkSaveEvent = event_create(save_event, info, save_event_second_cycle);
  4236. }
  4237.  
  4238. void CHARACTER::MonsterLog(const char* format, ...)
  4239. {
  4240. if (!test_server)
  4241. return;
  4242.  
  4243. if (IsPC())
  4244. return;
  4245.  
  4246. char chatbuf[CHAT_MAX_LEN + 1];
  4247. int len = snprintf(chatbuf, sizeof(chatbuf), "(%u)", (DWORD)GetVID());
  4248.  
  4249. if (len < 0 || len >= (int) sizeof(chatbuf))
  4250. len = sizeof(chatbuf) - 1;
  4251.  
  4252. va_list args;
  4253.  
  4254. va_start(args, format);
  4255.  
  4256. int len2 = vsnprintf(chatbuf + len, sizeof(chatbuf) - len, format, args);
  4257.  
  4258. if (len2 < 0 || len2 >= (int) sizeof(chatbuf) - len)
  4259. len += (sizeof(chatbuf) - len) - 1;
  4260. else
  4261. len += len2;
  4262.  
  4263. // \0 문자 포함
  4264. ++len;
  4265.  
  4266. va_end(args);
  4267.  
  4268. TPacketGCChat pack_chat;
  4269.  
  4270. pack_chat.header = HEADER_GC_CHAT;
  4271. pack_chat.size = sizeof(TPacketGCChat) + len;
  4272. pack_chat.type = CHAT_TYPE_TALKING;
  4273. pack_chat.id = (DWORD)GetVID();
  4274. pack_chat.bEmpire = 0;
  4275.  
  4276. TEMP_BUFFER buf;
  4277. buf.write(&pack_chat, sizeof(TPacketGCChat));
  4278. buf.write(chatbuf, len);
  4279.  
  4280. CHARACTER_MANAGER::instance().PacketMonsterLog(this, buf.read_peek(), buf.size());
  4281. }
  4282.  
  4283. void CHARACTER::ChatPacket(BYTE type, const char * format, ...)
  4284. {
  4285. LPDESC d = GetDesc();
  4286.  
  4287. if (!d || !format)
  4288. return;
  4289.  
  4290. char chatbuf[CHAT_MAX_LEN + 1];
  4291. va_list args;
  4292.  
  4293. va_start(args, format);
  4294. int len = vsnprintf(chatbuf, sizeof(chatbuf), format, args);
  4295. va_end(args);
  4296.  
  4297. struct packet_chat pack_chat;
  4298.  
  4299. pack_chat.header = HEADER_GC_CHAT;
  4300. pack_chat.size = sizeof(struct packet_chat) + len;
  4301. pack_chat.type = type;
  4302. pack_chat.id = 0;
  4303. pack_chat.bEmpire = d->GetEmpire();
  4304.  
  4305. TEMP_BUFFER buf;
  4306. buf.write(&pack_chat, sizeof(struct packet_chat));
  4307. buf.write(chatbuf, len);
  4308.  
  4309. d->Packet(buf.read_peek(), buf.size());
  4310.  
  4311. if (type == CHAT_TYPE_COMMAND && test_server)
  4312. sys_log(0, "SEND_COMMAND %s %s", GetName(), chatbuf);
  4313. }
  4314.  
  4315. // MINING
  4316. void CHARACTER::mining_take()
  4317. {
  4318. m_pkMiningEvent = NULL;
  4319. }
  4320.  
  4321. void CHARACTER::mining_cancel()
  4322. {
  4323. if (m_pkMiningEvent)
  4324. {
  4325. sys_log(0, "XXX MINING CANCEL");
  4326. event_cancel(&m_pkMiningEvent);
  4327. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("채광을 중단하였습니다."));
  4328. }
  4329. }
  4330.  
  4331. void CHARACTER::mining(LPCHARACTER chLoad)
  4332. {
  4333. if (m_pkMiningEvent)
  4334. {
  4335. mining_cancel();
  4336. return;
  4337. }
  4338.  
  4339. if (!chLoad)
  4340. return;
  4341.  
  4342. if (GetMapIndex() != chLoad->GetMapIndex() || DISTANCE_APPROX(GetX() - chLoad->GetX(), GetY() - chLoad->GetY()) > 1000)
  4343. return;
  4344.  
  4345. if (mining::GetRawOreFromLoad(chLoad->GetRaceNum()) == 0)
  4346. return;
  4347.  
  4348. LPITEM pick = GetWear(WEAR_WEAPON);
  4349.  
  4350. if (!pick || pick->GetType() != ITEM_PICK)
  4351. {
  4352. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("곡괭이를 장착하세요."));
  4353. return;
  4354. }
  4355.  
  4356. int count = number(5, 15); // 동작 횟수, 한 동작당 2초
  4357.  
  4358. // 채광 동작을 보여줌
  4359. TPacketGCDigMotion p;
  4360. p.header = HEADER_GC_DIG_MOTION;
  4361. p.vid = GetVID();
  4362. p.target_vid = chLoad->GetVID();
  4363. p.count = count;
  4364.  
  4365. PacketAround(&p, sizeof(p));
  4366.  
  4367. m_pkMiningEvent = mining::CreateMiningEvent(this, chLoad, count);
  4368. }
  4369. // END_OF_MINING
  4370.  
  4371. bool CHARACTER::IsNearWater() const {
  4372. if (!GetSectree())
  4373. return false;
  4374.  
  4375. for (int x = -1; x <= 1; ++x) {
  4376. for (int y = -1; y <= 1; ++y) {
  4377. if (IS_SET(GetSectree()->GetAttribute(GetX() + x * 100, GetY() + y * 100), ATTR_WATER))
  4378. return true;
  4379. }
  4380. }
  4381.  
  4382. return false;
  4383. }
  4384. void CHARACTER::fishing()
  4385. {
  4386. if (m_pkFishingEvent)
  4387. {
  4388. fishing_take();
  4389. return;
  4390. }
  4391.  
  4392. // 못감 속성에서 낚시를 시도한다?
  4393. {
  4394. LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(GetMapIndex());
  4395.  
  4396. int x = GetX();
  4397. int y = GetY();
  4398.  
  4399. LPSECTREE tree = pkSectreeMap->Find(x, y);
  4400. DWORD dwAttr = tree->GetAttribute(x, y);
  4401.  
  4402. if (IS_SET(dwAttr, ATTR_BLOCK))
  4403. {
  4404. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("낚시를 할 수 있는 곳이 아닙니다"));
  4405. return;
  4406. }
  4407. }
  4408.  
  4409. LPITEM rod = GetWear(WEAR_WEAPON);
  4410.  
  4411. // 낚시대 장착
  4412. if (!rod || rod->GetType() != ITEM_ROD)
  4413. {
  4414. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("낚시대를 장착 하세요."));
  4415. return;
  4416. }
  4417.  
  4418. if (!IsNearWater())
  4419. return;
  4420.  
  4421. if (0 == rod->GetSocket(2))
  4422. {
  4423. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("미끼를 끼고 던져 주세요."));
  4424. return;
  4425. }
  4426.  
  4427. float fx, fy;
  4428. GetDeltaByDegree(GetRotation(), 400.0f, &fx, &fy);
  4429.  
  4430. m_pkFishingEvent = fishing::CreateFishingEvent(this);
  4431. }
  4432.  
  4433. void CHARACTER::fishing_take()
  4434. {
  4435. LPITEM rod = GetWear(WEAR_WEAPON);
  4436. if (rod && rod->GetType() == ITEM_ROD)
  4437. {
  4438. using fishing::fishing_event_info;
  4439. if (m_pkFishingEvent)
  4440. {
  4441. struct fishing_event_info* info = dynamic_cast<struct fishing_event_info*>(m_pkFishingEvent->info);
  4442.  
  4443. if (info)
  4444. fishing::Take(info, this);
  4445. }
  4446. }
  4447. else
  4448. {
  4449. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("낚시대가 아닌 물건으로 낚시를 할 수 없습니다!"));
  4450. }
  4451.  
  4452. event_cancel(&m_pkFishingEvent);
  4453. }
  4454.  
  4455. bool CHARACTER::StartStateMachine(int iNextPulse)
  4456. {
  4457. if (CHARACTER_MANAGER::instance().AddToStateList(this))
  4458. {
  4459. m_dwNextStatePulse = thecore_heart->pulse + iNextPulse;
  4460. return true;
  4461. }
  4462.  
  4463. return false;
  4464. }
  4465.  
  4466. void CHARACTER::StopStateMachine()
  4467. {
  4468. CHARACTER_MANAGER::instance().RemoveFromStateList(this);
  4469. }
  4470.  
  4471. void CHARACTER::UpdateStateMachine(DWORD dwPulse)
  4472. {
  4473. if (dwPulse < m_dwNextStatePulse)
  4474. return;
  4475.  
  4476. if (IsDead())
  4477. return;
  4478.  
  4479. Update();
  4480. m_dwNextStatePulse = dwPulse + m_dwStateDuration;
  4481. }
  4482.  
  4483. void CHARACTER::SetNextStatePulse(int iNextPulse)
  4484. {
  4485. CHARACTER_MANAGER::instance().AddToStateList(this);
  4486. m_dwNextStatePulse = iNextPulse;
  4487.  
  4488. if (iNextPulse < 10)
  4489. MonsterLog("다음상태로어서가자");
  4490. }
  4491.  
  4492.  
  4493. // 캐릭터 인스턴스 업데이트 함수.
  4494. void CHARACTER::UpdateCharacter(DWORD dwPulse)
  4495. {
  4496. CFSM::Update();
  4497. }
  4498.  
  4499. void CHARACTER::SetShop(LPSHOP pkShop)
  4500. {
  4501. if ((m_pkShop = pkShop))
  4502. SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_SHOP);
  4503. else
  4504. {
  4505. REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_SHOP);
  4506. SetShopOwner(NULL);
  4507. }
  4508. }
  4509.  
  4510. void CHARACTER::SetExchange(CExchange * pkExchange)
  4511. {
  4512. m_pkExchange = pkExchange;
  4513. }
  4514.  
  4515. void CHARACTER::SetPart(BYTE bPartPos, WORD wVal)
  4516. {
  4517. assert(bPartPos < PART_MAX_NUM);
  4518. m_pointsInstant.parts[bPartPos] = wVal;
  4519. }
  4520.  
  4521. WORD CHARACTER::GetPart(BYTE bPartPos) const
  4522. {
  4523. assert(bPartPos < PART_MAX_NUM);
  4524. return m_pointsInstant.parts[bPartPos];
  4525. }
  4526.  
  4527. WORD CHARACTER::GetOriginalPart(BYTE bPartPos) const
  4528. {
  4529. switch (bPartPos)
  4530. {
  4531. case PART_MAIN:
  4532. if (!IsPC()) // PC가 아닌 경우 현재 파트를 그대로 리턴
  4533. return GetPart(PART_MAIN);
  4534. else
  4535. return m_pointsInstant.bBasePart;
  4536.  
  4537. case PART_HAIR:
  4538. return GetPart(PART_HAIR);
  4539.  
  4540. #ifdef __SASH_SYSTEM__
  4541. case PART_SASH:
  4542. return GetPart(PART_SASH);
  4543. #endif
  4544.  
  4545. default:
  4546. return 0;
  4547. }
  4548. }
  4549.  
  4550. BYTE CHARACTER::GetCharType() const
  4551. {
  4552. return m_bCharType;
  4553. }
  4554.  
  4555. bool CHARACTER::SetSyncOwner(LPCHARACTER ch, bool bRemoveFromList)
  4556. {
  4557. // TRENT_MONSTER
  4558. if (IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE))
  4559. return false;
  4560. // END_OF_TRENT_MONSTER
  4561.  
  4562. if (ch == this)
  4563. {
  4564. sys_err("SetSyncOwner owner == this (%p)", this);
  4565. return false;
  4566. }
  4567.  
  4568. if (!ch)
  4569. {
  4570. if (bRemoveFromList && m_pkChrSyncOwner)
  4571. {
  4572. m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.remove(this);
  4573. }
  4574.  
  4575. if (m_pkChrSyncOwner)
  4576. sys_log(1, "SyncRelease %s %p from %s", GetName(), this, m_pkChrSyncOwner->GetName());
  4577.  
  4578. // 리스트에서 제거하지 않더라도 포인터는 NULL로 셋팅되어야 한다.
  4579. m_pkChrSyncOwner = NULL;
  4580. }
  4581. else
  4582. {
  4583. if (!IsSyncOwner(ch))
  4584. return false;
  4585.  
  4586. // 거리가 200 이상이면 SyncOwner가 될 수 없다.
  4587. if (DISTANCE_APPROX(GetX() - ch->GetX(), GetY() - ch->GetY()) > 250)
  4588. {
  4589. sys_log(1, "SetSyncOwner distance over than 250 %s %s", GetName(), ch->GetName());
  4590.  
  4591. // SyncOwner일 경우 Owner로 표시한다.
  4592. if (m_pkChrSyncOwner == ch)
  4593. return true;
  4594.  
  4595. return false;
  4596. }
  4597.  
  4598. if (m_pkChrSyncOwner != ch)
  4599. {
  4600. if (m_pkChrSyncOwner)
  4601. {
  4602. sys_log(1, "SyncRelease %s %p from %s", GetName(), this, m_pkChrSyncOwner->GetName());
  4603. m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.remove(this);
  4604. }
  4605.  
  4606. m_pkChrSyncOwner = ch;
  4607. m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.push_back(this);
  4608.  
  4609. // SyncOwner가 바뀌면 LastSyncTime을 초기화한다.
  4610. static const timeval zero_tv = {0, 0};
  4611. SetLastSyncTime(zero_tv);
  4612.  
  4613. sys_log(1, "SetSyncOwner set %s %p to %s", GetName(), this, ch->GetName());
  4614. }
  4615.  
  4616. m_fSyncTime = get_float_time();
  4617. }
  4618.  
  4619. //TODO: Sync will send a packet, so the Owner is still the same
  4620. //Synchronized time is more than 3 seconds when relaxing packets
  4621. //Sending the packet can be reduced.
  4622. TPacketGCOwnership pack;
  4623.  
  4624. pack.bHeader = HEADER_GC_OWNERSHIP;
  4625. pack.dwOwnerVID = ch ? ch->GetVID() : 0;
  4626. pack.dwVictimVID = GetVID();
  4627.  
  4628. PacketAround(&pack, sizeof(TPacketGCOwnership));
  4629. return true;
  4630. }
  4631.  
  4632. struct FuncClearSync
  4633. {
  4634. void operator () (LPCHARACTER ch)
  4635. {
  4636. assert(ch != NULL);
  4637. ch->SetSyncOwner(NULL, false); // false 플래그로 해야 for_each 가 제대로 돈다.
  4638. }
  4639. };
  4640.  
  4641. void CHARACTER::ClearSync()
  4642. {
  4643. SetSyncOwner(NULL);
  4644.  
  4645. // 아래 for_each에서 나를 m_pkChrSyncOwner로 가진 자들의 포인터를 NULL로 한다.
  4646. std::for_each(m_kLst_pkChrSyncOwned.begin(), m_kLst_pkChrSyncOwned.end(), FuncClearSync());
  4647. m_kLst_pkChrSyncOwned.clear();
  4648. }
  4649.  
  4650. bool CHARACTER::IsSyncOwner(LPCHARACTER ch) const
  4651. {
  4652. if (m_pkChrSyncOwner == ch)
  4653. return true;
  4654.  
  4655. // 마지막으로 동기화 된 시간이 3초 이상 지났다면 소유권이 아무에게도
  4656. // 없다. 따라서 아무나 SyncOwner이므로 true 리턴
  4657. if (get_float_time() - m_fSyncTime >= 3.0f)
  4658. return true;
  4659.  
  4660. return false;
  4661. }
  4662.  
  4663. void CHARACTER::SetParty(LPPARTY pkParty)
  4664. {
  4665. if (pkParty == m_pkParty)
  4666. return;
  4667.  
  4668. if (pkParty && m_pkParty)
  4669. sys_err("%s is trying to reassigning party (current %p, new party %p)", GetName(), get_pointer(m_pkParty), get_pointer(pkParty));
  4670.  
  4671. sys_log(1, "PARTY set to %p", get_pointer(pkParty));
  4672.  
  4673. if (m_pkDungeon && IsPC() && !pkParty)
  4674. SetDungeon(NULL);
  4675. m_pkParty = pkParty;
  4676.  
  4677. if (IsPC())
  4678. {
  4679. if (m_pkParty)
  4680. SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_PARTY);
  4681. else
  4682. REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_PARTY);
  4683.  
  4684. UpdatePacket();
  4685. }
  4686. }
  4687.  
  4688. // PARTY_JOIN_BUG_FIX
  4689. /// 파티 가입 event 정보
  4690. EVENTINFO(TPartyJoinEventInfo)
  4691. {
  4692. DWORD dwGuestPID; ///< 파티에 참여할 캐릭터의 PID
  4693. DWORD dwLeaderPID; ///< 파티 리더의 PID
  4694.  
  4695. TPartyJoinEventInfo()
  4696. : dwGuestPID( 0 )
  4697. , dwLeaderPID( 0 )
  4698. {
  4699. }
  4700. } ;
  4701.  
  4702. EVENTFUNC(party_request_event)
  4703. {
  4704. TPartyJoinEventInfo * info = dynamic_cast<TPartyJoinEventInfo *>( event->info );
  4705.  
  4706. if ( info == NULL )
  4707. {
  4708. sys_err( "party_request_event> <Factor> Null pointer" );
  4709. return 0;
  4710. }
  4711.  
  4712. LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(info->dwGuestPID);
  4713.  
  4714. if (ch)
  4715. {
  4716. sys_log(0, "PartyRequestEvent %s", ch->GetName());
  4717. ch->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied");
  4718. ch->SetPartyRequestEvent(NULL);
  4719. }
  4720.  
  4721. return 0;
  4722. }
  4723.  
  4724. bool CHARACTER::RequestToParty(LPCHARACTER leader)
  4725. {
  4726. if (leader->GetParty())
  4727. leader = leader->GetParty()->GetLeaderCharacter();
  4728.  
  4729. if (!leader)
  4730. {
  4731. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("파티장이 접속 상태가 아니라서 요청을 할 수 없습니다."));
  4732. return false;
  4733. }
  4734.  
  4735. if (m_pkPartyRequestEvent)
  4736. return false;
  4737.  
  4738. if (!IsPC() || !leader->IsPC())
  4739. return false;
  4740.  
  4741. if (leader->IsBlockMode(BLOCK_PARTY_REQUEST))
  4742. return false;
  4743.  
  4744. PartyJoinErrCode errcode = IsPartyJoinableCondition(leader, this);
  4745.  
  4746. switch (errcode)
  4747. {
  4748. case PERR_NONE:
  4749. break;
  4750.  
  4751. case PERR_SERVER:
  4752. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다."));
  4753. return false;
  4754.  
  4755. case PERR_DIFFEMPIRE:
  4756. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 다른 제국과 파티를 이룰 수 없습니다."));
  4757. return false;
  4758.  
  4759. case PERR_DUNGEON:
  4760. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티 초대를 할 수 없습니다."));
  4761. return false;
  4762.  
  4763. case PERR_OBSERVER:
  4764. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 관전 모드에선 파티 초대를 할 수 없습니다."));
  4765. return false;
  4766.  
  4767. case PERR_LVBOUNDARY:
  4768. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> -30 ~ +30 레벨 이내의 상대방만 초대할 수 있습니다."));
  4769. return false;
  4770.  
  4771. case PERR_LOWLEVEL:
  4772. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최고 레벨 보다 30레벨이 낮아 초대할 수 없습니다."));
  4773. return false;
  4774.  
  4775. case PERR_HILEVEL:
  4776. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최저 레벨 보다 30레벨이 높아 초대할 수 없습니다."));
  4777. return false;
  4778.  
  4779. case PERR_ALREADYJOIN:
  4780. return false;
  4781.  
  4782. case PERR_PARTYISFULL:
  4783. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 더 이상 파티원을 초대할 수 없습니다."));
  4784. return false;
  4785.  
  4786. default:
  4787. sys_err("Do not process party join error(%d)", errcode);
  4788. return false;
  4789. }
  4790.  
  4791. TPartyJoinEventInfo* info = AllocEventInfo<TPartyJoinEventInfo>();
  4792.  
  4793. info->dwGuestPID = GetPlayerID();
  4794. info->dwLeaderPID = leader->GetPlayerID();
  4795.  
  4796. SetPartyRequestEvent(event_create(party_request_event, info, PASSES_PER_SEC(10)));
  4797.  
  4798. leader->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequest %u", (DWORD) GetVID());
  4799. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 님에게 파티가입 신청을 했습니다."), leader->GetName());
  4800. return true;
  4801. }
  4802.  
  4803. void CHARACTER::DenyToParty(LPCHARACTER member)
  4804. {
  4805. sys_log(1, "DenyToParty %s member %s %p", GetName(), member->GetName(), get_pointer(member->m_pkPartyRequestEvent));
  4806.  
  4807. if (!member->m_pkPartyRequestEvent)
  4808. return;
  4809.  
  4810. TPartyJoinEventInfo * info = dynamic_cast<TPartyJoinEventInfo *>(member->m_pkPartyRequestEvent->info);
  4811.  
  4812. if (!info)
  4813. {
  4814. sys_err( "CHARACTER::DenyToParty> <Factor> Null pointer" );
  4815. return;
  4816. }
  4817.  
  4818. if (info->dwGuestPID != member->GetPlayerID())
  4819. return;
  4820.  
  4821. if (info->dwLeaderPID != GetPlayerID())
  4822. return;
  4823.  
  4824. event_cancel(&member->m_pkPartyRequestEvent);
  4825.  
  4826. member->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied");
  4827. }
  4828.  
  4829. void CHARACTER::AcceptToParty(LPCHARACTER member)
  4830. {
  4831. sys_log(1, "AcceptToParty %s member %s %p", GetName(), member->GetName(), get_pointer(member->m_pkPartyRequestEvent));
  4832.  
  4833. if (!member->m_pkPartyRequestEvent)
  4834. return;
  4835.  
  4836. TPartyJoinEventInfo * info = dynamic_cast<TPartyJoinEventInfo *>(member->m_pkPartyRequestEvent->info);
  4837.  
  4838. if (!info)
  4839. {
  4840. sys_err( "CHARACTER::AcceptToParty> <Factor> Null pointer" );
  4841. return;
  4842. }
  4843.  
  4844. if (info->dwGuestPID != member->GetPlayerID())
  4845. return;
  4846.  
  4847. if (info->dwLeaderPID != GetPlayerID())
  4848. return;
  4849.  
  4850. event_cancel(&member->m_pkPartyRequestEvent);
  4851.  
  4852. if (!GetParty())
  4853. member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 파티에 속해있지 않습니다."));
  4854. else
  4855. {
  4856. if (GetPlayerID() != GetParty()->GetLeaderPID())
  4857. return;
  4858.  
  4859. PartyJoinErrCode errcode = IsPartyJoinableCondition(this, member);
  4860. switch (errcode)
  4861. {
  4862. case PERR_NONE: member->PartyJoin(this); return;
  4863. case PERR_SERVER: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다.")); break;
  4864. case PERR_DUNGEON: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티 초대를 할 수 없습니다.")); break;
  4865. case PERR_OBSERVER: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 관전 모드에선 파티 초대를 할 수 없습니다.")); break;
  4866. case PERR_LVBOUNDARY: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> -30 ~ +30 레벨 이내의 상대방만 초대할 수 있습니다.")); break;
  4867. case PERR_LOWLEVEL: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최고 레벨 보다 30레벨이 낮아 초대할 수 없습니다.")); break;
  4868. case PERR_HILEVEL: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최저 레벨 보다 30레벨이 높아 초대할 수 없습니다.")); break;
  4869. case PERR_ALREADYJOIN: break;
  4870. case PERR_PARTYISFULL: {
  4871. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 더 이상 파티원을 초대할 수 없습니다."));
  4872. member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티의 인원제한이 초과하여 파티에 참가할 수 없습니다."));
  4873. break;
  4874. }
  4875. default: sys_err("Do not process party join error(%d)", errcode);
  4876. }
  4877. }
  4878.  
  4879. member->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied");
  4880. }
  4881.  
  4882. /**
  4883. * 파티 초대 event callback 함수.
  4884. * event 가 발동하면 초대 거절로 처리한다.
  4885. */
  4886. EVENTFUNC(party_invite_event)
  4887. {
  4888. TPartyJoinEventInfo * pInfo = dynamic_cast<TPartyJoinEventInfo *>( event->info );
  4889.  
  4890. if ( pInfo == NULL )
  4891. {
  4892. sys_err( "party_invite_event> <Factor> Null pointer" );
  4893. return 0;
  4894. }
  4895.  
  4896. LPCHARACTER pchInviter = CHARACTER_MANAGER::instance().FindByPID(pInfo->dwLeaderPID);
  4897.  
  4898. if (pchInviter)
  4899. {
  4900. sys_log(1, "PartyInviteEvent %s", pchInviter->GetName());
  4901. pchInviter->PartyInviteDeny(pInfo->dwGuestPID);
  4902. }
  4903.  
  4904. return 0;
  4905. }
  4906.  
  4907. void CHARACTER::PartyInvite(LPCHARACTER pchInvitee)
  4908. {
  4909. if (GetParty() && GetParty()->GetLeaderPID() != GetPlayerID())
  4910. {
  4911. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티원을 초대할 수 있는 권한이 없습니다."));
  4912. return;
  4913. }
  4914. else if (pchInvitee->IsBlockMode(BLOCK_PARTY_INVITE))
  4915. {
  4916. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> %s 님이 파티 거부 상태입니다."), pchInvitee->GetName());
  4917. return;
  4918. }
  4919.  
  4920. else if (IsGM() == true && pchInvitee->IsGM() == false)
  4921. {
  4922. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Party> You cannot send a party invitation to a player!"));
  4923. return;
  4924. }
  4925.  
  4926. else if (IsGM() == false && pchInvitee->IsGM() == true)
  4927. {
  4928. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Party> You cannot send a party invitation to a GameMaster!"));
  4929. return;
  4930. }
  4931.  
  4932. PartyJoinErrCode errcode = IsPartyJoinableCondition(this, pchInvitee);
  4933.  
  4934. switch (errcode)
  4935. {
  4936. case PERR_NONE:
  4937. break;
  4938.  
  4939. case PERR_SERVER:
  4940. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다."));
  4941. return;
  4942.  
  4943. case PERR_DIFFEMPIRE:
  4944. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 다른 제국과 파티를 이룰 수 없습니다."));
  4945. return;
  4946.  
  4947. case PERR_DUNGEON:
  4948. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티 초대를 할 수 없습니다."));
  4949. return;
  4950.  
  4951. case PERR_OBSERVER:
  4952. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 관전 모드에선 파티 초대를 할 수 없습니다."));
  4953. return;
  4954.  
  4955. case PERR_LVBOUNDARY:
  4956. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> -30 ~ +30 레벨 이내의 상대방만 초대할 수 있습니다."));
  4957. return;
  4958.  
  4959. case PERR_LOWLEVEL:
  4960. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최고 레벨 보다 30레벨이 낮아 초대할 수 없습니다."));
  4961. return;
  4962.  
  4963. case PERR_HILEVEL:
  4964. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최저 레벨 보다 30레벨이 높아 초대할 수 없습니다."));
  4965. return;
  4966.  
  4967. case PERR_ALREADYJOIN:
  4968. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 이미 %s님은 파티에 속해 있습니다."), pchInvitee->GetName());
  4969. return;
  4970.  
  4971. case PERR_PARTYISFULL:
  4972. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 더 이상 파티원을 초대할 수 없습니다."));
  4973. return;
  4974.  
  4975. default:
  4976. sys_err("Do not process party join error(%d)", errcode);
  4977. return;
  4978. }
  4979.  
  4980. if (m_PartyInviteEventMap.end() != m_PartyInviteEventMap.find(pchInvitee->GetPlayerID()))
  4981. return;
  4982.  
  4983. //
  4984. // EventMap 에 이벤트 추가
  4985. //
  4986. TPartyJoinEventInfo* info = AllocEventInfo<TPartyJoinEventInfo>();
  4987.  
  4988. info->dwGuestPID = pchInvitee->GetPlayerID();
  4989. info->dwLeaderPID = GetPlayerID();
  4990.  
  4991. m_PartyInviteEventMap.insert(EventMap::value_type(pchInvitee->GetPlayerID(), event_create(party_invite_event, info, PASSES_PER_SEC(10))));
  4992.  
  4993. //
  4994. // 초대 받는 character 에게 초대 패킷 전송
  4995. //
  4996.  
  4997. TPacketGCPartyInvite p;
  4998. p.header = HEADER_GC_PARTY_INVITE;
  4999. p.leader_vid = GetVID();
  5000. pchInvitee->GetDesc()->Packet(&p, sizeof(p));
  5001. }
  5002.  
  5003. void CHARACTER::PartyInviteAccept(LPCHARACTER pchInvitee)
  5004. {
  5005. EventMap::iterator itFind = m_PartyInviteEventMap.find(pchInvitee->GetPlayerID());
  5006.  
  5007. if (itFind == m_PartyInviteEventMap.end())
  5008. {
  5009. sys_log(1, "PartyInviteAccept from not invited character(%s)", pchInvitee->GetName());
  5010. return;
  5011. }
  5012.  
  5013. event_cancel(&itFind->second);
  5014. m_PartyInviteEventMap.erase(itFind);
  5015.  
  5016. if (GetParty() && GetParty()->GetLeaderPID() != GetPlayerID())
  5017. {
  5018. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티원을 초대할 수 있는 권한이 없습니다."));
  5019. return;
  5020. }
  5021.  
  5022. PartyJoinErrCode errcode = IsPartyJoinableMutableCondition(this, pchInvitee);
  5023.  
  5024. switch (errcode)
  5025. {
  5026. case PERR_NONE:
  5027. break;
  5028.  
  5029. case PERR_SERVER:
  5030. pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다."));
  5031. return;
  5032.  
  5033. case PERR_DUNGEON:
  5034. pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티 초대에 응할 수 없습니다."));
  5035. return;
  5036.  
  5037. case PERR_OBSERVER:
  5038. pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 관전 모드에선 파티 초대를 할 수 없습니다."));
  5039. return;
  5040.  
  5041. case PERR_LVBOUNDARY:
  5042. pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> -30 ~ +30 레벨 이내의 상대방만 초대할 수 있습니다."));
  5043. return;
  5044.  
  5045. case PERR_LOWLEVEL:
  5046. pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최고 레벨 보다 30레벨이 낮아 초대할 수 없습니다."));
  5047. return;
  5048.  
  5049. case PERR_HILEVEL:
  5050. pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최저 레벨 보다 30레벨이 높아 초대할 수 없습니다."));
  5051. return;
  5052.  
  5053. case PERR_ALREADYJOIN:
  5054. pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티 초대에 응할 수 없습니다."));
  5055. return;
  5056.  
  5057. case PERR_PARTYISFULL:
  5058. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 더 이상 파티원을 초대할 수 없습니다."));
  5059. pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티의 인원제한이 초과하여 파티에 참가할 수 없습니다."));
  5060. return;
  5061.  
  5062. default:
  5063. sys_err("ignore party join error(%d)", errcode);
  5064. return;
  5065. }
  5066.  
  5067. //
  5068. // 파티 가입 처리
  5069. //
  5070.  
  5071. if (GetParty())
  5072. pchInvitee->PartyJoin(this);
  5073. else
  5074. {
  5075. LPPARTY pParty = CPartyManager::instance().CreateParty(this);
  5076.  
  5077. pParty->Join(pchInvitee->GetPlayerID());
  5078. pParty->Link(pchInvitee);
  5079. pParty->SendPartyInfoAllToOne(this);
  5080. }
  5081. }
  5082.  
  5083. void CHARACTER::PartyInviteDeny(DWORD dwPID)
  5084. {
  5085. EventMap::iterator itFind = m_PartyInviteEventMap.find(dwPID);
  5086.  
  5087. if (itFind == m_PartyInviteEventMap.end())
  5088. {
  5089. sys_log(1, "PartyInviteDeny to not exist event(inviter PID: %d, invitee PID: %d)", GetPlayerID(), dwPID);
  5090. return;
  5091. }
  5092.  
  5093. event_cancel(&itFind->second);
  5094. m_PartyInviteEventMap.erase(itFind);
  5095.  
  5096. LPCHARACTER pchInvitee = CHARACTER_MANAGER::instance().FindByPID(dwPID);
  5097. if (pchInvitee)
  5098. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> %s님이 파티 초대를 거절하셨습니다."), pchInvitee->GetName());
  5099. }
  5100.  
  5101. void CHARACTER::PartyJoin(LPCHARACTER pLeader)
  5102. {
  5103. pLeader->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> %s님이 파티에 참가하셨습니다."), GetName());
  5104. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> %s님의 파티에 참가하셨습니다."), pLeader->GetName());
  5105.  
  5106. pLeader->GetParty()->Join(GetPlayerID());
  5107. pLeader->GetParty()->Link(this);
  5108. }
  5109.  
  5110. CHARACTER::PartyJoinErrCode CHARACTER::IsPartyJoinableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest)
  5111. {
  5112. if (pchLeader->GetEmpire() != pchGuest->GetEmpire())
  5113. return PERR_DIFFEMPIRE;
  5114.  
  5115. return IsPartyJoinableMutableCondition(pchLeader, pchGuest);
  5116. }
  5117.  
  5118. static bool __party_can_join_by_level(LPCHARACTER leader, LPCHARACTER quest)
  5119. {
  5120. int level_limit = 30;
  5121.  
  5122. if (LC_IsCanada())
  5123. level_limit = 15;
  5124. else if (LC_IsBrazil() == true)
  5125. {
  5126. level_limit = 10;
  5127. }
  5128. else
  5129. level_limit = 30;
  5130.  
  5131. return (abs(leader->GetLevel() - quest->GetLevel()) <= level_limit);
  5132. }
  5133.  
  5134. CHARACTER::PartyJoinErrCode CHARACTER::IsPartyJoinableMutableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest)
  5135. {
  5136. if (!CPartyManager::instance().IsEnablePCParty())
  5137. return PERR_SERVER;
  5138. else if (pchLeader->GetDungeon())
  5139. return PERR_DUNGEON;
  5140. #ifdef DEATHMATCH_MODE
  5141. else if (pchLeader->GetMapIndex() == DEATMATCH_MAP_INDEX)
  5142. return PERR_DUNGEON;
  5143. #endif
  5144. else if (pchGuest->IsObserverMode())
  5145. return PERR_OBSERVER;
  5146. else if (false == __party_can_join_by_level(pchLeader, pchGuest))
  5147. return PERR_LVBOUNDARY;
  5148. else if (pchGuest->GetParty())
  5149. return PERR_ALREADYJOIN;
  5150. else if (pchLeader->GetParty())
  5151. {
  5152. if (pchLeader->GetParty()->GetMemberCount() == PARTY_MAX_MEMBER)
  5153. return PERR_PARTYISFULL;
  5154. }
  5155.  
  5156. return PERR_NONE;
  5157. }
  5158. // END_OF_PARTY_JOIN_BUG_FIX
  5159.  
  5160. void CHARACTER::SetDungeon(LPDUNGEON pkDungeon)
  5161. {
  5162. if (pkDungeon && m_pkDungeon)
  5163. sys_err("%s is trying to reassigning dungeon (current %p, new party %p)", GetName(), get_pointer(m_pkDungeon), get_pointer(pkDungeon));
  5164.  
  5165. if (m_pkDungeon == pkDungeon) {
  5166. return;
  5167. }
  5168.  
  5169. if (m_pkDungeon)
  5170. {
  5171. if (IsPC())
  5172. {
  5173. if (GetParty())
  5174. m_pkDungeon->DecPartyMember(GetParty(), this);
  5175. else
  5176. m_pkDungeon->DecMember(this);
  5177. }
  5178. else if (IsMonster() || IsStone())
  5179. {
  5180. m_pkDungeon->DecMonster();
  5181. }
  5182. }
  5183.  
  5184. m_pkDungeon = pkDungeon;
  5185.  
  5186. if (pkDungeon)
  5187. {
  5188. sys_log(0, "%s DUNGEON set to %p, PARTY is %p", GetName(), get_pointer(pkDungeon), get_pointer(m_pkParty));
  5189.  
  5190. if (IsPC())
  5191. {
  5192. if (GetParty())
  5193. m_pkDungeon->IncPartyMember(GetParty(), this);
  5194. else
  5195. m_pkDungeon->IncMember(this);
  5196. }
  5197. else if (IsMonster() || IsStone())
  5198. {
  5199. m_pkDungeon->IncMonster();
  5200. }
  5201. }
  5202. }
  5203.  
  5204. void CHARACTER::SetWarMap(CWarMap * pWarMap)
  5205. {
  5206. if (m_pWarMap)
  5207. m_pWarMap->DecMember(this);
  5208.  
  5209. m_pWarMap = pWarMap;
  5210.  
  5211. if (m_pWarMap)
  5212. m_pWarMap->IncMember(this);
  5213. }
  5214.  
  5215. void CHARACTER::SetWeddingMap(marriage::WeddingMap* pMap)
  5216. {
  5217. if (m_pWeddingMap)
  5218. m_pWeddingMap->DecMember(this);
  5219.  
  5220. m_pWeddingMap = pMap;
  5221.  
  5222. if (m_pWeddingMap)
  5223. m_pWeddingMap->IncMember(this);
  5224. }
  5225.  
  5226. void CHARACTER::SetRegen(LPREGEN pkRegen)
  5227. {
  5228. m_pkRegen = pkRegen;
  5229. if (pkRegen != NULL) {
  5230. regen_id_ = pkRegen->id;
  5231. }
  5232. m_fRegenAngle = GetRotation();
  5233. m_posRegen = GetXYZ();
  5234. }
  5235.  
  5236. bool CHARACTER::OnIdle()
  5237. {
  5238. return false;
  5239. }
  5240.  
  5241. void CHARACTER::OnMove(bool bIsAttack)
  5242. {
  5243. m_dwLastMoveTime = get_dword_time();
  5244.  
  5245. if (bIsAttack)
  5246. {
  5247. m_dwLastAttackTime = m_dwLastMoveTime;
  5248.  
  5249. if (IsAffectFlag(AFF_EUNHYUNG))
  5250. {
  5251. RemoveAffect(SKILL_EUNHYUNG);
  5252. SetAffectedEunhyung();
  5253. }
  5254. else
  5255. {
  5256. ClearAffectedEunhyung();
  5257. }
  5258.  
  5259. /*if (IsAffectFlag(AFF_JEONSIN))
  5260. RemoveAffect(SKILL_JEONSINBANGEO);*/
  5261. }
  5262.  
  5263. /*if (IsAffectFlag(AFF_GUNGON))
  5264. RemoveAffect(SKILL_GUNGON);*/
  5265.  
  5266. // MINING
  5267. mining_cancel();
  5268. // END_OF_MINING
  5269. }
  5270.  
  5271. void CHARACTER::OnClick(LPCHARACTER pkChrCauser)
  5272. {
  5273. if (!pkChrCauser)
  5274. {
  5275. sys_err("OnClick %s by NULL", GetName());
  5276. return;
  5277. }
  5278.  
  5279. DWORD vid = GetVID();
  5280. //if (test_server)
  5281. sys_log(0, "OnClick %s[vnum %d ServerUniqueID %d, pid %d] by %s", GetName(), GetRaceNum(), vid, GetPlayerID(), pkChrCauser->GetName());
  5282.  
  5283. // ??? ???? ???? ??? ? ??.
  5284. {
  5285. // ?, ??? ??? ??? ??? ? ??.
  5286. if (pkChrCauser->GetMyShop() && pkChrCauser != this)
  5287. {
  5288. if (test_server)
  5289. sys_err("OnClick Fail (%s->%s) - pc has shop", pkChrCauser->GetName(), GetName());
  5290. return;
  5291. }
  5292. }
  5293.  
  5294. // ????? ???? ??? ? ??.
  5295. {
  5296. if (pkChrCauser->GetExchange())
  5297. {
  5298. if (test_server)
  5299. sys_err("OnClick Fail (%s->%s) - pc is exchanging", pkChrCauser->GetName(), GetName());
  5300. return;
  5301. }
  5302. }
  5303.  
  5304. #ifdef OFFLINE_SHOP
  5305. if (IsPC() || IsNPC() && GetRaceNum() == 30000)
  5306. {
  5307. if (!CTargetManager::instance().GetTargetInfo(pkChrCauser->GetPlayerID(), TARGET_TYPE_VID, GetVID()) || GetRaceNum() == 30000)
  5308. #else
  5309.  
  5310. if (IsPC())
  5311. {
  5312. if (!CTargetManager::instance().GetTargetInfo(pkChrCauser->GetPlayerID(), TARGET_TYPE_VID, GetVID()))
  5313. #endif
  5314. {
  5315. // 2005.03.17.myevan.?¸°??? ??´? °???´A °ł?I ≫oAˇ ?ł¸® ±a´E?≫ ??μ?˝??˛´?.
  5316. if (GetMyShop())
  5317. {
  5318. if (pkChrCauser->IsDead() == true) return;
  5319.  
  5320. //PREVENT_TRADE_WINDOW
  5321. if (pkChrCauser == this) // ?U±a´A °ˇ´E
  5322. {
  5323. if ((GetExchange() || IsOpenSafebox() || GetShopOwner()) || IsCubeOpen())
  5324. {
  5325. pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("´?¸? °?·ˇAß(?˘°i,±ł??,≫oAˇ)?ˇ´A °ł?I≫oAˇ?≫ ≫c?eC? ?o ??˝?´?´?."));
  5326. return;
  5327. }
  5328. }
  5329. else // ´?¸? ≫c¶÷?? ?¬¸?Cß?≫¶§
  5330. {
  5331. // ?¬¸?C? ≫c¶÷?? ±ł??/?˘°i/°ł?I≫oAˇ/≫oAˇ???eAß??¶o¸e ??°ˇ
  5332. if ((pkChrCauser->GetExchange() || pkChrCauser->IsOpenSafebox() || pkChrCauser->GetMyShop() || pkChrCauser->GetShopOwner()) || pkChrCauser->IsCubeOpen())
  5333. {
  5334. pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("´?¸? °?·ˇAß(?˘°i,±ł??,≫oAˇ)?ˇ´A °ł?I≫oAˇ?≫ ≫c?eC? ?o ??˝?´?´?."));
  5335. return;
  5336. }
  5337.  
  5338. // ?¬¸?C? ´e≫o?? ±ł??/?˘°i/≫oAˇ???eAß??¶o¸e ??°ˇ
  5339. //if ((GetExchange() || IsOpenSafebox() || GetShopOwner()))
  5340. if ((GetExchange() || IsOpenSafebox() || IsCubeOpen()))
  5341. {
  5342. pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("≫o´e???? ´?¸? °?·ˇ¸| C?°i ?O´A Aß?O´?´?."));
  5343. return;
  5344. }
  5345. }
  5346. //END_PREVENT_TRADE_WINDOW
  5347. #if defined(SHOP_SEARCH)
  5348. CTargetManager::Instance().DeleteTarget(pkChrCauser->GetPlayerID(), IsPrivShop() ? GetPrivShop() : GetPlayerID(), "SHOP_SEARCH_TARGET");
  5349. #endif
  5350. #ifdef OFFLINE_SHOP
  5351. if (GetMyShop()->IsLocked())
  5352. return;
  5353. #endif
  5354. GetMyShop()->AddGuest(pkChrCauser, GetVID(), false);
  5355.  
  5356. pkChrCauser->SetShopOwner(this);
  5357. return;
  5358. }
  5359.  
  5360. if (test_server)
  5361. sys_err("%s.OnClickFailure(%s) - target is PC", pkChrCauser->GetName(), GetName());
  5362.  
  5363. return;
  5364. }
  5365. }
  5366. pkChrCauser->SetQuestNPCID(GetVID());
  5367.  
  5368. if (quest::CQuestManager::instance().Click(pkChrCauser->GetPlayerID(), this))
  5369. {
  5370. return;
  5371. }
  5372.  
  5373.  
  5374. // NPC ?? ?? ?? : ?? ?? ?
  5375. if (!IsPC())
  5376. {
  5377. if (!m_triggerOnClick.pFunc)
  5378. {
  5379. return;
  5380. }
  5381.  
  5382. m_triggerOnClick.pFunc(this, pkChrCauser);
  5383. }
  5384.  
  5385. }
  5386.  
  5387. BYTE CHARACTER::GetGMLevel() const
  5388. {
  5389. if (test_server)
  5390. return GM_IMPLEMENTOR;
  5391. return m_pointsInstant.gm_level;
  5392. }
  5393.  
  5394. void CHARACTER::SetGMLevel()
  5395. {
  5396. if (GetDesc())
  5397. {
  5398. m_pointsInstant.gm_level = gm_get_level(GetName(), GetDesc()->GetHostName(), GetDesc()->GetAccountTable().login);
  5399. }
  5400. else
  5401. {
  5402. m_pointsInstant.gm_level = GM_PLAYER;
  5403. }
  5404. }
  5405.  
  5406. BOOL CHARACTER::IsGM() const
  5407. {
  5408. if (m_pointsInstant.gm_level != GM_PLAYER)
  5409. return true;
  5410. if (test_server)
  5411. return true;
  5412. return false;
  5413. }
  5414.  
  5415. void CHARACTER::SetStone(LPCHARACTER pkChrStone)
  5416. {
  5417. m_pkChrStone = pkChrStone;
  5418.  
  5419. if (m_pkChrStone)
  5420. {
  5421. if (pkChrStone->m_set_pkChrSpawnedBy.find(this) == pkChrStone->m_set_pkChrSpawnedBy.end())
  5422. pkChrStone->m_set_pkChrSpawnedBy.insert(this);
  5423. }
  5424. }
  5425.  
  5426. struct FuncDeadSpawnedByStone
  5427. {
  5428. void operator () (LPCHARACTER ch)
  5429. {
  5430. ch->Dead(NULL);
  5431. ch->SetStone(NULL);
  5432. }
  5433. };
  5434.  
  5435. void CHARACTER::ClearStone()
  5436. {
  5437. if (!m_set_pkChrSpawnedBy.empty())
  5438. {
  5439. // 내가 스폰시킨 몬스터들을 모두 죽인다.
  5440. FuncDeadSpawnedByStone f;
  5441. std::for_each(m_set_pkChrSpawnedBy.begin(), m_set_pkChrSpawnedBy.end(), f);
  5442. m_set_pkChrSpawnedBy.clear();
  5443. }
  5444.  
  5445. if (!m_pkChrStone)
  5446. return;
  5447.  
  5448. m_pkChrStone->m_set_pkChrSpawnedBy.erase(this);
  5449. m_pkChrStone = NULL;
  5450. }
  5451.  
  5452. void CHARACTER::ClearTarget()
  5453. {
  5454. if (m_pkChrTarget)
  5455. {
  5456. m_pkChrTarget->m_set_pkChrTargetedBy.erase(this);
  5457. m_pkChrTarget = NULL;
  5458. }
  5459.  
  5460. TPacketGCTarget p;
  5461.  
  5462. p.header = HEADER_GC_TARGET;
  5463. p.dwVID = 0;
  5464. p.bHPPercent = 0;
  5465.  
  5466. CHARACTER_SET::iterator it = m_set_pkChrTargetedBy.begin();
  5467.  
  5468. while (it != m_set_pkChrTargetedBy.end())
  5469. {
  5470. LPCHARACTER pkChr = *(it++);
  5471. pkChr->m_pkChrTarget = NULL;
  5472.  
  5473. if (!pkChr->GetDesc())
  5474. {
  5475. sys_err("%s %p does not have desc", pkChr->GetName(), get_pointer(pkChr));
  5476. abort();
  5477. }
  5478.  
  5479. pkChr->GetDesc()->Packet(&p, sizeof(TPacketGCTarget));
  5480. }
  5481.  
  5482. m_set_pkChrTargetedBy.clear();
  5483. }
  5484.  
  5485. void CHARACTER::SetTarget(LPCHARACTER pkChrTarget)
  5486. {
  5487. if (m_pkChrTarget == pkChrTarget)
  5488. return;
  5489.  
  5490. // CASTLE
  5491. if (IS_CASTLE_MAP(GetMapIndex()) && !IsGM())
  5492. return;
  5493. // CASTLE
  5494.  
  5495. if (m_pkChrTarget)
  5496. m_pkChrTarget->m_set_pkChrTargetedBy.erase(this);
  5497.  
  5498. m_pkChrTarget = pkChrTarget;
  5499.  
  5500. TPacketGCTarget p;
  5501.  
  5502. p.header = HEADER_GC_TARGET;
  5503.  
  5504. if (m_pkChrTarget)
  5505. {
  5506. m_pkChrTarget->m_set_pkChrTargetedBy.insert(this);
  5507.  
  5508. p.dwVID = m_pkChrTarget->GetVID();
  5509.  
  5510. if (m_pkChrTarget->IsPC() && !m_pkChrTarget->IsPolymorphed() || m_pkChrTarget->GetMaxHP() <= 0)
  5511. p.bHPPercent = 0;
  5512. else
  5513. {
  5514. if (m_pkChrTarget->GetRaceNum() == 20101 ||
  5515. m_pkChrTarget->GetRaceNum() == 20102 ||
  5516. m_pkChrTarget->GetRaceNum() == 20103 ||
  5517. m_pkChrTarget->GetRaceNum() == 20104 ||
  5518. m_pkChrTarget->GetRaceNum() == 20105 ||
  5519. m_pkChrTarget->GetRaceNum() == 20106 ||
  5520. m_pkChrTarget->GetRaceNum() == 20107 ||
  5521. m_pkChrTarget->GetRaceNum() == 20108 ||
  5522. m_pkChrTarget->GetRaceNum() == 20109)
  5523. {
  5524. LPCHARACTER owner = m_pkChrTarget->GetVictim();
  5525.  
  5526. if (owner)
  5527. {
  5528. int iHorseHealth = owner->GetHorseHealth();
  5529. int iHorseMaxHealth = owner->GetHorseMaxHealth();
  5530.  
  5531. if (iHorseMaxHealth)
  5532. p.bHPPercent = MINMAX(0, iHorseHealth * 100 / iHorseMaxHealth, 100);
  5533. else
  5534. p.bHPPercent = 100;
  5535. }
  5536. else
  5537. p.bHPPercent = 100;
  5538. }
  5539. else
  5540. p.bHPPercent = MINMAX(0, (m_pkChrTarget->GetHP() * 100) / m_pkChrTarget->GetMaxHP(), 100);
  5541. }
  5542. }
  5543. else
  5544. {
  5545. p.dwVID = 0;
  5546. p.bHPPercent = 0;
  5547. }
  5548.  
  5549. GetDesc()->Packet(&p, sizeof(TPacketGCTarget));
  5550. }
  5551.  
  5552. void CHARACTER::BroadcastTargetPacket()
  5553. {
  5554. if (m_set_pkChrTargetedBy.empty())
  5555. return;
  5556.  
  5557. TPacketGCTarget p;
  5558.  
  5559. p.header = HEADER_GC_TARGET;
  5560. p.dwVID = GetVID();
  5561.  
  5562. if (IsPC())
  5563. p.bHPPercent = 0;
  5564. else
  5565. p.bHPPercent = MINMAX(0, (GetHP() * 100) / GetMaxHP(), 100);
  5566.  
  5567. CHARACTER_SET::iterator it = m_set_pkChrTargetedBy.begin();
  5568.  
  5569. while (it != m_set_pkChrTargetedBy.end())
  5570. {
  5571. LPCHARACTER pkChr = *it++;
  5572.  
  5573. if (!pkChr->GetDesc())
  5574. {
  5575. sys_err("%s %p does not have desc", pkChr->GetName(), get_pointer(pkChr));
  5576. abort();
  5577. }
  5578.  
  5579. pkChr->GetDesc()->Packet(&p, sizeof(TPacketGCTarget));
  5580. }
  5581. }
  5582.  
  5583. void CHARACTER::CheckTarget()
  5584. {
  5585. if (!m_pkChrTarget)
  5586. return;
  5587.  
  5588. if (DISTANCE_APPROX(GetX() - m_pkChrTarget->GetX(), GetY() - m_pkChrTarget->GetY()) >= 4800)
  5589. SetTarget(NULL);
  5590. }
  5591.  
  5592. void CHARACTER::SetWarpLocation(long lMapIndex, long x, long y)
  5593. {
  5594. m_posWarp.x = x * 100;
  5595. m_posWarp.y = y * 100;
  5596. m_lWarpMapIndex = lMapIndex;
  5597. }
  5598.  
  5599. void CHARACTER::SaveExitLocation()
  5600. {
  5601. m_posExit = GetXYZ();
  5602. m_lExitMapIndex = GetMapIndex();
  5603. }
  5604.  
  5605. void CHARACTER::ExitToSavedLocation()
  5606. {
  5607. sys_log (0, "ExitToSavedLocation");
  5608. WarpSet(m_posWarp.x, m_posWarp.y, m_lWarpMapIndex);
  5609.  
  5610. m_posExit.x = m_posExit.y = m_posExit.z = 0;
  5611. m_lExitMapIndex = 0;
  5612. }
  5613. //Channel Switch
  5614. bool CHARACTER::ChannelChange(int channel)
  5615. {
  5616. if (!IsPC())
  5617. return false;
  5618. WORD port = 0;
  5619. long x=GetX();
  5620. long y=GetY();
  5621. long lMapIndex=GetMapIndex();
  5622. std::auto_ptr<SQLMsg> pmsg(DBManager::instance().DirectQuery("SELECT port FROM common.map_list WHERE channel=%d AND map_index=%d", channel,lMapIndex));
  5623. if (pmsg->Get()->uiNumRows != 1)
  5624. {
  5625. ChatPacket(1,"Cannot find map %d on channel %d",lMapIndex,channel);
  5626. return false;
  5627. }
  5628. MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult);
  5629.  
  5630. str_to_number(port,row[0]);
  5631.  
  5632. Stop();
  5633. Save();
  5634.  
  5635. if (GetSectree())
  5636. {
  5637. GetSectree()->RemoveEntity(this);
  5638. ViewCleanup();
  5639.  
  5640. EncodeRemovePacket(this);
  5641. }
  5642.  
  5643. m_lWarpMapIndex = lMapIndex;
  5644. m_posWarp.x = x;
  5645. m_posWarp.y = y;
  5646.  
  5647. sys_log(0, "WarpSet %s %d %d current map %d target map %d port %d", GetName(), x, y, GetMapIndex(), lMapIndex,port);
  5648.  
  5649. long lAddr1;
  5650. long lMapIndex1=0;
  5651. WORD wPort1;
  5652.  
  5653. if (!CMapLocation::instance().Get(x, y, lMapIndex1, lAddr1, wPort1))
  5654. {
  5655. sys_err("cannot find map location index %d x %d y %d name %s", lMapIndex1, x, y, GetName());
  5656. return false;
  5657. }
  5658.  
  5659. TPacketGCWarp p;
  5660.  
  5661. p.bHeader = HEADER_GC_WARP;
  5662. p.lX = x;
  5663. p.lY = y;
  5664. p.lAddr = lAddr1;
  5665. p.wPort = port;
  5666. GetDesc()->Packet(&p, sizeof(TPacketGCWarp));
  5667. return true;
  5668. }
  5669. // fixme
  5670. // 지금까진 privateMapIndex 가 현재 맵 인덱스와 같은지 체크 하는 것을 외부에서 하고,
  5671. // 다르면 warpset을 불렀는데
  5672. // 이를 warpset 안으로 넣자.
  5673. bool CHARACTER::WarpSet(long x, long y, long lPrivateMapIndex)
  5674. {
  5675. if (!IsPC())
  5676. return false;
  5677.  
  5678. long lAddr;
  5679. long lMapIndex;
  5680. WORD wPort;
  5681.  
  5682. if (!CMapLocation::instance().Get(x, y, lMapIndex, lAddr, wPort))
  5683. {
  5684. sys_err("cannot find map location index %d x %d y %d name %s", lMapIndex, x, y, GetName());
  5685. return false;
  5686. }
  5687.  
  5688. //Send Supplementary Data Block if new map requires security packages in loading this map
  5689. {
  5690. long lCurAddr;
  5691. long lCurMapIndex = 0;
  5692. WORD wCurPort;
  5693.  
  5694. CMapLocation::instance().Get(GetX(), GetY(), lCurMapIndex, lCurAddr, wCurPort);
  5695.  
  5696. //do not send SDB files if char is in the same map
  5697. if( lCurMapIndex != lMapIndex )
  5698. {
  5699. const TMapRegion * rMapRgn = SECTREE_MANAGER::instance().GetMapRegion(lMapIndex);
  5700. {
  5701. DESC_MANAGER::instance().SendClientPackageSDBToLoadMap( GetDesc(), rMapRgn->strMapName.c_str() );
  5702. }
  5703. }
  5704. }
  5705.  
  5706. if (lPrivateMapIndex >= 10000)
  5707. {
  5708. if (lPrivateMapIndex / 10000 != lMapIndex)
  5709. {
  5710. sys_err("Invalid map inedx %d, must be child of %d", lPrivateMapIndex, lMapIndex);
  5711. return false;
  5712. }
  5713.  
  5714. lMapIndex = lPrivateMapIndex;
  5715. }
  5716.  
  5717. Stop();
  5718. Save();
  5719.  
  5720. if (GetSectree())
  5721. {
  5722. GetSectree()->RemoveEntity(this);
  5723. ViewCleanup();
  5724.  
  5725. EncodeRemovePacket(this);
  5726. }
  5727.  
  5728. m_lWarpMapIndex = lMapIndex;
  5729. m_posWarp.x = x;
  5730. m_posWarp.y = y;
  5731.  
  5732. sys_log(0, "WarpSet %s %d %d current map %d target map %d", GetName(), x, y, GetMapIndex(), lMapIndex);
  5733.  
  5734. TPacketGCWarp p;
  5735.  
  5736. p.bHeader = HEADER_GC_WARP;
  5737. p.lX = x;
  5738. p.lY = y;
  5739. p.lAddr = lAddr;
  5740. p.wPort = wPort;
  5741.  
  5742. GetDesc()->Packet(&p, sizeof(TPacketGCWarp));
  5743.  
  5744. //if (!LC_IsNewCIBN())
  5745. {
  5746. char buf[256];
  5747. snprintf(buf, sizeof(buf), "%s MapIdx %ld DestMapIdx%ld DestX%ld DestY%ld Empire%d", GetName(), GetMapIndex(), lPrivateMapIndex, x, y, GetEmpire());
  5748. LogManager::instance().CharLog(this, 0, "WARP", buf);
  5749. }
  5750.  
  5751. return true;
  5752. }
  5753.  
  5754. void CHARACTER::WarpEnd()
  5755. {
  5756. if (test_server)
  5757. sys_log(0, "WarpEnd %s", GetName());
  5758.  
  5759. if (m_posWarp.x == 0 && m_posWarp.y == 0)
  5760. return;
  5761.  
  5762. int index = m_lWarpMapIndex;
  5763.  
  5764. if (index > 10000)
  5765. index /= 10000;
  5766.  
  5767. if (!map_allow_find(index))
  5768. {
  5769. // 이 곳으로 워프할 수 없으므로 워프하기 전 좌표로 되돌리자.
  5770. sys_err("location %d %d not allowed to login this server", m_posWarp.x, m_posWarp.y);
  5771. GetDesc()->SetPhase(PHASE_CLOSE);
  5772. return;
  5773. }
  5774.  
  5775. sys_log(0, "WarpEnd %s %d %u %u", GetName(), m_lWarpMapIndex, m_posWarp.x, m_posWarp.y);
  5776.  
  5777. Show(m_lWarpMapIndex, m_posWarp.x, m_posWarp.y, 0);
  5778. Stop();
  5779.  
  5780. m_lWarpMapIndex = 0;
  5781. m_posWarp.x = m_posWarp.y = m_posWarp.z = 0;
  5782.  
  5783. {
  5784. // P2P Login
  5785. TPacketGGLogin p;
  5786.  
  5787. p.bHeader = HEADER_GG_LOGIN;
  5788. strlcpy(p.szName, GetName(), sizeof(p.szName));
  5789. p.dwPID = GetPlayerID();
  5790. p.bEmpire = GetEmpire();
  5791. p.lMapIndex = SECTREE_MANAGER::instance().GetMapIndex(GetX(), GetY());
  5792. p.bChannel = g_bChannel;
  5793.  
  5794. P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGLogin));
  5795. }
  5796. }
  5797.  
  5798. bool CHARACTER::Return()
  5799. {
  5800. if (!IsNPC())
  5801. return false;
  5802.  
  5803. int x, y;
  5804. /*
  5805. float fDist = DISTANCE_SQRT(m_pkMobData->m_posLastAttacked.x - GetX(), m_pkMobData->m_posLastAttacked.y - GetY());
  5806. float fx, fy;
  5807. GetDeltaByDegree(GetRotation(), fDist, &fx, &fy);
  5808. x = GetX() + (int) fx;
  5809. y = GetY() + (int) fy;
  5810. */
  5811. SetVictim(NULL);
  5812.  
  5813. x = m_pkMobInst->m_posLastAttacked.x;
  5814. y = m_pkMobInst->m_posLastAttacked.y;
  5815.  
  5816. SetRotationToXY(x, y);
  5817.  
  5818. if (!Goto(x, y))
  5819. return false;
  5820.  
  5821. SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
  5822.  
  5823. if (test_server)
  5824. sys_log(0, "%s %p 포기하고 돌아가자! %d %d", GetName(), this, x, y);
  5825.  
  5826. if (GetParty())
  5827. GetParty()->SendMessage(this, PM_RETURN, x, y);
  5828.  
  5829. return true;
  5830. }
  5831.  
  5832. bool CHARACTER::Follow(LPCHARACTER pkChr, float fMinDistance)
  5833. {
  5834. if (IsPC())
  5835. {
  5836. sys_err("CHARACTER::Follow : PC cannot use this method", GetName());
  5837. return false;
  5838. }
  5839.  
  5840. // TRENT_MONSTER
  5841. if (IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE))
  5842. {
  5843. if (pkChr->IsPC()) // 쫓아가는 상대가 PC일 때
  5844. {
  5845. // If i'm in a party. I must obey party leader's AI.
  5846. if (!GetParty() || !GetParty()->GetLeader() || GetParty()->GetLeader() == this)
  5847. {
  5848. if (get_dword_time() - m_pkMobInst->m_dwLastAttackedTime >= 15000) // 마지막으로 공격받은지 15초가 지났고
  5849. {
  5850. // 마지막 맞은 곳으로 부터 50미터 이상 차이나면 포기하고 돌아간다.
  5851. if (m_pkMobData->m_table.wAttackRange < DISTANCE_APPROX(pkChr->GetX() - GetX(), pkChr->GetY() - GetY()))
  5852. if (Return())
  5853. return true;
  5854. }
  5855. }
  5856. }
  5857. return false;
  5858. }
  5859. // END_OF_TRENT_MONSTER
  5860.  
  5861. long x = pkChr->GetX();
  5862. long y = pkChr->GetY();
  5863.  
  5864. if (pkChr->IsPC()) // 쫓아가는 상대가 PC일 때
  5865. {
  5866. // If i'm in a party. I must obey party leader's AI.
  5867. if (!GetParty() || !GetParty()->GetLeader() || GetParty()->GetLeader() == this)
  5868. {
  5869. if (get_dword_time() - m_pkMobInst->m_dwLastAttackedTime >= 15000) // 마지막으로 공격받은지 15초가 지났고
  5870. {
  5871. // 마지막 맞은 곳으로 부터 50미터 이상 차이나면 포기하고 돌아간다.
  5872. if (5000 < DISTANCE_APPROX(m_pkMobInst->m_posLastAttacked.x - GetX(), m_pkMobInst->m_posLastAttacked.y - GetY()))
  5873. if (Return())
  5874. return true;
  5875. }
  5876. }
  5877. }
  5878.  
  5879. if (IsGuardNPC())
  5880. {
  5881. if (5000 < DISTANCE_APPROX(m_pkMobInst->m_posLastAttacked.x - GetX(), m_pkMobInst->m_posLastAttacked.y - GetY()))
  5882. if (Return())
  5883. return true;
  5884. }
  5885.  
  5886. if (pkChr->IsState(pkChr->m_stateMove) &&
  5887. GetMobBattleType() != BATTLE_TYPE_RANGE &&
  5888. GetMobBattleType() != BATTLE_TYPE_MAGIC &&
  5889. !IsPet())
  5890. {
  5891. // 대상이 이동중이면 예측 이동을 한다
  5892. // 나와 상대방의 속도차와 거리로부터 만날 시간을 예상한 후
  5893. // 상대방이 그 시간까지 직선으로 이동한다고 가정하여 거기로 이동한다.
  5894. float rot = pkChr->GetRotation();
  5895. float rot_delta = GetDegreeDelta(rot, GetDegreeFromPositionXY(GetX(), GetY(), pkChr->GetX(), pkChr->GetY()));
  5896.  
  5897. float yourSpeed = pkChr->GetMoveSpeed();
  5898. float mySpeed = GetMoveSpeed();
  5899.  
  5900. float fDist = DISTANCE_SQRT(x - GetX(), y - GetY());
  5901. float fFollowSpeed = mySpeed - yourSpeed * cos(rot_delta * M_PI / 180);
  5902.  
  5903. if (fFollowSpeed >= 0.1f)
  5904. {
  5905. float fMeetTime = fDist / fFollowSpeed;
  5906. float fYourMoveEstimateX, fYourMoveEstimateY;
  5907.  
  5908. if( fMeetTime * yourSpeed <= 100000.0f )
  5909. {
  5910. GetDeltaByDegree(pkChr->GetRotation(), fMeetTime * yourSpeed, &fYourMoveEstimateX, &fYourMoveEstimateY);
  5911.  
  5912. x += (long) fYourMoveEstimateX;
  5913. y += (long) fYourMoveEstimateY;
  5914.  
  5915. float fDistNew = sqrt(((double)x - GetX())*(x-GetX())+((double)y - GetY())*(y-GetY()));
  5916. if (fDist < fDistNew)
  5917. {
  5918. x = (long)(GetX() + (x - GetX()) * fDist / fDistNew);
  5919. y = (long)(GetY() + (y - GetY()) * fDist / fDistNew);
  5920. }
  5921. }
  5922. }
  5923. }
  5924.  
  5925. // 가려는 위치를 바라봐야 한다.
  5926. SetRotationToXY(x, y);
  5927.  
  5928. float fDist = DISTANCE_SQRT(x - GetX(), y - GetY());
  5929.  
  5930. if (fDist <= fMinDistance)
  5931. return false;
  5932.  
  5933. float fx, fy;
  5934.  
  5935. if (IsChangeAttackPosition(pkChr) && GetMobRank() < MOB_RANK_BOSS)
  5936. {
  5937. // 상대방 주변 랜덤한 곳으로 이동
  5938. SetChangeAttackPositionTime();
  5939.  
  5940. int retry = 16;
  5941. int dx, dy;
  5942. int rot = (int) GetDegreeFromPositionXY(x, y, GetX(), GetY());
  5943.  
  5944. while (--retry)
  5945. {
  5946. if (fDist < 500.0f)
  5947. GetDeltaByDegree((rot + number(-90, 90) + number(-90, 90)) % 360, fMinDistance, &fx, &fy);
  5948. else
  5949. GetDeltaByDegree(number(0, 359), fMinDistance, &fx, &fy);
  5950.  
  5951. dx = x + (int) fx;
  5952. dy = y + (int) fy;
  5953.  
  5954. LPSECTREE tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), dx, dy);
  5955.  
  5956. if (NULL == tree)
  5957. break;
  5958.  
  5959. if (0 == (tree->GetAttribute(dx, dy) & (ATTR_BLOCK | ATTR_OBJECT)))
  5960. break;
  5961. }
  5962.  
  5963. //sys_log(0, "근처 어딘가로 이동 %s retry %d", GetName(), retry);
  5964. if (!Goto(dx, dy))
  5965. return false;
  5966. }
  5967. else
  5968. {
  5969. // 직선 따라가기
  5970. float fDistToGo = fDist - fMinDistance;
  5971. GetDeltaByDegree(GetRotation(), fDistToGo, &fx, &fy);
  5972.  
  5973. //sys_log(0, "직선으로 이동 %s", GetName());
  5974. if (!Goto(GetX() + (int) fx, GetY() + (int) fy))
  5975. return false;
  5976. }
  5977.  
  5978. SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
  5979. //MonsterLog("쫓아가기; %s", pkChr->GetName());
  5980. return true;
  5981. }
  5982.  
  5983. float CHARACTER::GetDistanceFromSafeboxOpen() const
  5984. {
  5985. return DISTANCE_APPROX(GetX() - m_posSafeboxOpen.x, GetY() - m_posSafeboxOpen.y);
  5986. }
  5987.  
  5988. void CHARACTER::SetSafeboxOpenPosition()
  5989. {
  5990. m_posSafeboxOpen = GetXYZ();
  5991. }
  5992.  
  5993. CSafebox * CHARACTER::GetSafebox() const
  5994. {
  5995. return m_pkSafebox;
  5996. }
  5997.  
  5998. void CHARACTER::ReqSafeboxLoad(const char* pszPassword)
  5999. {
  6000. if (!*pszPassword || strlen(pszPassword) > SAFEBOX_PASSWORD_MAX_LEN)
  6001. {
  6002. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<A¢°i> A߸øμE ¾IE£¸| AO·ACI¼I½A´I´U."));
  6003. return;
  6004. }
  6005. else if (IsGM())
  6006. {
  6007. ChatPacket(CHAT_TYPE_INFO, "GM's cannot open safebox.");
  6008. return;
  6009. }
  6010. else if (m_pkSafebox)
  6011. {
  6012. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 창고가 이미 열려있습니다."));
  6013. return;
  6014. }
  6015.  
  6016. int iPulse = thecore_pulse();
  6017.  
  6018. if (iPulse - GetSafeboxLoadTime() < PASSES_PER_SEC(10))
  6019. {
  6020. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 창고를 닫은지 10초 안에는 열 수 없습니다."));
  6021. return;
  6022. }
  6023. else if (GetDistanceFromSafeboxOpen() > 1000)
  6024. {
  6025. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 거리가 멀어서 창고를 열 수 없습니다."));
  6026. return;
  6027. }
  6028. else if (m_bOpeningSafebox)
  6029. {
  6030. sys_log(0, "Overlapped safebox load request from %s", GetName());
  6031. return;
  6032. }
  6033.  
  6034. SetSafeboxLoadTime();
  6035. m_bOpeningSafebox = true;
  6036.  
  6037. TSafeboxLoadPacket p;
  6038. p.dwID = GetDesc()->GetAccountTable().id;
  6039. strlcpy(p.szLogin, GetDesc()->GetAccountTable().login, sizeof(p.szLogin));
  6040. strlcpy(p.szPassword, pszPassword, sizeof(p.szPassword));
  6041.  
  6042. db_clientdesc->DBPacket(HEADER_GD_SAFEBOX_LOAD, GetDesc()->GetHandle(), &p, sizeof(p));
  6043. }
  6044.  
  6045. void CHARACTER::LoadSafebox(int iSize, DWORD dwGold, int iItemCount, TPlayerItem * pItems)
  6046. {
  6047. bool bLoaded = false;
  6048.  
  6049. //PREVENT_TRADE_WINDOW
  6050. SetOpenSafebox(true);
  6051. //END_PREVENT_TRADE_WINDOW
  6052.  
  6053. if (m_pkSafebox)
  6054. bLoaded = true;
  6055.  
  6056. if (!m_pkSafebox)
  6057. m_pkSafebox = M2_NEW CSafebox(this, iSize, dwGold);
  6058. else
  6059. m_pkSafebox->ChangeSize(iSize);
  6060.  
  6061. m_iSafeboxSize = iSize;
  6062.  
  6063. TPacketCGSafeboxSize p;
  6064.  
  6065. p.bHeader = HEADER_GC_SAFEBOX_SIZE;
  6066. p.bSize = iSize;
  6067.  
  6068. GetDesc()->Packet(&p, sizeof(TPacketCGSafeboxSize));
  6069.  
  6070. if (!bLoaded)
  6071. {
  6072. for (int i = 0; i < iItemCount; ++i, ++pItems)
  6073. {
  6074. if (!m_pkSafebox->IsValidPosition(pItems->pos))
  6075. continue;
  6076.  
  6077. LPITEM item = ITEM_MANAGER::instance().CreateItem(pItems->vnum, pItems->count, pItems->id);
  6078.  
  6079. if (!item)
  6080. {
  6081. sys_err("cannot create item vnum %d id %u (name: %s)", pItems->vnum, pItems->id, GetName());
  6082. continue;
  6083. }
  6084.  
  6085. item->SetSkipSave(true);
  6086. item->SetSockets(pItems->alSockets);
  6087. item->SetAttributes(pItems->aAttr);
  6088.  
  6089. #ifdef __CHANGELOOK_SYSTEM__
  6090. item->SetTransmutation(pItems->transmutation);
  6091. #endif
  6092.  
  6093. if (!m_pkSafebox->Add(pItems->pos, item))
  6094. {
  6095. M2_DESTROY_ITEM(item);
  6096. }
  6097. else
  6098. item->SetSkipSave(false);
  6099. }
  6100. }
  6101. }
  6102.  
  6103. void CHARACTER::ChangeSafeboxSize(BYTE bSize)
  6104. {
  6105. //if (!m_pkSafebox)
  6106. //return;
  6107.  
  6108. TPacketCGSafeboxSize p;
  6109.  
  6110. p.bHeader = HEADER_GC_SAFEBOX_SIZE;
  6111. p.bSize = bSize;
  6112.  
  6113. GetDesc()->Packet(&p, sizeof(TPacketCGSafeboxSize));
  6114.  
  6115. if (m_pkSafebox)
  6116. m_pkSafebox->ChangeSize(bSize);
  6117.  
  6118. m_iSafeboxSize = bSize;
  6119. }
  6120.  
  6121. void CHARACTER::CloseSafebox()
  6122. {
  6123. if (!m_pkSafebox)
  6124. return;
  6125.  
  6126. //PREVENT_TRADE_WINDOW
  6127. SetOpenSafebox(false);
  6128. //END_PREVENT_TRADE_WINDOW
  6129.  
  6130. m_pkSafebox->Save();
  6131.  
  6132. M2_DELETE(m_pkSafebox);
  6133. m_pkSafebox = NULL;
  6134.  
  6135. ChatPacket(CHAT_TYPE_COMMAND, "CloseSafebox");
  6136.  
  6137. SetSafeboxLoadTime();
  6138. m_bOpeningSafebox = false;
  6139.  
  6140. Save();
  6141. }
  6142.  
  6143. CSafebox * CHARACTER::GetMall() const
  6144. {
  6145. return m_pkMall;
  6146. }
  6147.  
  6148. void CHARACTER::LoadMall(int iItemCount, TPlayerItem * pItems)
  6149. {
  6150. bool bLoaded = false;
  6151.  
  6152. if (m_pkMall)
  6153. bLoaded = true;
  6154.  
  6155. if (!m_pkMall)
  6156. m_pkMall = M2_NEW CSafebox(this, 3 * SAFEBOX_PAGE_SIZE, 0);
  6157. else
  6158. m_pkMall->ChangeSize(3 * SAFEBOX_PAGE_SIZE);
  6159.  
  6160. m_pkMall->SetWindowMode(MALL);
  6161.  
  6162. TPacketCGSafeboxSize p;
  6163.  
  6164. p.bHeader = HEADER_GC_MALL_OPEN;
  6165. p.bSize = 3 * SAFEBOX_PAGE_SIZE;
  6166.  
  6167. GetDesc()->Packet(&p, sizeof(TPacketCGSafeboxSize));
  6168.  
  6169. if (!bLoaded)
  6170. {
  6171. for (int i = 0; i < iItemCount; ++i, ++pItems)
  6172. {
  6173. if (!m_pkMall->IsValidPosition(pItems->pos))
  6174. continue;
  6175.  
  6176. LPITEM item = ITEM_MANAGER::instance().CreateItem(pItems->vnum, pItems->count, pItems->id);
  6177.  
  6178. if (!item)
  6179. {
  6180. sys_err("cannot create item vnum %d id %u (name: %s)", pItems->vnum, pItems->id, GetName());
  6181. continue;
  6182. }
  6183.  
  6184. item->SetSkipSave(true);
  6185. item->SetSockets(pItems->alSockets);
  6186. item->SetAttributes(pItems->aAttr);
  6187. #ifdef __CHANGELOOK_SYSTEM__
  6188. item->SetTransmutation(pItems->transmutation);
  6189. #endif
  6190.  
  6191. if (!m_pkMall->Add(pItems->pos, item))
  6192. M2_DESTROY_ITEM(item);
  6193. else
  6194. item->SetSkipSave(false);
  6195. }
  6196. }
  6197. }
  6198.  
  6199. void CHARACTER::CloseMall()
  6200. {
  6201. if (!m_pkMall)
  6202. return;
  6203.  
  6204. m_pkMall->Save();
  6205.  
  6206. M2_DELETE(m_pkMall);
  6207. m_pkMall = NULL;
  6208.  
  6209. ChatPacket(CHAT_TYPE_COMMAND, "CloseMall");
  6210. }
  6211.  
  6212. bool CHARACTER::BuildUpdatePartyPacket(TPacketGCPartyUpdate & out)
  6213. {
  6214. if (!GetParty())
  6215. return false;
  6216.  
  6217. memset(&out, 0, sizeof(out));
  6218.  
  6219. out.header = HEADER_GC_PARTY_UPDATE;
  6220. out.pid = GetPlayerID();
  6221. out.percent_hp = MINMAX(0, GetHP() * 100 / GetMaxHP(), 100);
  6222. out.role = GetParty()->GetRole(GetPlayerID());
  6223.  
  6224. sys_log(1, "PARTY %s role is %d", GetName(), out.role);
  6225.  
  6226. LPCHARACTER l = GetParty()->GetLeaderCharacter();
  6227.  
  6228. if (l && DISTANCE_APPROX(GetX() - l->GetX(), GetY() - l->GetY()) < PARTY_DEFAULT_RANGE)
  6229. {
  6230. if (g_iUseLocale)
  6231. out.affects[0] = GetParty()->GetPartyBonusExpPercent();
  6232. else
  6233. out.affects[0] = GetParty()->GetExpBonusPercent();
  6234. out.affects[1] = GetPoint(POINT_PARTY_ATTACKER_BONUS);
  6235. out.affects[2] = GetPoint(POINT_PARTY_TANKER_BONUS);
  6236. out.affects[3] = GetPoint(POINT_PARTY_BUFFER_BONUS);
  6237. out.affects[4] = GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);
  6238. out.affects[5] = GetPoint(POINT_PARTY_HASTE_BONUS);
  6239. out.affects[6] = GetPoint(POINT_PARTY_DEFENDER_BONUS);
  6240. }
  6241.  
  6242. return true;
  6243. }
  6244.  
  6245. int CHARACTER::GetLeadershipSkillLevel() const
  6246. {
  6247. return GetSkillLevel(SKILL_LEADERSHIP);
  6248. }
  6249.  
  6250. void CHARACTER::QuerySafeboxSize()
  6251. {
  6252. if (m_iSafeboxSize == -1)
  6253. {
  6254. DBManager::instance().ReturnQuery(QID_SAFEBOX_SIZE,
  6255. GetPlayerID(),
  6256. NULL,
  6257. "SELECT size FROM safebox%s WHERE account_id = %u",
  6258. get_table_postfix(),
  6259. GetDesc()->GetAccountTable().id);
  6260. }
  6261. }
  6262.  
  6263. void CHARACTER::SetSafeboxSize(int iSize)
  6264. {
  6265. sys_log(1, "SetSafeboxSize: %s %d", GetName(), iSize);
  6266. m_iSafeboxSize = iSize;
  6267. DBManager::instance().Query("UPDATE safebox%s SET size = %d WHERE account_id = %u", get_table_postfix(), iSize / SAFEBOX_PAGE_SIZE, GetDesc()->GetAccountTable().id);
  6268. }
  6269.  
  6270. int CHARACTER::GetSafeboxSize() const
  6271. {
  6272. return m_iSafeboxSize;
  6273. }
  6274.  
  6275. void CHARACTER::SetNowWalking(bool bWalkFlag)
  6276. {
  6277. //if (m_bNowWalking != bWalkFlag || IsNPC())
  6278. if (m_bNowWalking != bWalkFlag)
  6279. {
  6280. if (bWalkFlag)
  6281. {
  6282. m_bNowWalking = true;
  6283. m_dwWalkStartTime = get_dword_time();
  6284. }
  6285. else
  6286. {
  6287. m_bNowWalking = false;
  6288. }
  6289.  
  6290. //if (m_bNowWalking)
  6291. {
  6292. TPacketGCWalkMode p;
  6293. p.vid = GetVID();
  6294. p.header = HEADER_GC_WALK_MODE;
  6295. p.mode = m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN;
  6296.  
  6297. PacketView(&p, sizeof(p));
  6298. }
  6299.  
  6300. if (IsNPC())
  6301. {
  6302. if (m_bNowWalking)
  6303. MonsterLog("걷는다");
  6304. else
  6305. MonsterLog("뛴다");
  6306. }
  6307.  
  6308. //sys_log(0, "%s is now %s", GetName(), m_bNowWalking?"walking.":"running.");
  6309. }
  6310. }
  6311.  
  6312. void CHARACTER::StartStaminaConsume()
  6313. {
  6314. if (m_bStaminaConsume)
  6315. return;
  6316. PointChange(POINT_STAMINA, 0);
  6317. m_bStaminaConsume = true;
  6318. //ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec, GetStamina());
  6319. if (IsStaminaHalfConsume())
  6320. ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec / 2, GetStamina());
  6321. else
  6322. ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec, GetStamina());
  6323. }
  6324.  
  6325. void CHARACTER::StopStaminaConsume()
  6326. {
  6327. if (!m_bStaminaConsume)
  6328. return;
  6329. PointChange(POINT_STAMINA, 0);
  6330. m_bStaminaConsume = false;
  6331. ChatPacket(CHAT_TYPE_COMMAND, "StopStaminaConsume %d", GetStamina());
  6332. }
  6333.  
  6334. bool CHARACTER::IsStaminaConsume() const
  6335. {
  6336. return m_bStaminaConsume;
  6337. }
  6338.  
  6339. bool CHARACTER::IsStaminaHalfConsume() const
  6340. {
  6341. return IsEquipUniqueItem(UNIQUE_ITEM_HALF_STAMINA);
  6342. }
  6343.  
  6344. void CHARACTER::ResetStopTime()
  6345. {
  6346. m_dwStopTime = get_dword_time();
  6347. }
  6348.  
  6349. DWORD CHARACTER::GetStopTime() const
  6350. {
  6351. return m_dwStopTime;
  6352. }
  6353.  
  6354. void CHARACTER::ResetPoint(int iLv)
  6355. {
  6356. BYTE bJob = GetJob();
  6357.  
  6358. PointChange(POINT_LEVEL, iLv - GetLevel());
  6359.  
  6360. SetRealPoint(POINT_ST, JobInitialPoints[bJob].st);
  6361. SetPoint(POINT_ST, GetRealPoint(POINT_ST));
  6362.  
  6363. SetRealPoint(POINT_HT, JobInitialPoints[bJob].ht);
  6364. SetPoint(POINT_HT, GetRealPoint(POINT_HT));
  6365.  
  6366. SetRealPoint(POINT_DX, JobInitialPoints[bJob].dx);
  6367. SetPoint(POINT_DX, GetRealPoint(POINT_DX));
  6368.  
  6369. SetRealPoint(POINT_IQ, JobInitialPoints[bJob].iq);
  6370. SetPoint(POINT_IQ, GetRealPoint(POINT_IQ));
  6371.  
  6372. SetRandomHP((iLv - 1) * number(JobInitialPoints[GetJob()].hp_per_lv_begin, JobInitialPoints[GetJob()].hp_per_lv_end));
  6373. SetRandomSP((iLv - 1) * number(JobInitialPoints[GetJob()].sp_per_lv_begin, JobInitialPoints[GetJob()].sp_per_lv_end));
  6374.  
  6375. //PointChange(POINT_STAT, ((MINMAX(1, iLv, 99) - 1) * 3) + GetPoint(POINT_LEVEL_STEP) - GetPoint(POINT_STAT));
  6376. if(iLv <= g_iMaxLevelPoints)
  6377. PointChange(POINT_STAT, ((MINMAX(1, iLv, g_iMaxLevelPoints) - 1) * 3) + GetPoint(POINT_LEVEL_STEP) - GetPoint(POINT_STAT));
  6378. else
  6379. PointChange(POINT_STAT, (g_iMaxLevelPoints * 3) - GetPoint(POINT_STAT));
  6380.  
  6381. ComputePoints();
  6382.  
  6383. // 회복
  6384. PointChange(POINT_HP, GetMaxHP() - GetHP());
  6385. PointChange(POINT_SP, GetMaxSP() - GetSP());
  6386.  
  6387. PointsPacket();
  6388.  
  6389. LogManager::instance().CharLog(this, 0, "RESET_POINT", "");
  6390. }
  6391.  
  6392. bool CHARACTER::IsChangeAttackPosition(LPCHARACTER target) const
  6393. {
  6394. if (!IsNPC())
  6395. return true;
  6396.  
  6397. DWORD dwChangeTime = AI_CHANGE_ATTACK_POISITION_TIME_NEAR;
  6398.  
  6399. if (DISTANCE_APPROX(GetX() - target->GetX(), GetY() - target->GetY()) >
  6400. AI_CHANGE_ATTACK_POISITION_DISTANCE + GetMobAttackRange())
  6401. dwChangeTime = AI_CHANGE_ATTACK_POISITION_TIME_FAR;
  6402.  
  6403. return get_dword_time() - m_dwLastChangeAttackPositionTime > dwChangeTime;
  6404. }
  6405.  
  6406. void CHARACTER::GiveRandomSkillBook()
  6407. {
  6408. LPITEM item = AutoGiveItem(50300);
  6409.  
  6410. if (NULL != item)
  6411. {
  6412. BYTE bJob = 0;
  6413.  
  6414. if (!number(0, 1))
  6415. bJob = GetJob() + 1;
  6416.  
  6417. DWORD dwSkillVnum = 0;
  6418.  
  6419. do
  6420. {
  6421. dwSkillVnum = number(1, 111);
  6422. const CSkillProto* pkSk = CSkillManager::instance().Get(dwSkillVnum);
  6423.  
  6424. if (NULL == pkSk)
  6425. continue;
  6426.  
  6427. if (bJob && bJob != pkSk->dwType)
  6428. continue;
  6429.  
  6430. break;
  6431. } while (true);
  6432.  
  6433. item->SetSocket(0, dwSkillVnum);
  6434. }
  6435. }
  6436.  
  6437. void CHARACTER::ReviveInvisible(int iDur)
  6438. {
  6439. AddAffect(AFFECT_REVIVE_INVISIBLE, POINT_NONE, 0, AFF_REVIVE_INVISIBLE, iDur, 0, true);
  6440. }
  6441.  
  6442. void CHARACTER::ToggleMonsterLog()
  6443. {
  6444. m_bMonsterLog = !m_bMonsterLog;
  6445.  
  6446. if (m_bMonsterLog)
  6447. {
  6448. CHARACTER_MANAGER::instance().RegisterForMonsterLog(this);
  6449. }
  6450. else
  6451. {
  6452. CHARACTER_MANAGER::instance().UnregisterForMonsterLog(this);
  6453. }
  6454. }
  6455.  
  6456. void CHARACTER::SetGuild(CGuild* pGuild)
  6457. {
  6458. if (m_pGuild != pGuild)
  6459. {
  6460. m_pGuild = pGuild;
  6461. UpdatePacket();
  6462. }
  6463. }
  6464.  
  6465. void CHARACTER::SendGreetMessage()
  6466. {
  6467. typeof(DBManager::instance().GetGreetMessage()) v = DBManager::instance().GetGreetMessage();
  6468.  
  6469. for (itertype(v) it = v.begin(); it != v.end(); ++it)
  6470. {
  6471. ChatPacket(CHAT_TYPE_NOTICE, it->c_str());
  6472. }
  6473. }
  6474.  
  6475. void CHARACTER::BeginStateEmpty()
  6476. {
  6477. MonsterLog("!");
  6478. }
  6479.  
  6480. void CHARACTER::EffectPacket(int enumEffectType)
  6481. {
  6482. TPacketGCSpecialEffect p;
  6483.  
  6484. p.header = HEADER_GC_SEPCIAL_EFFECT;
  6485. p.type = enumEffectType;
  6486. p.vid = GetVID();
  6487.  
  6488. PacketAround(&p, sizeof(TPacketGCSpecialEffect));
  6489. }
  6490.  
  6491. void CHARACTER::SpecificEffectPacket(const char filename[MAX_EFFECT_FILE_NAME])
  6492. {
  6493. TPacketGCSpecificEffect p;
  6494.  
  6495. p.header = HEADER_GC_SPECIFIC_EFFECT;
  6496. p.vid = GetVID();
  6497. memcpy (p.effect_file, filename, MAX_EFFECT_FILE_NAME);
  6498.  
  6499. PacketAround(&p, sizeof(TPacketGCSpecificEffect));
  6500. }
  6501.  
  6502. void CHARACTER::MonsterChat(BYTE bMonsterChatType)
  6503. {
  6504. if (IsPC())
  6505. return;
  6506.  
  6507. char sbuf[256+1];
  6508.  
  6509. if (IsMonster())
  6510. {
  6511. if (number(0, 60))
  6512. return;
  6513.  
  6514. snprintf(sbuf, sizeof(sbuf),
  6515. "(locale.monster_chat[%i] and locale.monster_chat[%i][%d] or '')",
  6516. GetRaceNum(), GetRaceNum(), bMonsterChatType*3 + number(1, 3));
  6517. }
  6518. else
  6519. {
  6520. if (bMonsterChatType != MONSTER_CHAT_WAIT)
  6521. return;
  6522.  
  6523. if (IsGuardNPC())
  6524. {
  6525. if (number(0, 6))
  6526. return;
  6527. }
  6528. else
  6529. {
  6530. if (number(0, 30))
  6531. return;
  6532. }
  6533.  
  6534. snprintf(sbuf, sizeof(sbuf), "(locale.monster_chat[%i] and locale.monster_chat[%i][number(1, table.getn(locale.monster_chat[%i]))] or '')", GetRaceNum(), GetRaceNum(), GetRaceNum());
  6535. }
  6536.  
  6537. std::string text = quest::ScriptToString(sbuf);
  6538.  
  6539. if (text.empty())
  6540. return;
  6541.  
  6542. struct packet_chat pack_chat;
  6543.  
  6544. pack_chat.header = HEADER_GC_CHAT;
  6545. pack_chat.size = sizeof(struct packet_chat) + text.size() + 1;
  6546. pack_chat.type = CHAT_TYPE_TALKING;
  6547. pack_chat.id = GetVID();
  6548. pack_chat.bEmpire = 0;
  6549.  
  6550. TEMP_BUFFER buf;
  6551. buf.write(&pack_chat, sizeof(struct packet_chat));
  6552. buf.write(text.c_str(), text.size() + 1);
  6553.  
  6554. PacketAround(buf.read_peek(), buf.size());
  6555. }
  6556.  
  6557. void CHARACTER::SetQuestNPCID(DWORD vid)
  6558. {
  6559. m_dwQuestNPCVID = vid;
  6560. if (vid && m_dwQuestNPCVID)
  6561. {
  6562. quest::PC* pPC = quest::CQuestManager::instance().GetPCForce(GetPlayerID());
  6563. if (pPC && pPC->IsRunning())
  6564. {
  6565. sys_err("cannot reset quest npc id - already running quest [%u %s]", GetPlayerID(), GetName());
  6566. return;
  6567. }
  6568. }
  6569. }
  6570.  
  6571. LPCHARACTER CHARACTER::GetQuestNPC() const
  6572. {
  6573. return CHARACTER_MANAGER::instance().Find(m_dwQuestNPCVID);
  6574. }
  6575.  
  6576. void CHARACTER::SetQuestItemPtr(LPITEM item)
  6577. {
  6578. m_pQuestItem = item;
  6579. }
  6580.  
  6581. void CHARACTER::ClearQuestItemPtr()
  6582. {
  6583. m_pQuestItem = NULL;
  6584. }
  6585.  
  6586. LPITEM CHARACTER::GetQuestItemPtr() const
  6587. {
  6588. return m_pQuestItem;
  6589. }
  6590.  
  6591. LPDUNGEON CHARACTER::GetDungeonForce() const
  6592. {
  6593. if (m_lWarpMapIndex > 10000)
  6594. return CDungeonManager::instance().FindByMapIndex(m_lWarpMapIndex);
  6595.  
  6596. return m_pkDungeon;
  6597. }
  6598.  
  6599. void CHARACTER::SetBlockMode(BYTE bFlag)
  6600. {
  6601. m_pointsInstant.bBlockMode = bFlag;
  6602.  
  6603. ChatPacket(CHAT_TYPE_COMMAND, "setblockmode %d", m_pointsInstant.bBlockMode);
  6604.  
  6605. SetQuestFlag("game_option.block_exchange", bFlag & BLOCK_EXCHANGE ? 1 : 0);
  6606. SetQuestFlag("game_option.block_party_invite", bFlag & BLOCK_PARTY_INVITE ? 1 : 0);
  6607. SetQuestFlag("game_option.block_guild_invite", bFlag & BLOCK_GUILD_INVITE ? 1 : 0);
  6608. SetQuestFlag("game_option.block_whisper", bFlag & BLOCK_WHISPER ? 1 : 0);
  6609. SetQuestFlag("game_option.block_messenger_invite", bFlag & BLOCK_MESSENGER_INVITE ? 1 : 0);
  6610. SetQuestFlag("game_option.block_party_request", bFlag & BLOCK_PARTY_REQUEST ? 1 : 0);
  6611. }
  6612.  
  6613. void CHARACTER::SetBlockModeForce(BYTE bFlag)
  6614. {
  6615. m_pointsInstant.bBlockMode = bFlag;
  6616. ChatPacket(CHAT_TYPE_COMMAND, "setblockmode %d", m_pointsInstant.bBlockMode);
  6617. }
  6618.  
  6619. bool CHARACTER::IsGuardNPC() const
  6620. {
  6621. return IsNPC() && (GetRaceNum() == 11000 || GetRaceNum() == 11002 || GetRaceNum() == 11004);
  6622. }
  6623.  
  6624. int CHARACTER::GetPolymorphPower() const
  6625. {
  6626. if (test_server)
  6627. {
  6628. int value = quest::CQuestManager::instance().GetEventFlag("poly");
  6629. if (value)
  6630. return value;
  6631. }
  6632. return aiPolymorphPowerByLevel[MINMAX(0, GetSkillLevel(SKILL_POLYMORPH), 40)];
  6633. }
  6634.  
  6635. void CHARACTER::SetPolymorph(DWORD dwRaceNum, bool bMaintainStat)
  6636. {
  6637. if (dwRaceNum < JOB_MAX_NUM)
  6638. {
  6639. dwRaceNum = 0;
  6640. bMaintainStat = false;
  6641. }
  6642.  
  6643. if (m_dwPolymorphRace == dwRaceNum)
  6644. return;
  6645.  
  6646. m_bPolyMaintainStat = bMaintainStat;
  6647. m_dwPolymorphRace = dwRaceNum;
  6648.  
  6649. sys_log(0, "POLYMORPH: %s race %u ", GetName(), dwRaceNum);
  6650.  
  6651. if (dwRaceNum != 0)
  6652. StopRiding();
  6653.  
  6654. SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
  6655. m_afAffectFlag.Set(AFF_SPAWN);
  6656.  
  6657. ViewReencode();
  6658.  
  6659. REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
  6660.  
  6661. if (!bMaintainStat)
  6662. {
  6663. PointChange(POINT_ST, 0);
  6664. PointChange(POINT_DX, 0);
  6665. PointChange(POINT_IQ, 0);
  6666. PointChange(POINT_HT, 0);
  6667. }
  6668.  
  6669. // 폴리모프 상태에서 죽는 경우, 폴리모프가 풀리게 되는데
  6670. // 폴리 모프 전후로 valid combo interval이 다르기 때문에
  6671. // Combo 핵 또는 Hacker로 인식하는 경우가 있다.
  6672. // 따라서 폴리모프를 풀거나 폴리모프 하게 되면,
  6673. // valid combo interval을 reset한다.
  6674. SetValidComboInterval(0);
  6675. SetComboSequence(0);
  6676.  
  6677. ComputeBattlePoints();
  6678. }
  6679.  
  6680. int CHARACTER::GetQuestFlag(const std::string& flag) const
  6681. {
  6682. quest::CQuestManager& q = quest::CQuestManager::instance();
  6683. quest::PC* pPC = q.GetPC(GetPlayerID());
  6684. return pPC->GetFlag(flag);
  6685. }
  6686.  
  6687. void CHARACTER::SetQuestFlag(const std::string& flag, int value)
  6688. {
  6689. quest::CQuestManager& q = quest::CQuestManager::instance();
  6690. quest::PC* pPC = q.GetPC(GetPlayerID());
  6691. pPC->SetFlag(flag, value);
  6692. }
  6693.  
  6694. void CHARACTER::DetermineDropMetinStone()
  6695. {
  6696. const int METIN_STONE_NUM = 14;
  6697. static DWORD c_adwMetin[METIN_STONE_NUM] =
  6698. {
  6699. 28030,
  6700. 28031,
  6701. 28032,
  6702. 28033,
  6703. 28034,
  6704. 28035,
  6705. 28036,
  6706. 28037,
  6707. 28038,
  6708. 28039,
  6709. 28040,
  6710. 28041,
  6711. 28042,
  6712. 28043,
  6713. };
  6714. DWORD stone_num = GetRaceNum();
  6715. int idx = std::lower_bound(aStoneDrop, aStoneDrop+STONE_INFO_MAX_NUM, stone_num) - aStoneDrop;
  6716. if (idx >= STONE_INFO_MAX_NUM || aStoneDrop[idx].dwMobVnum != stone_num)
  6717. {
  6718. m_dwDropMetinStone = 0;
  6719. }
  6720. else
  6721. {
  6722. const SStoneDropInfo & info = aStoneDrop[idx];
  6723. m_bDropMetinStonePct = info.iDropPct;
  6724. {
  6725. m_dwDropMetinStone = c_adwMetin[number(0, METIN_STONE_NUM - 1)];
  6726. int iGradePct = number(1, 100);
  6727. for (int iStoneLevel = 0; iStoneLevel < STONE_LEVEL_MAX_NUM; iStoneLevel ++)
  6728. {
  6729. int iLevelGradePortion = info.iLevelPct[iStoneLevel];
  6730. if (iGradePct <= iLevelGradePortion)
  6731. {
  6732. break;
  6733. }
  6734. else
  6735. {
  6736. iGradePct -= iLevelGradePortion;
  6737. m_dwDropMetinStone += 100; // 돌 +a -> +(a+1)이 될때마다 100씩 증가
  6738. }
  6739. }
  6740. }
  6741. }
  6742. }
  6743.  
  6744. void CHARACTER::SendEquipment(LPCHARACTER ch)
  6745. {
  6746. TPacketViewEquip p;
  6747. p.header = HEADER_GC_VIEW_EQUIP;
  6748. p.vid = GetVID();
  6749. int pos[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 19, 20, 21, 22, 23 };
  6750. for (int i = 0; i < 16; i++)
  6751. {
  6752. LPITEM item = GetWear(pos[i]);
  6753. if (item)
  6754. {
  6755. p.equips[i].vnum = item->GetVnum();
  6756. p.equips[i].count = item->GetCount();
  6757.  
  6758. thecore_memcpy(p.equips[i].alSockets, item->GetSockets(), sizeof(p.equips[i].alSockets));
  6759. thecore_memcpy(p.equips[i].aAttr, item->GetAttributes(), sizeof(p.equips[i].aAttr));
  6760. }
  6761. else
  6762. {
  6763. p.equips[i].vnum = 0;
  6764. }
  6765. }
  6766. ch->GetDesc()->Packet(&p, sizeof(p));
  6767. }
  6768.  
  6769. bool CHARACTER::CanSummon(int iLeaderShip)
  6770. {
  6771. return (iLeaderShip >= 20 || iLeaderShip >= 12 && m_dwLastDeadTime + 180 > get_dword_time());
  6772. }
  6773.  
  6774.  
  6775. void CHARACTER::MountVnum(DWORD vnum)
  6776. {
  6777. if (m_dwMountVnum == vnum)
  6778. return;
  6779.  
  6780. m_dwMountVnum = vnum;
  6781. m_dwMountTime = get_dword_time();
  6782.  
  6783. if (m_bIsObserver)
  6784. return;
  6785.  
  6786. //NOTE : Mount한다고 해서 Client Side의 객체를 삭제하진 않는다.
  6787. //그리고 서버Side에서 탔을때 위치 이동은 하지 않는다. 왜냐하면 Client Side에서 Coliision Adjust를 할수 있는데
  6788. //객체를 소멸시켰다가 서버위치로 이동시키면 이때 collision check를 하지는 않으므로 배경에 끼거나 뚫고 나가는 문제가 존재한다.
  6789. m_posDest.x = m_posStart.x = GetX();
  6790. m_posDest.y = m_posStart.y = GetY();
  6791. //EncodeRemovePacket(this);
  6792. EncodeInsertPacket(this);
  6793.  
  6794. ENTITY_MAP::iterator it = m_map_view.begin();
  6795.  
  6796. while (it != m_map_view.end())
  6797. {
  6798. LPENTITY entity = (it++)->first;
  6799.  
  6800. //Mount한다고 해서 Client Side의 객체를 삭제하진 않는다.
  6801. //EncodeRemovePacket(entity);
  6802. //if (!m_bIsObserver)
  6803. EncodeInsertPacket(entity);
  6804.  
  6805. //if (!entity->IsObserverMode())
  6806. // entity->EncodeInsertPacket(this);
  6807. }
  6808.  
  6809. SetValidComboInterval(0);
  6810. SetComboSequence(0);
  6811.  
  6812. ComputePoints();
  6813. }
  6814.  
  6815. namespace {
  6816. class FuncCheckWarp
  6817. {
  6818. public:
  6819. FuncCheckWarp(LPCHARACTER pkWarp)
  6820. {
  6821. m_lTargetY = 0;
  6822. m_lTargetX = 0;
  6823.  
  6824. m_lX = pkWarp->GetX();
  6825. m_lY = pkWarp->GetY();
  6826.  
  6827. m_bInvalid = false;
  6828. m_bEmpire = pkWarp->GetEmpire();
  6829.  
  6830. char szTmp[64];
  6831.  
  6832. if (3 != sscanf(pkWarp->GetName(), " %s %ld %ld ", szTmp, &m_lTargetX, &m_lTargetY))
  6833. {
  6834. if (number(1, 100) < 5)
  6835. sys_err("Warp NPC name wrong : vnum(%d) name(%s)", pkWarp->GetRaceNum(), pkWarp->GetName());
  6836.  
  6837. m_bInvalid = true;
  6838.  
  6839. return;
  6840. }
  6841.  
  6842. m_lTargetX *= 100;
  6843. m_lTargetY *= 100;
  6844.  
  6845. m_bUseWarp = true;
  6846.  
  6847. if (pkWarp->IsGoto())
  6848. {
  6849. LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(pkWarp->GetMapIndex());
  6850. m_lTargetX += pkSectreeMap->m_setting.iBaseX;
  6851. m_lTargetY += pkSectreeMap->m_setting.iBaseY;
  6852. m_bUseWarp = false;
  6853. }
  6854. }
  6855.  
  6856. bool Valid()
  6857. {
  6858. return !m_bInvalid;
  6859. }
  6860.  
  6861. void operator () (LPENTITY ent)
  6862. {
  6863. if (!Valid())
  6864. return;
  6865.  
  6866. if (!ent->IsType(ENTITY_CHARACTER))
  6867. return;
  6868.  
  6869. LPCHARACTER pkChr = (LPCHARACTER) ent;
  6870.  
  6871. if (!pkChr->IsPC())
  6872. return;
  6873.  
  6874. int iDist = DISTANCE_APPROX(pkChr->GetX() - m_lX, pkChr->GetY() - m_lY);
  6875.  
  6876. if (iDist > 300)
  6877. return;
  6878.  
  6879. if (m_bEmpire && pkChr->GetEmpire() && m_bEmpire != pkChr->GetEmpire())
  6880. return;
  6881.  
  6882. if (pkChr->IsHack())
  6883. return;
  6884.  
  6885. if (!pkChr->CanHandleItem(false, true))
  6886. return;
  6887.  
  6888. if (m_bUseWarp)
  6889. pkChr->WarpSet(m_lTargetX, m_lTargetY);
  6890. else
  6891. {
  6892. pkChr->Show(pkChr->GetMapIndex(), m_lTargetX, m_lTargetY);
  6893. pkChr->Stop();
  6894. }
  6895. }
  6896.  
  6897. bool m_bInvalid;
  6898. bool m_bUseWarp;
  6899.  
  6900. long m_lX;
  6901. long m_lY;
  6902. long m_lTargetX;
  6903. long m_lTargetY;
  6904.  
  6905. BYTE m_bEmpire;
  6906. };
  6907. }
  6908.  
  6909. EVENTFUNC(warp_npc_event)
  6910. {
  6911. char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  6912. if ( info == NULL )
  6913. {
  6914. sys_err( "warp_npc_event> <Factor> Null pointer" );
  6915. return 0;
  6916. }
  6917.  
  6918. LPCHARACTER ch = info->ch;
  6919.  
  6920. if (ch == NULL) { // <Factor>
  6921. return 0;
  6922. }
  6923.  
  6924. if (!ch->GetSectree())
  6925. {
  6926. ch->m_pkWarpNPCEvent = NULL;
  6927. return 0;
  6928. }
  6929.  
  6930. FuncCheckWarp f(ch);
  6931. if (f.Valid())
  6932. ch->GetSectree()->ForEachAround(f);
  6933.  
  6934. return passes_per_sec / 2;
  6935. }
  6936.  
  6937.  
  6938. void CHARACTER::StartWarpNPCEvent()
  6939. {
  6940. if (m_pkWarpNPCEvent)
  6941. return;
  6942.  
  6943. if (!IsWarp() && !IsGoto())
  6944. return;
  6945.  
  6946. char_event_info* info = AllocEventInfo<char_event_info>();
  6947.  
  6948. info->ch = this;
  6949.  
  6950. m_pkWarpNPCEvent = event_create(warp_npc_event, info, passes_per_sec / 2);
  6951. }
  6952.  
  6953. void CHARACTER::SyncPacket()
  6954. {
  6955. TEMP_BUFFER buf;
  6956.  
  6957. TPacketCGSyncPositionElement elem;
  6958.  
  6959. elem.dwVID = GetVID();
  6960. elem.lX = GetX();
  6961. elem.lY = GetY();
  6962.  
  6963. TPacketGCSyncPosition pack;
  6964.  
  6965. pack.bHeader = HEADER_GC_SYNC_POSITION;
  6966. pack.wSize = sizeof(TPacketGCSyncPosition) + sizeof(elem);
  6967.  
  6968. buf.write(&pack, sizeof(pack));
  6969. buf.write(&elem, sizeof(elem));
  6970.  
  6971. PacketAround(buf.read_peek(), buf.size());
  6972. }
  6973.  
  6974. LPCHARACTER CHARACTER::GetMarryPartner() const
  6975. {
  6976. return m_pkChrMarried;
  6977. }
  6978.  
  6979. void CHARACTER::SetMarryPartner(LPCHARACTER ch)
  6980. {
  6981. m_pkChrMarried = ch;
  6982. }
  6983.  
  6984. int CHARACTER::GetMarriageBonus(DWORD dwItemVnum, bool bSum)
  6985. {
  6986. if (IsNPC())
  6987. return 0;
  6988.  
  6989. marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID());
  6990.  
  6991. if (!pMarriage)
  6992. return 0;
  6993.  
  6994. return pMarriage->GetBonus(dwItemVnum, bSum, this);
  6995. }
  6996.  
  6997. void CHARACTER::ConfirmWithMsg(const char* szMsg, int iTimeout, DWORD dwRequestPID)
  6998. {
  6999. if (!IsPC())
  7000. return;
  7001.  
  7002. TPacketGCQuestConfirm p;
  7003.  
  7004. p.header = HEADER_GC_QUEST_CONFIRM;
  7005. p.requestPID = dwRequestPID;
  7006. p.timeout = iTimeout;
  7007. strlcpy(p.msg, szMsg, sizeof(p.msg));
  7008.  
  7009. GetDesc()->Packet(&p, sizeof(p));
  7010. }
  7011.  
  7012. int CHARACTER::GetPremiumRemainSeconds(BYTE bType) const
  7013. {
  7014. if (bType >= PREMIUM_MAX_NUM)
  7015. return 0;
  7016.  
  7017. return m_aiPremiumTimes[bType] - get_global_time();
  7018. }
  7019.  
  7020. bool CHARACTER::WarpToPID(DWORD dwPID)
  7021. {
  7022. LPCHARACTER victim;
  7023. if ((victim = (CHARACTER_MANAGER::instance().FindByPID(dwPID))))
  7024. {
  7025. int mapIdx = victim->GetMapIndex();
  7026. if (IS_SUMMONABLE_ZONE(mapIdx))
  7027. {
  7028. if (CAN_ENTER_ZONE(this, mapIdx))
  7029. {
  7030. WarpSet(victim->GetX(), victim->GetY());
  7031. }
  7032. else
  7033. {
  7034. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 있는 곳으로 워프할 수 없습니다."));
  7035. return false;
  7036. }
  7037. }
  7038. else
  7039. {
  7040. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 있는 곳으로 워프할 수 없습니다."));
  7041. return false;
  7042. }
  7043. }
  7044. else
  7045. {
  7046. // 다른 서버에 로그인된 사람이 있음 -> 메시지 보내 좌표를 받아오자
  7047. // 1. A.pid, B.pid 를 뿌림
  7048. // 2. B.pid를 가진 서버가 뿌린서버에게 A.pid, 좌표 를 보냄
  7049. // 3. 워프
  7050. CCI * pcci = P2P_MANAGER::instance().FindByPID(dwPID);
  7051.  
  7052. if (!pcci)
  7053. {
  7054. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 온라인 상태가 아닙니다."));
  7055. return false;
  7056. }
  7057.  
  7058. if (pcci->bChannel != g_bChannel)
  7059. {
  7060. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 %d 채널에 있습니다. (현재 채널 %d)"), pcci->bChannel, g_bChannel);
  7061. return false;
  7062. }
  7063. else if (false == IS_SUMMONABLE_ZONE(pcci->lMapIndex))
  7064. {
  7065. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 있는 곳으로 워프할 수 없습니다."));
  7066. return false;
  7067. }
  7068. else
  7069. {
  7070. if (!CAN_ENTER_ZONE(this, pcci->lMapIndex))
  7071. {
  7072. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 있는 곳으로 워프할 수 없습니다."));
  7073. return false;
  7074. }
  7075.  
  7076. TPacketGGFindPosition p;
  7077. p.header = HEADER_GG_FIND_POSITION;
  7078. p.dwFromPID = GetPlayerID();
  7079. p.dwTargetPID = dwPID;
  7080. pcci->pkDesc->Packet(&p, sizeof(TPacketGGFindPosition));
  7081.  
  7082. if (test_server)
  7083. ChatPacket(CHAT_TYPE_PARTY, "sent find position packet for teleport");
  7084. }
  7085. }
  7086. return true;
  7087. }
  7088.  
  7089. // ADD_REFINE_BUILDING
  7090. CGuild* CHARACTER::GetRefineGuild() const
  7091. {
  7092. LPCHARACTER chRefineNPC = CHARACTER_MANAGER::instance().Find(m_dwRefineNPCVID);
  7093.  
  7094. return (chRefineNPC ? chRefineNPC->GetGuild() : NULL);
  7095. }
  7096.  
  7097. bool CHARACTER::IsRefineThroughGuild() const
  7098. {
  7099. return GetRefineGuild() != NULL;
  7100. }
  7101.  
  7102. int CHARACTER::ComputeRefineFee(int iCost, int iMultiply) const
  7103. {
  7104. CGuild* pGuild = GetRefineGuild();
  7105. if (pGuild)
  7106. {
  7107. if (pGuild == GetGuild())
  7108. return iCost * iMultiply * 9 / 10;
  7109.  
  7110. // 다른 제국 사람이 시도하는 경우 추가로 3배 더
  7111. LPCHARACTER chRefineNPC = CHARACTER_MANAGER::instance().Find(m_dwRefineNPCVID);
  7112. if (chRefineNPC && chRefineNPC->GetEmpire() != GetEmpire())
  7113. return iCost * iMultiply * 3;
  7114.  
  7115. return iCost * iMultiply;
  7116. }
  7117. else
  7118. return iCost;
  7119. }
  7120.  
  7121. void CHARACTER::PayRefineFee(int iTotalMoney)
  7122. {
  7123. int iFee = iTotalMoney / 10;
  7124. CGuild* pGuild = GetRefineGuild();
  7125.  
  7126. int iRemain = iTotalMoney;
  7127.  
  7128. if (pGuild)
  7129. {
  7130. // 자기 길드이면 iTotalMoney에 이미 10%가 제외되어있다
  7131. if (pGuild != GetGuild())
  7132. {
  7133. pGuild->RequestDepositMoney(this, iFee);
  7134. iRemain -= iFee;
  7135. }
  7136. }
  7137.  
  7138. PointChange(POINT_GOLD, -iRemain);
  7139. }
  7140. // END_OF_ADD_REFINE_BUILDING
  7141.  
  7142. //Hack 방지를 위한 체크.
  7143. bool CHARACTER::IsHack(bool bSendMsg, bool bCheckShopOwner, int limittime)
  7144. {
  7145. const int iPulse = thecore_pulse();
  7146.  
  7147. if (test_server)
  7148. bSendMsg = true;
  7149.  
  7150. //창고 연후 체크
  7151. if (iPulse - GetSafeboxLoadTime() < PASSES_PER_SEC(limittime))
  7152. {
  7153. if (bSendMsg)
  7154. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("창고를 연후 %d초 이내에는 다른곳으로 이동할수 없습니다."), limittime);
  7155.  
  7156. if (test_server)
  7157. ChatPacket(CHAT_TYPE_INFO, "[TestOnly]Pulse %d LoadTime %d PASS %d", iPulse, GetSafeboxLoadTime(), PASSES_PER_SEC(limittime));
  7158. return true;
  7159. }
  7160.  
  7161. //거래관련 창 체크
  7162. if (bCheckShopOwner)
  7163. {
  7164. if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen())
  7165. {
  7166. if (bSendMsg)
  7167. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래창,창고 등을 연 상태에서는 다른곳으로 이동,종료 할수 없습니다"));
  7168.  
  7169. return true;
  7170. }
  7171. }
  7172. else
  7173. {
  7174. if (GetExchange() || GetMyShop() || IsOpenSafebox() || IsCubeOpen())
  7175. {
  7176. if (bSendMsg)
  7177. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래창,창고 등을 연 상태에서는 다른곳으로 이동,종료 할수 없습니다"));
  7178.  
  7179. return true;
  7180. }
  7181. }
  7182.  
  7183. //PREVENT_PORTAL_AFTER_EXCHANGE
  7184. //교환 후 시간체크
  7185. if (iPulse - GetExchangeTime() < PASSES_PER_SEC(limittime))
  7186. {
  7187. if (bSendMsg)
  7188. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래 후 %d초 이내에는 다른지역으로 이동 할 수 없습니다."), limittime );
  7189. return true;
  7190. }
  7191. //END_PREVENT_PORTAL_AFTER_EXCHANGE
  7192.  
  7193. //PREVENT_ITEM_COPY
  7194. if (iPulse - GetMyShopTime() < PASSES_PER_SEC(limittime))
  7195. {
  7196. if (bSendMsg)
  7197. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래 후 %d초 이내에는 다른지역으로 이동 할 수 없습니다."), limittime);
  7198. return true;
  7199. }
  7200.  
  7201. if (iPulse - GetRefineTime() < PASSES_PER_SEC(limittime))
  7202. {
  7203. if (bSendMsg)
  7204. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 개량후 %d초 이내에는 귀환부,귀환기억부를 사용할 수 없습니다."), limittime);
  7205. return true;
  7206. }
  7207. //END_PREVENT_ITEM_COPY
  7208.  
  7209. return false;
  7210. }
  7211.  
  7212. BOOL CHARACTER::IsMonarch() const
  7213. {
  7214. //MONARCH_LIMIT
  7215. if (CMonarch::instance().IsMonarch(GetPlayerID(), GetEmpire()))
  7216. return true;
  7217.  
  7218. return false;
  7219.  
  7220. //END_MONARCH_LIMIT
  7221. }
  7222. void CHARACTER::Say(const std::string & s)
  7223. {
  7224. struct ::packet_script packet_script;
  7225.  
  7226. packet_script.header = HEADER_GC_SCRIPT;
  7227. packet_script.skin = 1;
  7228. packet_script.src_size = s.size();
  7229. packet_script.size = packet_script.src_size + sizeof(struct packet_script);
  7230.  
  7231. TEMP_BUFFER buf;
  7232.  
  7233. buf.write(&packet_script, sizeof(struct packet_script));
  7234. buf.write(&s[0], s.size());
  7235.  
  7236. if (IsPC())
  7237. {
  7238. GetDesc()->Packet(buf.read_peek(), buf.size());
  7239. }
  7240. }
  7241.  
  7242. //
  7243. // Monarch
  7244. //
  7245. void CHARACTER::InitMC()
  7246. {
  7247. for (int n = 0; n < MI_MAX; ++n)
  7248. {
  7249. m_dwMonarchCooltime[n] = thecore_pulse();
  7250. }
  7251.  
  7252. m_dwMonarchCooltimelimit[MI_HEAL] = PASSES_PER_SEC(MC_HEAL);
  7253. m_dwMonarchCooltimelimit[MI_WARP] = PASSES_PER_SEC(MC_WARP);
  7254. m_dwMonarchCooltimelimit[MI_TRANSFER] = PASSES_PER_SEC(MC_TRANSFER);
  7255. m_dwMonarchCooltimelimit[MI_TAX] = PASSES_PER_SEC(MC_TAX);
  7256. m_dwMonarchCooltimelimit[MI_SUMMON] = PASSES_PER_SEC(MC_SUMMON);
  7257.  
  7258. m_dwMonarchCooltime[MI_HEAL] -= PASSES_PER_SEC(GetMCL(MI_HEAL));
  7259. m_dwMonarchCooltime[MI_WARP] -= PASSES_PER_SEC(GetMCL(MI_WARP));
  7260. m_dwMonarchCooltime[MI_TRANSFER] -= PASSES_PER_SEC(GetMCL(MI_TRANSFER));
  7261. m_dwMonarchCooltime[MI_TAX] -= PASSES_PER_SEC(GetMCL(MI_TAX));
  7262. m_dwMonarchCooltime[MI_SUMMON] -= PASSES_PER_SEC(GetMCL(MI_SUMMON));
  7263. }
  7264.  
  7265. DWORD CHARACTER::GetMC(enum MONARCH_INDEX e) const
  7266. {
  7267. return m_dwMonarchCooltime[e];
  7268. }
  7269.  
  7270. void CHARACTER::SetMC(enum MONARCH_INDEX e)
  7271. {
  7272. m_dwMonarchCooltime[e] = thecore_pulse();
  7273. }
  7274.  
  7275. bool CHARACTER::IsMCOK(enum MONARCH_INDEX e) const
  7276. {
  7277. int iPulse = thecore_pulse();
  7278.  
  7279. if ((iPulse - GetMC(e)) < GetMCL(e))
  7280. {
  7281. if (test_server)
  7282. sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e));
  7283.  
  7284. return false;
  7285. }
  7286.  
  7287. if (test_server)
  7288. sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e));
  7289.  
  7290. return true;
  7291. }
  7292.  
  7293. DWORD CHARACTER::GetMCL(enum MONARCH_INDEX e) const
  7294. {
  7295. return m_dwMonarchCooltimelimit[e];
  7296. }
  7297.  
  7298. DWORD CHARACTER::GetMCLTime(enum MONARCH_INDEX e) const
  7299. {
  7300. int iPulse = thecore_pulse();
  7301.  
  7302. if (test_server)
  7303. sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e));
  7304.  
  7305. return (GetMCL(e)) / passes_per_sec - (iPulse - GetMC(e)) / passes_per_sec;
  7306. }
  7307.  
  7308. bool CHARACTER::IsSiegeNPC() const
  7309. {
  7310. return IsNPC() && (GetRaceNum() == 11000 || GetRaceNum() == 11002 || GetRaceNum() == 11004);
  7311. }
  7312.  
  7313. //------------------------------------------------
  7314. void CHARACTER::UpdateDepositPulse()
  7315. {
  7316. m_deposit_pulse = thecore_pulse() + PASSES_PER_SEC(60*5); // 5분
  7317. }
  7318.  
  7319. bool CHARACTER::CanDeposit() const
  7320. {
  7321. return (m_deposit_pulse == 0 || (m_deposit_pulse < thecore_pulse()));
  7322. }
  7323. //------------------------------------------------
  7324.  
  7325. ESex GET_SEX(LPCHARACTER ch)
  7326. {
  7327. switch (ch->GetRaceNum())
  7328. {
  7329. case MAIN_RACE_WARRIOR_M:
  7330. case MAIN_RACE_SURA_M:
  7331. case MAIN_RACE_ASSASSIN_M:
  7332. case MAIN_RACE_SHAMAN_M:
  7333. return SEX_MALE;
  7334.  
  7335. case MAIN_RACE_ASSASSIN_W:
  7336. case MAIN_RACE_SHAMAN_W:
  7337. case MAIN_RACE_WARRIOR_W:
  7338. case MAIN_RACE_SURA_W:
  7339. return SEX_FEMALE;
  7340. }
  7341.  
  7342. /* default sex = male */
  7343. return SEX_MALE;
  7344. }
  7345.  
  7346. int CHARACTER::GetHPPct() const
  7347. {
  7348. return (GetHP() * 100) / GetMaxHP();
  7349. }
  7350.  
  7351. bool CHARACTER::IsBerserk() const
  7352. {
  7353. if (m_pkMobInst != NULL)
  7354. return m_pkMobInst->m_IsBerserk;
  7355. else
  7356. return false;
  7357. }
  7358.  
  7359. void CHARACTER::SetBerserk(bool mode)
  7360. {
  7361. if (m_pkMobInst != NULL)
  7362. m_pkMobInst->m_IsBerserk = mode;
  7363. }
  7364.  
  7365. bool CHARACTER::IsGodSpeed() const
  7366. {
  7367. if (m_pkMobInst != NULL)
  7368. {
  7369. return m_pkMobInst->m_IsGodSpeed;
  7370. }
  7371. else
  7372. {
  7373. return false;
  7374. }
  7375. }
  7376.  
  7377. void CHARACTER::SetGodSpeed(bool mode)
  7378. {
  7379. if (m_pkMobInst != NULL)
  7380. {
  7381. m_pkMobInst->m_IsGodSpeed = mode;
  7382.  
  7383. if (mode == true)
  7384. {
  7385. SetPoint(POINT_ATT_SPEED, 250);
  7386. }
  7387. else
  7388. {
  7389. SetPoint(POINT_ATT_SPEED, m_pkMobData->m_table.sAttackSpeed);
  7390. }
  7391. }
  7392. }
  7393.  
  7394. bool CHARACTER::IsDeathBlow() const
  7395. {
  7396. if (number(1, 100) <= m_pkMobData->m_table.bDeathBlowPoint)
  7397. {
  7398. return true;
  7399. }
  7400. else
  7401. {
  7402. return false;
  7403. }
  7404. }
  7405.  
  7406. struct FFindReviver
  7407. {
  7408. FFindReviver()
  7409. {
  7410. pChar = NULL;
  7411. HasReviver = false;
  7412. }
  7413.  
  7414. void operator() (LPCHARACTER ch)
  7415. {
  7416. if (ch->IsMonster() != true)
  7417. {
  7418. return;
  7419. }
  7420.  
  7421. if (ch->IsReviver() == true && pChar != ch && ch->IsDead() != true)
  7422. {
  7423. if (number(1, 100) <= ch->GetMobTable().bRevivePoint)
  7424. {
  7425. HasReviver = true;
  7426. pChar = ch;
  7427. }
  7428. }
  7429. }
  7430.  
  7431. LPCHARACTER pChar;
  7432. bool HasReviver;
  7433. };
  7434.  
  7435. bool CHARACTER::HasReviverInParty() const
  7436. {
  7437. LPPARTY party = GetParty();
  7438.  
  7439. if (party != NULL)
  7440. {
  7441. if (party->GetMemberCount() == 1) return false;
  7442.  
  7443. FFindReviver f;
  7444. party->ForEachMemberPtr(f);
  7445. return f.HasReviver;
  7446. }
  7447.  
  7448. return false;
  7449. }
  7450.  
  7451. bool CHARACTER::IsRevive() const
  7452. {
  7453. if (m_pkMobInst != NULL)
  7454. {
  7455. return m_pkMobInst->m_IsRevive;
  7456. }
  7457.  
  7458. return false;
  7459. }
  7460.  
  7461. void CHARACTER::SetRevive(bool mode)
  7462. {
  7463. if (m_pkMobInst != NULL)
  7464. {
  7465. m_pkMobInst->m_IsRevive = mode;
  7466. }
  7467. }
  7468.  
  7469. #define IS_SPEED_HACK_PLAYER(ch) (ch->m_speed_hack_count > SPEEDHACK_LIMIT_COUNT)
  7470.  
  7471. EVENTFUNC(check_speedhack_event)
  7472. {
  7473. char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  7474. if ( info == NULL )
  7475. {
  7476. sys_err( "check_speedhack_event> <Factor> Null pointer" );
  7477. return 0;
  7478. }
  7479.  
  7480. LPCHARACTER ch = info->ch;
  7481.  
  7482. if (NULL == ch || ch->IsNPC())
  7483. return 0;
  7484.  
  7485. if (IS_SPEED_HACK_PLAYER(ch))
  7486. {
  7487. // write hack log
  7488. LogManager::instance().SpeedHackLog(ch->GetPlayerID(), ch->GetX(), ch->GetY(), ch->m_speed_hack_count);
  7489.  
  7490. if (false == LC_IsEurope())
  7491. {
  7492. // close connection
  7493. LPDESC desc = ch->GetDesc();
  7494.  
  7495. if (desc)
  7496. {
  7497. DESC_MANAGER::instance().DestroyDesc(desc);
  7498. return 0;
  7499. }
  7500. }
  7501. }
  7502.  
  7503. ch->m_speed_hack_count = 0;
  7504.  
  7505. ch->ResetComboHackCount();
  7506. return PASSES_PER_SEC(60);
  7507. }
  7508.  
  7509. void CHARACTER::StartCheckSpeedHackEvent()
  7510. {
  7511. if (m_pkCheckSpeedHackEvent)
  7512. return;
  7513.  
  7514. char_event_info* info = AllocEventInfo<char_event_info>();
  7515.  
  7516. info->ch = this;
  7517.  
  7518. m_pkCheckSpeedHackEvent = event_create(check_speedhack_event, info, PASSES_PER_SEC(60)); // 1분
  7519. }
  7520.  
  7521. void CHARACTER::GoHome()
  7522. {
  7523. WarpSet(EMPIRE_START_X(GetEmpire()), EMPIRE_START_Y(GetEmpire()));
  7524. }
  7525.  
  7526. void CHARACTER::SendGuildName(CGuild* pGuild)
  7527. {
  7528. if (NULL == pGuild) return;
  7529.  
  7530. DESC *desc = GetDesc();
  7531.  
  7532. if (NULL == desc) return;
  7533. if (m_known_guild.find(pGuild->GetID()) != m_known_guild.end()) return;
  7534.  
  7535. m_known_guild.insert(pGuild->GetID());
  7536.  
  7537. TPacketGCGuildName pack;
  7538. memset(&pack, 0x00, sizeof(pack));
  7539.  
  7540. pack.header = HEADER_GC_GUILD;
  7541. pack.subheader = GUILD_SUBHEADER_GC_GUILD_NAME;
  7542. pack.size = sizeof(TPacketGCGuildName);
  7543. pack.guildID = pGuild->GetID();
  7544. memcpy(pack.guildName, pGuild->GetName(), GUILD_NAME_MAX_LEN);
  7545.  
  7546. desc->Packet(&pack, sizeof(pack));
  7547. }
  7548.  
  7549. void CHARACTER::SendGuildName(DWORD dwGuildID)
  7550. {
  7551. SendGuildName(CGuildManager::instance().FindGuild(dwGuildID));
  7552. }
  7553.  
  7554. EVENTFUNC(destroy_when_idle_event)
  7555. {
  7556. char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  7557. if ( info == NULL )
  7558. {
  7559. sys_err( "destroy_when_idle_event> <Factor> Null pointer" );
  7560. return 0;
  7561. }
  7562.  
  7563. LPCHARACTER ch = info->ch;
  7564. if (ch == NULL) { // <Factor>
  7565. return 0;
  7566. }
  7567.  
  7568. if (ch->GetVictim())
  7569. {
  7570. return PASSES_PER_SEC(300);
  7571. }
  7572.  
  7573. sys_log(1, "DESTROY_WHEN_IDLE: %s", ch->GetName());
  7574.  
  7575. ch->m_pkDestroyWhenIdleEvent = NULL;
  7576. M2_DESTROY_CHARACTER(ch);
  7577. return 0;
  7578. }
  7579.  
  7580. void CHARACTER::StartDestroyWhenIdleEvent()
  7581. {
  7582. if (m_pkDestroyWhenIdleEvent)
  7583. return;
  7584.  
  7585. char_event_info* info = AllocEventInfo<char_event_info>();
  7586.  
  7587. info->ch = this;
  7588.  
  7589. m_pkDestroyWhenIdleEvent = event_create(destroy_when_idle_event, info, PASSES_PER_SEC(300));
  7590. }
  7591.  
  7592. void CHARACTER::SetComboSequence(BYTE seq)
  7593. {
  7594. m_bComboSequence = seq;
  7595. }
  7596.  
  7597. BYTE CHARACTER::GetComboSequence() const
  7598. {
  7599. return m_bComboSequence;
  7600. }
  7601.  
  7602. void CHARACTER::SetLastComboTime(DWORD time)
  7603. {
  7604. m_dwLastComboTime = time;
  7605. }
  7606.  
  7607. DWORD CHARACTER::GetLastComboTime() const
  7608. {
  7609. return m_dwLastComboTime;
  7610. }
  7611.  
  7612. void CHARACTER::SetValidComboInterval(int interval)
  7613. {
  7614. m_iValidComboInterval = interval;
  7615. }
  7616.  
  7617. int CHARACTER::GetValidComboInterval() const
  7618. {
  7619. return m_iValidComboInterval;
  7620. }
  7621.  
  7622. BYTE CHARACTER::GetComboIndex() const
  7623. {
  7624. return m_bComboIndex;
  7625. }
  7626.  
  7627. void CHARACTER::IncreaseComboHackCount(int k)
  7628. {
  7629. m_iComboHackCount += k;
  7630.  
  7631. if (m_iComboHackCount >= 10)
  7632. {
  7633. if (GetDesc())
  7634. if (GetDesc()->DelayedDisconnect(number(2, 7)))
  7635. {
  7636. sys_log(0, "COMBO_HACK_DISCONNECT: %s count: %d", GetName(), m_iComboHackCount);
  7637. LogManager::instance().HackLog("Combo", this);
  7638. }
  7639. }
  7640. }
  7641.  
  7642. void CHARACTER::ResetComboHackCount()
  7643. {
  7644. m_iComboHackCount = 0;
  7645. }
  7646.  
  7647. void CHARACTER::SkipComboAttackByTime(int interval)
  7648. {
  7649. m_dwSkipComboAttackByTime = get_dword_time() + interval;
  7650. }
  7651.  
  7652. DWORD CHARACTER::GetSkipComboAttackByTime() const
  7653. {
  7654. return m_dwSkipComboAttackByTime;
  7655. }
  7656.  
  7657. void CHARACTER::ResetChatCounter()
  7658. {
  7659. m_bChatCounter = 0;
  7660. }
  7661.  
  7662. BYTE CHARACTER::IncreaseChatCounter()
  7663. {
  7664. return ++m_bChatCounter;
  7665. }
  7666.  
  7667. BYTE CHARACTER::GetChatCounter() const
  7668. {
  7669. return m_bChatCounter;
  7670. }
  7671.  
  7672. // 말이나 다른것을 타고 있나?
  7673. bool CHARACTER::IsRiding() const
  7674. {
  7675. return IsHorseRiding() || GetMountVnum();
  7676. }
  7677.  
  7678. bool CHARACTER::CanWarp() const
  7679. {
  7680. const int iPulse = thecore_pulse();
  7681. const int limit_time = PASSES_PER_SEC(g_nPortalLimitTime);
  7682.  
  7683. if ((iPulse - GetSafeboxLoadTime()) < limit_time)
  7684. return false;
  7685.  
  7686. if ((iPulse - GetExchangeTime()) < limit_time)
  7687. return false;
  7688.  
  7689. if ((iPulse - GetMyShopTime()) < limit_time)
  7690. return false;
  7691.  
  7692. if ((iPulse - GetRefineTime()) < limit_time)
  7693. return false;
  7694.  
  7695. if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen())
  7696. return false;
  7697.  
  7698. return true;
  7699. }
  7700.  
  7701. DWORD CHARACTER::GetNextExp() const
  7702. {
  7703. if (PLAYER_EXP_TABLE_MAX < GetLevel())
  7704. return 2500000000U;
  7705. else
  7706. return exp_table[GetLevel()];
  7707. }
  7708.  
  7709. int CHARACTER::GetSkillPowerByLevel(int level, bool bMob) const
  7710. {
  7711. return CTableBySkill::instance().GetSkillPowerByLevelFromType(GetJob(), GetSkillGroup(), MINMAX(0, level, SKILL_MAX_LEVEL), bMob);
  7712. }
  7713.  
  7714. bool CHARACTER::CanWhisper()
  7715. {
  7716. return ((m_dwLastWhisperTime + 1000) < get_global_time()) ? true : false;
  7717. }
  7718.  
  7719. void CHARACTER::UpdateWhisperTime()
  7720. {
  7721. m_dwLastWhisperTime = get_global_time();
  7722. }
  7723.  
  7724. bool CHARACTER::SetCoins(long coins)
  7725. {
  7726. SQLMsg *msg;
  7727. msg = DBManager::instance().DirectQuery("UPDATE account.account SET coins = coins + '%ld' WHERE id = '%d'", coins, GetAID());
  7728. if (msg->uiSQLErrno != 0)
  7729. {
  7730. sys_err("Coins konnten nicht uebergeben werden.");
  7731. return false;
  7732. }
  7733. return true;
  7734. }
  7735.  
  7736. void CHARACTER::ChangeGold(long gold)
  7737. {
  7738.  
  7739. DBManager::instance().SendMoneyLog(MONEY_LOG_QUEST, GetPlayerID(), gold);
  7740. PointChange(POINT_GOLD, gold, true);
  7741. }
  7742.  
  7743. bool CHARACTER::IsEquipNewRingItem(DWORD dwItemVnum) const
  7744. {
  7745. {
  7746. LPITEM i = GetWear(WEAR_RING1);
  7747.  
  7748. if (i && i->GetVnum() == dwItemVnum)
  7749. {
  7750. return true;
  7751. }
  7752. }
  7753. return false;
  7754. }
  7755.  
  7756. #ifdef __SASH_SYSTEM__
  7757. void CHARACTER::OpenSash(bool bCombination)
  7758. {
  7759. if (isSashOpened(bCombination))
  7760. {
  7761. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The sash window it's already opened."));
  7762. return;
  7763. }
  7764.  
  7765. if (bCombination)
  7766. {
  7767. if (m_bSashAbsorption)
  7768. {
  7769. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Before you may close the sash absorption window."));
  7770. return;
  7771. }
  7772.  
  7773. m_bSashCombination = true;
  7774. }
  7775. else
  7776. {
  7777. if (m_bSashCombination)
  7778. {
  7779. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Before you may close the sash combine window."));
  7780. return;
  7781. }
  7782.  
  7783. m_bSashAbsorption = true;
  7784. }
  7785.  
  7786. TItemPos tPos;
  7787. tPos.window_type = INVENTORY;
  7788. tPos.cell = 0;
  7789.  
  7790. TPacketSash sPacket;
  7791. sPacket.header = HEADER_GC_SASH;
  7792. sPacket.subheader = SASH_SUBHEADER_GC_OPEN;
  7793. sPacket.bWindow = bCombination;
  7794. sPacket.dwPrice = 0;
  7795. sPacket.bPos = 0;
  7796. sPacket.tPos = tPos;
  7797. sPacket.dwItemVnum = 0;
  7798. sPacket.dwMinAbs = 0;
  7799. sPacket.dwMaxAbs = 0;
  7800. GetDesc()->Packet(&sPacket, sizeof(TPacketSash));
  7801.  
  7802. ClearSashMaterials();
  7803. }
  7804.  
  7805. void CHARACTER::CloseSash()
  7806. {
  7807. if ((!m_bSashCombination) && (!m_bSashAbsorption))
  7808. return;
  7809.  
  7810. bool bWindow = (m_bSashCombination == true ? true : false);
  7811.  
  7812. TItemPos tPos;
  7813. tPos.window_type = INVENTORY;
  7814. tPos.cell = 0;
  7815.  
  7816. TPacketSash sPacket;
  7817. sPacket.header = HEADER_GC_SASH;
  7818. sPacket.subheader = SASH_SUBHEADER_GC_CLOSE;
  7819. sPacket.bWindow = bWindow;
  7820. sPacket.dwPrice = 0;
  7821. sPacket.bPos = 0;
  7822. sPacket.tPos = tPos;
  7823. sPacket.dwItemVnum = 0;
  7824. sPacket.dwMinAbs = 0;
  7825. sPacket.dwMaxAbs = 0;
  7826. GetDesc()->Packet(&sPacket, sizeof(TPacketSash));
  7827.  
  7828. if (bWindow)
  7829. m_bSashCombination = false;
  7830. else
  7831. m_bSashAbsorption = false;
  7832.  
  7833. ClearSashMaterials();
  7834. }
  7835.  
  7836. void CHARACTER::ClearSashMaterials()
  7837. {
  7838. LPITEM * pkItemMaterial;
  7839. pkItemMaterial = GetSashMaterials();
  7840. for (int i = 0; i < SASH_WINDOW_MAX_MATERIALS; ++i)
  7841. {
  7842. if (!pkItemMaterial[i])
  7843. continue;
  7844.  
  7845. pkItemMaterial[i]->Lock(false);
  7846. pkItemMaterial[i] = NULL;
  7847. }
  7848. }
  7849.  
  7850. bool CHARACTER::SashIsSameGrade(long lGrade)
  7851. {
  7852. LPITEM * pkItemMaterial;
  7853. pkItemMaterial = GetSashMaterials();
  7854. if (!pkItemMaterial[0])
  7855. return false;
  7856.  
  7857. bool bReturn = (pkItemMaterial[0]->GetValue(SASH_GRADE_VALUE_FIELD) == lGrade ? true : false);
  7858. return bReturn;
  7859. }
  7860.  
  7861. long long CHARACTER::GetSashCombinePrice(long lGrade)
  7862. {
  7863. long long dwPrice = 0;
  7864. switch (lGrade)
  7865. {
  7866. case 2:
  7867. {
  7868. dwPrice = SASH_GRADE_2_PRICE;
  7869. }
  7870. break;
  7871. case 3:
  7872. {
  7873. dwPrice = SASH_GRADE_3_PRICE;
  7874. }
  7875. break;
  7876. case 4:
  7877. {
  7878. dwPrice = SASH_GRADE_4_PRICE;
  7879. }
  7880. break;
  7881. default:
  7882. {
  7883. dwPrice = SASH_GRADE_1_PRICE;
  7884. }
  7885. break;
  7886. }
  7887.  
  7888. return dwPrice;
  7889. }
  7890.  
  7891. BYTE CHARACTER::CheckEmptyMaterialSlot()
  7892. {
  7893. LPITEM * pkItemMaterial;
  7894. pkItemMaterial = GetSashMaterials();
  7895. for (int i = 0; i < SASH_WINDOW_MAX_MATERIALS; ++i)
  7896. {
  7897. if (!pkItemMaterial[i])
  7898. return i;
  7899. }
  7900.  
  7901. return 255;
  7902. }
  7903.  
  7904. void CHARACTER::GetSashCombineResult(DWORD & dwItemVnum, DWORD & dwMinAbs, DWORD & dwMaxAbs)
  7905. {
  7906. LPITEM * pkItemMaterial;
  7907. pkItemMaterial = GetSashMaterials();
  7908.  
  7909. if (m_bSashCombination)
  7910. {
  7911. if ((pkItemMaterial[0]) && (pkItemMaterial[1]))
  7912. {
  7913. long lVal = pkItemMaterial[0]->GetValue(SASH_GRADE_VALUE_FIELD);
  7914. if (lVal == 4)
  7915. {
  7916. dwItemVnum = pkItemMaterial[0]->GetOriginalVnum();
  7917. dwMinAbs = pkItemMaterial[0]->GetSocket(SASH_ABSORPTION_SOCKET);
  7918. DWORD dwMaxAbsCalc = (dwMinAbs + SASH_GRADE_4_ABS_RANGE > SASH_GRADE_4_ABS_MAX ? SASH_GRADE_4_ABS_MAX : (dwMinAbs + SASH_GRADE_4_ABS_RANGE));
  7919. dwMaxAbs = dwMaxAbsCalc;
  7920. }
  7921. else
  7922. {
  7923. DWORD dwMaskVnum = pkItemMaterial[0]->GetOriginalVnum();
  7924. TItemTable * pTable = ITEM_MANAGER::instance().GetTable(dwMaskVnum + 1);
  7925. if (pTable)
  7926. dwMaskVnum += 1;
  7927.  
  7928. dwItemVnum = dwMaskVnum;
  7929. switch (lVal)
  7930. {
  7931. case 2:
  7932. {
  7933. dwMinAbs = SASH_GRADE_3_ABS;
  7934. dwMaxAbs = SASH_GRADE_3_ABS;
  7935. }
  7936. break;
  7937. case 3:
  7938. {
  7939. dwMinAbs = SASH_GRADE_4_ABS_MIN;
  7940. dwMaxAbs = SASH_GRADE_4_ABS_MAX_COMB;
  7941. }
  7942. break;
  7943. default:
  7944. {
  7945. dwMinAbs = SASH_GRADE_2_ABS;
  7946. dwMaxAbs = SASH_GRADE_2_ABS;
  7947. }
  7948. break;
  7949. }
  7950. }
  7951. }
  7952. else
  7953. {
  7954. dwItemVnum = 0;
  7955. dwMinAbs = 0;
  7956. dwMaxAbs = 0;
  7957. }
  7958. }
  7959. else
  7960. {
  7961. if ((pkItemMaterial[0]) && (pkItemMaterial[1]))
  7962. {
  7963. dwItemVnum = pkItemMaterial[0]->GetOriginalVnum();
  7964. dwMinAbs = pkItemMaterial[0]->GetSocket(SASH_ABSORPTION_SOCKET);
  7965. dwMaxAbs = dwMinAbs;
  7966. }
  7967. else
  7968. {
  7969. dwItemVnum = 0;
  7970. dwMinAbs = 0;
  7971. dwMaxAbs = 0;
  7972. }
  7973. }
  7974. }
  7975.  
  7976. void CHARACTER::AddSashMaterial(TItemPos tPos, BYTE bPos)
  7977. {
  7978. if (bPos >= SASH_WINDOW_MAX_MATERIALS)
  7979. {
  7980. if (bPos == 255)
  7981. {
  7982. bPos = CheckEmptyMaterialSlot();
  7983. if (bPos >= SASH_WINDOW_MAX_MATERIALS)
  7984. return;
  7985. }
  7986. else
  7987. return;
  7988. }
  7989.  
  7990. LPITEM pkItem = GetItem(tPos);
  7991. if (!pkItem)
  7992. return;
  7993. else if ((pkItem->GetCell() >= INVENTORY_MAX_NUM) || (pkItem->IsEquipped()) || (tPos.IsBeltInventoryPosition()) || (pkItem->IsDragonSoul()))
  7994. return;
  7995. else if ((pkItem->GetType() != ITEM_COSTUME) && (m_bSashCombination))
  7996. return;
  7997. else if ((pkItem->GetType() != ITEM_COSTUME) && (bPos == 0) && (m_bSashAbsorption))
  7998. return;
  7999. else if (pkItem->isLocked())
  8000. {
  8001. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't add locked items."));
  8002. return;
  8003. }
  8004. #ifdef __SOULBINDING_SYSTEM__
  8005. else if ((pkItem->IsBind()) || (pkItem->IsUntilBind()))
  8006. {
  8007. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't add binded items."));
  8008. return;
  8009. }
  8010. #endif
  8011. else if ((m_bSashCombination) && (bPos == 1) && (!SashIsSameGrade(pkItem->GetValue(SASH_GRADE_VALUE_FIELD))))
  8012. {
  8013. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can combine just sashes of same grade."));
  8014. return;
  8015. }
  8016. else if ((m_bSashCombination) && (pkItem->GetSocket(SASH_ABSORPTION_SOCKET) >= SASH_GRADE_4_ABS_MAX))
  8017. {
  8018. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This sash got already maximum absorption chance."));
  8019. return;
  8020. }
  8021. else if ((bPos == 1) && (m_bSashAbsorption))
  8022. {
  8023. if ((pkItem->GetType() != ITEM_WEAPON) && (pkItem->GetType() != ITEM_ARMOR))
  8024. {
  8025. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can absorb just the bonuses from armors and weapons."));
  8026. return;
  8027. }
  8028. else if ((pkItem->GetType() == ITEM_ARMOR) && (pkItem->GetSubType() != ARMOR_BODY))
  8029. {
  8030. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can absorb just the bonuses from armors and weapons."));
  8031. return;
  8032. }
  8033. }
  8034. else if ((pkItem->GetSubType() != COSTUME_SASH) && (m_bSashCombination))
  8035. return;
  8036. else if ((pkItem->GetSubType() != COSTUME_SASH) && (bPos == 0) && (m_bSashAbsorption))
  8037. return;
  8038. else if ((pkItem->GetSocket(SASH_ABSORBED_SOCKET) > 0) && (bPos == 0) && (m_bSashAbsorption))
  8039. return;
  8040.  
  8041. LPITEM * pkItemMaterial;
  8042. pkItemMaterial = GetSashMaterials();
  8043. if ((bPos == 1) && (!pkItemMaterial[0]))
  8044. return;
  8045.  
  8046. if (pkItemMaterial[bPos])
  8047. return;
  8048.  
  8049. pkItemMaterial[bPos] = pkItem;
  8050. pkItemMaterial[bPos]->Lock(true);
  8051.  
  8052. DWORD dwItemVnum, dwMinAbs, dwMaxAbs;
  8053. GetSashCombineResult(dwItemVnum, dwMinAbs, dwMaxAbs);
  8054.  
  8055. TPacketSash sPacket;
  8056. sPacket.header = HEADER_GC_SASH;
  8057. sPacket.subheader = SASH_SUBHEADER_GC_ADDED;
  8058. sPacket.bWindow = m_bSashCombination == true ? true : false;
  8059. sPacket.dwPrice = GetSashCombinePrice(pkItem->GetValue(SASH_GRADE_VALUE_FIELD));
  8060. sPacket.bPos = bPos;
  8061. sPacket.tPos = tPos;
  8062. sPacket.dwItemVnum = dwItemVnum;
  8063. sPacket.dwMinAbs = dwMinAbs;
  8064. sPacket.dwMaxAbs = dwMaxAbs;
  8065. GetDesc()->Packet(&sPacket, sizeof(TPacketSash));
  8066. }
  8067.  
  8068. void CHARACTER::RemoveSashMaterial(BYTE bPos)
  8069. {
  8070. if (bPos >= SASH_WINDOW_MAX_MATERIALS)
  8071. return;
  8072.  
  8073. LPITEM * pkItemMaterial;
  8074. pkItemMaterial = GetSashMaterials();
  8075.  
  8076. long long dwPrice = 0;
  8077.  
  8078. if (bPos == 1)
  8079. {
  8080. if (pkItemMaterial[bPos])
  8081. {
  8082. pkItemMaterial[bPos]->Lock(false);
  8083. pkItemMaterial[bPos] = NULL;
  8084. }
  8085.  
  8086. if (pkItemMaterial[0])
  8087. long long dwPrice = GetSashCombinePrice(pkItemMaterial[0]->GetValue(SASH_GRADE_VALUE_FIELD));
  8088. }
  8089. else
  8090. ClearSashMaterials();
  8091.  
  8092. TItemPos tPos;
  8093. tPos.window_type = INVENTORY;
  8094. tPos.cell = 0;
  8095.  
  8096. TPacketSash sPacket;
  8097. sPacket.header = HEADER_GC_SASH;
  8098. sPacket.subheader = SASH_SUBHEADER_GC_REMOVED;
  8099. sPacket.bWindow = m_bSashCombination == true ? true : false;
  8100. sPacket.dwPrice = dwPrice;
  8101. sPacket.bPos = bPos;
  8102. sPacket.tPos = tPos;
  8103. sPacket.dwItemVnum = 0;
  8104. sPacket.dwMinAbs = 0;
  8105. sPacket.dwMaxAbs = 0;
  8106. GetDesc()->Packet(&sPacket, sizeof(TPacketSash));
  8107. }
  8108.  
  8109. BYTE CHARACTER::CanRefineSashMaterials()
  8110. {
  8111. BYTE bReturn = 0;
  8112. LPITEM * pkItemMaterial;
  8113. pkItemMaterial = GetSashMaterials();
  8114. if (m_bSashCombination)
  8115. {
  8116. for (int i = 0; i < SASH_WINDOW_MAX_MATERIALS; ++i)
  8117. {
  8118. if (pkItemMaterial[i])
  8119. {
  8120. if ((pkItemMaterial[i]->GetType() == ITEM_COSTUME) && (pkItemMaterial[i]->GetSubType() == COSTUME_SASH))
  8121. bReturn = 1;
  8122. else
  8123. {
  8124. bReturn = 0;
  8125. break;
  8126. }
  8127. }
  8128. else
  8129. {
  8130. bReturn = 0;
  8131. break;
  8132. }
  8133. }
  8134. }
  8135. else if (m_bSashAbsorption)
  8136. {
  8137. if ((pkItemMaterial[0]) && (pkItemMaterial[1]))
  8138. {
  8139. if ((pkItemMaterial[0]->GetType() == ITEM_COSTUME) && (pkItemMaterial[0]->GetSubType() == COSTUME_SASH))
  8140. bReturn = 2;
  8141. else
  8142. bReturn = 0;
  8143.  
  8144. if ((pkItemMaterial[1]->GetType() == ITEM_WEAPON) || ((pkItemMaterial[1]->GetType() == ITEM_ARMOR) && (pkItemMaterial[1]->GetSubType() == ARMOR_BODY)))
  8145. bReturn = 2;
  8146. else
  8147. bReturn = 0;
  8148.  
  8149. if (pkItemMaterial[0]->GetSocket(SASH_ABSORBED_SOCKET) > 0)
  8150. bReturn = 0;
  8151. }
  8152. else
  8153. bReturn = 0;
  8154. }
  8155.  
  8156. return bReturn;
  8157. }
  8158.  
  8159. void CHARACTER::RefineSashMaterials()
  8160. {
  8161. BYTE bCan = CanRefineSashMaterials();
  8162. if (bCan == 0)
  8163. return;
  8164.  
  8165. LPITEM * pkItemMaterial;
  8166. pkItemMaterial = GetSashMaterials();
  8167.  
  8168. DWORD dwItemVnum, dwMinAbs, dwMaxAbs;
  8169. GetSashCombineResult(dwItemVnum, dwMinAbs, dwMaxAbs);
  8170. long long dwPrice = GetSashCombinePrice(pkItemMaterial[0]->GetValue(SASH_GRADE_VALUE_FIELD));
  8171.  
  8172. if (bCan == 1)
  8173. {
  8174. int iSuccessChance = 0;
  8175. long lVal = pkItemMaterial[0]->GetValue(SASH_GRADE_VALUE_FIELD);
  8176. switch (lVal)
  8177. {
  8178. case 2:
  8179. {
  8180. iSuccessChance = SASH_COMBINE_GRADE_2;
  8181. }
  8182. break;
  8183. case 3:
  8184. {
  8185. iSuccessChance = SASH_COMBINE_GRADE_3;
  8186. }
  8187. break;
  8188. case 4:
  8189. {
  8190. iSuccessChance = SASH_COMBINE_GRADE_4;
  8191. }
  8192. break;
  8193. default:
  8194. {
  8195. iSuccessChance = SASH_COMBINE_GRADE_1;
  8196. }
  8197. break;
  8198. }
  8199.  
  8200. if (GetGold() < dwPrice)
  8201. {
  8202. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You don't have enough Yang."));
  8203. return;
  8204. }
  8205.  
  8206. dwPrice *= -1;
  8207. int iChance = number(1, 100);
  8208. bool bSucces = (iChance <= iSuccessChance ? true : false);
  8209. if (bSucces)
  8210. {
  8211. LPITEM pkItem = ITEM_MANAGER::instance().CreateItem(dwItemVnum, 1, 0, false);
  8212. if (!pkItem)
  8213. {
  8214. sys_err("%d can't be created.", dwItemVnum);
  8215. return;
  8216. }
  8217.  
  8218. ITEM_MANAGER::CopyAllAttrTo(pkItemMaterial[0], pkItem);
  8219. LogManager::instance().ItemLog(this, pkItem, "COMBINE SUCCESS", pkItem->GetName());
  8220. DWORD dwAbs = (dwMinAbs == dwMaxAbs ? dwMinAbs : number(dwMinAbs + 1, dwMaxAbs));
  8221. pkItem->SetSocket(SASH_ABSORPTION_SOCKET, dwAbs);
  8222. pkItem->SetSocket(SASH_ABSORBED_SOCKET, pkItemMaterial[0]->GetSocket(SASH_ABSORBED_SOCKET));
  8223.  
  8224. PointChange(POINT_GOLD, dwPrice);
  8225. DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, pkItemMaterial[0]->GetVnum(), dwPrice);
  8226.  
  8227. WORD wCell = pkItemMaterial[0]->GetCell();
  8228. ITEM_MANAGER::instance().RemoveItem(pkItemMaterial[0], "COMBINE (REFINE SUCCESS)");
  8229. ITEM_MANAGER::instance().RemoveItem(pkItemMaterial[1], "COMBINE (REFINE SUCCESS)");
  8230.  
  8231. pkItem->AddToCharacter(this, TItemPos(INVENTORY, wCell));
  8232. ITEM_MANAGER::instance().FlushDelayedSave(pkItem);
  8233. pkItem->AttrLog();
  8234.  
  8235. if (lVal == 4)
  8236. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("New absorption rate: %d%"), dwAbs);
  8237. else
  8238. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Success."));
  8239.  
  8240. EffectPacket(SE_EFFECT_SASH_SUCCEDED);
  8241. LogManager::instance().SashLog(GetPlayerID(), GetX(), GetY(), dwItemVnum, pkItem->GetID(), 1, dwAbs, 1);
  8242.  
  8243. ClearSashMaterials();
  8244. }
  8245. else
  8246. {
  8247. PointChange(POINT_GOLD, dwPrice);
  8248. DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, pkItemMaterial[0]->GetVnum(), dwPrice);
  8249.  
  8250. ITEM_MANAGER::instance().RemoveItem(pkItemMaterial[1], "COMBINE (REFINE FAIL)");
  8251.  
  8252. if (lVal == 4)
  8253. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("New absorption rate: %d%"), pkItemMaterial[0]->GetSocket(SASH_ABSORPTION_SOCKET));
  8254. else
  8255. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Failed."));
  8256.  
  8257. LogManager::instance().SashLog(GetPlayerID(), GetX(), GetY(), dwItemVnum, 0, 0, 0, 0);
  8258.  
  8259. pkItemMaterial[1] = NULL;
  8260. }
  8261.  
  8262. TItemPos tPos;
  8263. tPos.window_type = INVENTORY;
  8264. tPos.cell = 0;
  8265.  
  8266. TPacketSash sPacket;
  8267. sPacket.header = HEADER_GC_SASH;
  8268. sPacket.subheader = SASH_SUBHEADER_CG_REFINED;
  8269. sPacket.bWindow = m_bSashCombination == true ? true : false;
  8270. sPacket.dwPrice = dwPrice;
  8271. sPacket.bPos = 0;
  8272. sPacket.tPos = tPos;
  8273. sPacket.dwItemVnum = 0;
  8274. sPacket.dwMinAbs = 0;
  8275. if (bSucces)
  8276. sPacket.dwMaxAbs = 100;
  8277. else
  8278. sPacket.dwMaxAbs = 0;
  8279.  
  8280. GetDesc()->Packet(&sPacket, sizeof(TPacketSash));
  8281. }
  8282. else
  8283. {
  8284. pkItemMaterial[1]->CopyAttributeTo(pkItemMaterial[0]);
  8285. LogManager::instance().ItemLog(this, pkItemMaterial[0], "ABSORB (REFINE SUCCESS)", pkItemMaterial[0]->GetName());
  8286. pkItemMaterial[0]->SetSocket(SASH_ABSORBED_SOCKET, pkItemMaterial[1]->GetOriginalVnum());
  8287. for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; ++i)
  8288. {
  8289. if (pkItemMaterial[0]->GetAttributeValue(i) < 0)
  8290. pkItemMaterial[0]->SetForceAttribute(i, pkItemMaterial[0]->GetAttributeType(i), 0);
  8291. }
  8292.  
  8293. ITEM_MANAGER::instance().RemoveItem(pkItemMaterial[1], "ABSORBED (REFINE SUCCESS)");
  8294.  
  8295. ITEM_MANAGER::instance().FlushDelayedSave(pkItemMaterial[0]);
  8296. pkItemMaterial[0]->AttrLog();
  8297.  
  8298. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Success."));
  8299.  
  8300. ClearSashMaterials();
  8301.  
  8302. TItemPos tPos;
  8303. tPos.window_type = INVENTORY;
  8304. tPos.cell = 0;
  8305.  
  8306. TPacketSash sPacket;
  8307. sPacket.header = HEADER_GC_SASH;
  8308. sPacket.subheader = SASH_SUBHEADER_CG_REFINED;
  8309. sPacket.bWindow = m_bSashCombination == true ? true : false;
  8310. sPacket.dwPrice = dwPrice;
  8311. sPacket.bPos = 255;
  8312. sPacket.tPos = tPos;
  8313. sPacket.dwItemVnum = 0;
  8314. sPacket.dwMinAbs = 0;
  8315. sPacket.dwMaxAbs = 1;
  8316. GetDesc()->Packet(&sPacket, sizeof(TPacketSash));
  8317. }
  8318. }
  8319.  
  8320. bool CHARACTER::CleanSashAttr(LPITEM pkItem, LPITEM pkTarget)
  8321. {
  8322. if (!CanHandleItem())
  8323. return false;
  8324. else if ((!pkItem) || (!pkTarget))
  8325. return false;
  8326. else if ((pkTarget->GetType() != ITEM_COSTUME) && (pkTarget->GetSubType() != COSTUME_SASH))
  8327. return false;
  8328.  
  8329. if (pkTarget->GetSocket(SASH_ABSORBED_SOCKET) <= 0)
  8330. return false;
  8331.  
  8332. pkTarget->SetSocket(SASH_ABSORBED_SOCKET, 0);
  8333. for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; ++i)
  8334. pkTarget->SetForceAttribute(i, 0, 0);
  8335.  
  8336. pkItem->SetCount(pkItem->GetCount() - 1);
  8337. LogManager::instance().ItemLog(this, pkTarget, "USE_DETACHMENT (CLEAN ATTR)", pkTarget->GetName());
  8338. return true;
  8339. }
  8340. #endif
  8341.  
  8342. #ifdef GIFT_SYSTEM
  8343. #include <boost/algorithm/string.hpp>
  8344. EVENTFUNC(gift_refresh_event)
  8345. {
  8346. char_event_info* info = dynamic_cast<char_event_info*>(event->info);
  8347. if (info == NULL)
  8348. {
  8349. sys_err("gift_refresh_event> <Factor> Null pointer");
  8350. return 0;
  8351. }
  8352.  
  8353. LPCHARACTER ch = info->ch;
  8354.  
  8355. if (NULL == ch || ch->IsNPC())
  8356. return 0;
  8357. ch->RefreshGift();
  8358. return PASSES_PER_SEC(5 * 60);
  8359. }
  8360. void CHARACTER::StartRefreshGift()
  8361. {
  8362. if (m_pkGiftRefresh)
  8363. return;
  8364. char_event_info* info = AllocEventInfo<char_event_info>();
  8365.  
  8366. info->ch = this;
  8367. m_pkGiftRefresh = event_create(gift_refresh_event, info, PASSES_PER_SEC(1)); // 1o?
  8368. }
  8369. void SendGift(LPCHARACTER ch, TGiftItem item)
  8370. {
  8371. char szSockets[1024] = { '\0' };
  8372. char *tempSockets = szSockets;
  8373. for (int soc = 0; soc < ITEM_SOCKET_MAX_NUM; soc++)
  8374. tempSockets += sprintf(tempSockets, "%ld%s", item.alSockets[soc], (soc<ITEM_SOCKET_MAX_NUM - 1 ? "|" : ""));
  8375. char szAttrs[1024] = { '\0' };
  8376. char *tempAttrs = szAttrs;
  8377. for (int attr = 0; attr < ITEM_ATTRIBUTE_MAX_NUM; attr++)
  8378. tempAttrs += sprintf(tempAttrs, "%u,%d%s", item.aAttr[attr].bType, item.aAttr[attr].sValue, (attr<ITEM_ATTRIBUTE_MAX_NUM - 1 ? "|" : ""));
  8379. #ifdef FULL_YANG
  8380. ch->ChatPacket(CHAT_TYPE_COMMAND, "gift_item %d %d %lld %d %d %s %s %s %s", item.id, item.vnum, item.count, item.pos, item.dwDateAdd, item.szFrom, item.szReason, szSockets, szAttrs);
  8381. #else
  8382. ch->ChatPacket(CHAT_TYPE_COMMAND, "gift_item %d %d %d %d %d %s %s %s %s", item.id, item.vnum, item.count, item.pos, item.dwDateAdd, item.szFrom, item.szReason, szSockets, szAttrs);
  8383. #endif
  8384. }
  8385. void CHARACTER::LoadGiftPage(int page)
  8386. {
  8387. page = MINMAX(1, page, m_mapGiftGrid.size());
  8388. ChatPacket(CHAT_TYPE_COMMAND, "gift_clear");
  8389. GIFT_MAP::iterator it = m_mapGiftGrid.find(page);
  8390. if (it == m_mapGiftGrid.end())
  8391. return;
  8392. m_dwLastGiftPage = page;
  8393. for (int i = 0; i < it->second.size(); i++)
  8394. SendGift(this, it->second[i]);
  8395. ChatPacket(CHAT_TYPE_COMMAND, "gift_load");
  8396.  
  8397. }
  8398. static CGrid* grid;
  8399. void CHARACTER::AddGiftGrid(int page)
  8400. {
  8401. if (m_mapGiftGrid.find(page) == m_mapGiftGrid.end())
  8402. {
  8403. std::vector<TGiftItem> vec;
  8404. m_mapGiftGrid.insert(std::make_pair(page, vec));
  8405. }
  8406.  
  8407. }
  8408. int CHARACTER::AddGiftGridItem( int page, int size)
  8409. {
  8410. AddGiftGrid(page);
  8411. int iPos = grid->FindBlank(1, size);
  8412.  
  8413. if (iPos < 0)
  8414. return -1;
  8415.  
  8416. if (!grid->IsEmpty(iPos, 1, size))
  8417. return -1;
  8418.  
  8419.  
  8420. grid->Put(iPos, 1, size);
  8421. return iPos;
  8422.  
  8423. }
  8424. void CHARACTER::RefreshGift()
  8425. {
  8426. char szSockets[1024] = { '\0' };
  8427. char *tempSockets = szSockets;
  8428. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++)
  8429. {
  8430. tempSockets += sprintf(tempSockets, "socket%d", i);
  8431.  
  8432. if (i<ITEM_SOCKET_MAX_NUM - 1)
  8433. tempSockets += sprintf(tempSockets, ",");
  8434. }
  8435. char szAttrs[1024] = { '\0' };
  8436. char *tempAttrs = szAttrs;
  8437. for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; i++)
  8438. {
  8439. if (i < 7)
  8440. tempAttrs += sprintf(tempAttrs, "attrtype%d,attrvalue%d", i, i);
  8441. else
  8442. tempAttrs += sprintf(tempAttrs, "applytype%d,applyvalue%d", i - 7, i - 7);
  8443. if (i<ITEM_ATTRIBUTE_MAX_NUM - 1)
  8444. tempAttrs += sprintf(tempAttrs, ",");
  8445. }
  8446. SQLMsg * pkMsg(DBManager::instance().DirectQuery("SELECT id,vnum,count,unix_timestamp(date_add),replace(reason,' ','_'),replace(`from`,' ','_'),%s,%s from player_gift where owner_id = %d and status = 'WAIT' ORDER BY date_add ", szSockets, szAttrs, GetPlayerID()));
  8447. SQLResult * pRes = pkMsg->Get();
  8448. m_mapGiftGrid.clear();
  8449. grid = M2_NEW CGrid(15, 8);
  8450. grid->Clear();
  8451. int page = 1;
  8452. if (pRes->uiNumRows>0)
  8453. {
  8454. AddGiftGrid(page);
  8455. MYSQL_ROW row;
  8456. while ((row = mysql_fetch_row(pRes->pSQLResult)) != NULL)
  8457. {
  8458. int col = 0;
  8459. TGiftItem item;
  8460. memset(&item, 0, sizeof(item));
  8461. str_to_number(item.id, row[col++]);
  8462. str_to_number(item.vnum, row[col++]);
  8463. str_to_number(item.count, row[col++]);
  8464. str_to_number(item.dwDateAdd, row[col++]);
  8465. strlcpy(item.szReason, row[col++], sizeof(item.szReason) - 1);
  8466. strlcpy(item.szFrom, row[col++], sizeof(item.szFrom) - 1);
  8467. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++)
  8468. str_to_number(item.alSockets[i], row[col++]);
  8469. for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; i++)
  8470. {
  8471. str_to_number(item.aAttr[i].bType, row[col++]);
  8472. str_to_number(item.aAttr[i].sValue, row[col++]);
  8473. }
  8474.  
  8475. const TItemTable * item_table = ITEM_MANAGER::instance().GetTable(item.vnum);
  8476. if (!item_table)
  8477. continue;
  8478. int iPos = AddGiftGridItem( page, item_table->bSize);
  8479. if (iPos < 0)
  8480. {
  8481. page++;
  8482. grid->Clear();
  8483. iPos = AddGiftGridItem(page, item_table->bSize);
  8484. if (iPos < 0)
  8485. {
  8486. sys_err("iPos <0");
  8487. break;
  8488. }
  8489. }
  8490. item.pos = iPos;
  8491. m_mapGiftGrid[page].push_back(item);
  8492.  
  8493. }
  8494. M2_DELETE(grid);
  8495. }
  8496. if(GetGiftPages()>0)
  8497. ChatPacket(CHAT_TYPE_COMMAND, "gift_info %d", GetGiftPages());
  8498. }
  8499. #endif
  8500.  
  8501. #ifdef OFFLINE_SHOP
  8502.  
  8503. extern std::map<DWORD, DWORD> g_ShopIndexCount;
  8504. extern std::map<int, TShopCost> g_ShopCosts;
  8505. void CHARACTER::OpenMyShop(const char * c_pszSign, TShopItemTable * pTable, BYTE bItemCount, DWORD id)
  8506. {
  8507.  
  8508. if (g_ShopCosts.find(id) == g_ShopCosts.end())
  8509. {
  8510. sys_log(0, "Shop days error %s %d", c_pszSign, id);
  8511. return;
  8512. }
  8513. int days = g_ShopCosts[id].days;
  8514. long long price = g_ShopCosts[id].price;
  8515. if (price > 0 && GetGold() < price)
  8516. {
  8517. ChatPacket(CHAT_TYPE_INFO, LC_TEXT( "SHOP_NEED_MONEY"), price);
  8518. return;
  8519. }
  8520. if (days == 0 && GetPart(PART_MAIN) > 2)
  8521. {
  8522. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("°ⓒ???≫ ???i?ß °ł?I ≫oAˇ?≫ ?­ ?o ?O˝?´?´?."));
  8523. return;
  8524. }
  8525.  
  8526. if (GetMyShop())
  8527. {
  8528. CloseMyShop();
  8529. return;
  8530. }
  8531.  
  8532.  
  8533. int gShopMax = quest::CQuestManager::Instance().GetEventFlag("shop_max");
  8534. if (m_mapshops.size() >= (gShopMax ? gShopMax : 2))
  8535. {
  8536. ChatPacket(CHAT_TYPE_INFO, LC_TEXT( "SHOP_MAX_ERR"));
  8537. return;
  8538. }
  8539.  
  8540.  
  8541. if (g_ShopIndexCount.count(GetMapIndex())>0)
  8542. {
  8543. int shop_max = g_ShopIndexCount[GetMapIndex()];
  8544. bool block = false;
  8545.  
  8546. #ifdef SHOP_ONLY_ALLOWED_INDEX
  8547. if (shop_max > 0)
  8548. {
  8549. #else
  8550. if (shop_max == 0)
  8551. block = true;
  8552. else{
  8553. #endif
  8554. std::auto_ptr<SQLMsg> pkMsg(DBManager::instance().DirectQuery("SELECT map_index from player_shop WHERE channel=%d and status='OK' and map_index=%d", g_bChannel, GetMapIndex()));
  8555. SQLResult * pRes = pkMsg->Get();
  8556. if (pRes->uiNumRows >= shop_max)
  8557. block = true;
  8558. }
  8559. if (block)
  8560. {
  8561. ChatPacket(CHAT_TYPE_INFO, LC_TEXT( "SHOP_MAP_MAX"));
  8562. return;
  8563. }
  8564. }
  8565. #ifdef SHOP_ONLY_ALLOWED_INDEX
  8566. else
  8567. {
  8568. ChatPacket(CHAT_TYPE_INFO, LC_TEXT( "SHOP_CANNOT_OPEN_HERE"));
  8569. return;
  8570. }
  8571. #endif
  8572. if (GetMyShop()) // AI1I 1YAI ?­·A AOA¸¸e ´Y´A´U.
  8573. {
  8574. CloseMyShop();
  8575. return;
  8576. }
  8577. #ifndef FULL_YANG
  8578. int64_t nTotalMoney = 0;
  8579.  
  8580. for (int n = 0; n < bItemCount; ++n)
  8581. {
  8582. nTotalMoney += static_cast<int64_t> ((pTable + n)->price);
  8583. }
  8584.  
  8585. nTotalMoney += static_cast<int64_t> (GetGold());
  8586.  
  8587. if (GOLD_MAX <= nTotalMoney)
  8588. {
  8589. sys_err("[OVERFLOW_GOLD] Overflow (GOLD_MAX) id %u name %s", GetPlayerID(), GetName());
  8590. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("20?? łE?≫ ??°uC??ⓒ ≫oAˇ?≫ ?­?o°ˇ ??˝?´?´?"));
  8591. return;
  8592. }
  8593. #endif
  8594. #ifdef ACCE_FIX
  8595. if(IsAcceOpen())
  8596. Acce_close(this);
  8597. #endif
  8598. quest::PC * pPC = quest::CQuestManager::instance().GetPCForce(GetPlayerID());
  8599. if (pPC->IsRunning())
  8600. return;
  8601. std::vector<TShopItemTable *> map_shop;
  8602. for (BYTE i = 0; i < bItemCount; ++i)
  8603. {
  8604. // ANTI_GIVE, ANTI_MYSHOP check
  8605. LPITEM pkItem = GetItem((pTable + i)->pos);
  8606.  
  8607. if (pkItem)
  8608. {
  8609. const TItemTable * item_table = pkItem->GetProto();
  8610.  
  8611. if (item_table && (IS_SET(item_table->dwAntiFlags, ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_MYSHOP)))
  8612. {
  8613. ChatPacket(CHAT_TYPE_INFO, LC_TEXT( "??·a?­ ???????? °ł?I≫oAˇ?ˇ?­ ?C¸?C? ?o ??˝?´?´?."));
  8614. return;
  8615. }
  8616.  
  8617. if (pkItem->IsEquipped())
  8618. {
  8619. ChatPacket(CHAT_TYPE_INFO, LC_TEXT( "????Aß?I ???????? °ł?I≫oAˇ?ˇ?­ ?C¸?C? ?o ??˝?´?´?."));
  8620. return;
  8621. }
  8622.  
  8623. if (pkItem->isLocked())
  8624. {
  8625. ChatPacket(CHAT_TYPE_INFO, LC_TEXT( "≫c?eAß?I ???????? °ł?I≫oAˇ?ˇ?­ ?C¸?C? ?o ??˝?´?´?."));
  8626. return;
  8627. }
  8628. if (pkItem->GetOwner() != this)
  8629. {
  8630. return;
  8631. }
  8632. #ifdef SOULBIND_SYSTEM
  8633. if (pkItem->IsSoulBind())
  8634. {
  8635. ChatPacket(CHAT_TYPE_INFO, LC_TEXT( "You can't sell in private shop item with soul bind."));
  8636. return;
  8637. }
  8638. #endif
  8639. }
  8640. if (days != 0)
  8641. map_shop.push_back(pTable++);
  8642. }
  8643. if (days != 0 && map_shop.size() == 0)
  8644. return;
  8645. char szName[256];
  8646. DBManager::instance().EscapeString(szName, 256, c_pszSign, strlen(c_pszSign));
  8647. m_stShopSign = szName;
  8648. boost::replace_all(m_stShopSign, "%", "%%");
  8649.  
  8650.  
  8651. if (m_stShopSign.length()>30)
  8652. m_stShopSign.resize(30);
  8653. if (m_stShopSign.length() == 0)
  8654. return;
  8655. #ifdef STRING_PROTECTION
  8656. if (CBanwordManager::instance().CheckString(m_stShopSign.c_str(), m_stShopSign.length()) != "")
  8657. {
  8658. ChatPacket(CHAT_TYPE_INFO, LC_TEXT( "???O?ił? ???i°ˇ ?÷COμ? ≫oAˇ ??¸§?¸·I ≫oAˇ?≫ ?­ ?o ??˝?´?´?."));
  8659. return;
  8660. }
  8661. #else
  8662. if (CBanwordManager::instance().CheckString(m_stShopSign.c_str(), m_stShopSign.length()))
  8663. {
  8664. ChatPacket(CHAT_TYPE_INFO, LC_TEXT( "???O?ił? ???i°ˇ ?÷COμ? ≫oAˇ ??¸§?¸·I ≫oAˇ?≫ ?­ ?o ??˝?´?´?."));
  8665. return;
  8666. }
  8667. #endif
  8668. if (m_pkExchange)
  8669. m_pkExchange->Cancel();
  8670. if (price > 0)
  8671. #ifdef FULL_YANG_OWN
  8672. ChangeGold(-price);
  8673. #else
  8674. PointChange(POINT_GOLD, -price);
  8675. #endif
  8676. if (days != 0)
  8677. {
  8678. CShopManager::instance().CreateOfflineShop(this, m_stShopSign.c_str(), map_shop, id);
  8679. m_stShopSign.clear();
  8680. return;
  8681. }
  8682. TPacketGCShopSign p;
  8683. p.bHeader = HEADER_GC_SHOP_SIGN;
  8684. p.dwVID = GetVID();
  8685. strlcpy(p.szSign, m_stShopSign.c_str(), sizeof(p.szSign));
  8686.  
  8687. PacketAround(&p, sizeof(TPacketGCShopSign));
  8688.  
  8689. m_pkMyShop = CShopManager::instance().CreatePCShop(this, pTable, bItemCount);
  8690. if (!m_pkMyShop)
  8691. {
  8692. sys_err("Cannot open normal shop %s %s", GetName(), m_stShopSign.c_str());
  8693. return;
  8694. }
  8695. if (IsPolymorphed() == true)
  8696. {
  8697. RemoveAffect(AFFECT_POLYMORPH);
  8698. }
  8699.  
  8700. if (GetHorse())
  8701. {
  8702. HorseSummon(false, true);
  8703. }
  8704. else if (GetMountVnum())
  8705. {
  8706. RemoveAffect(AFFECT_MOUNT);
  8707. RemoveAffect(AFFECT_MOUNT_BONUS);
  8708. }
  8709.  
  8710. SetPolymorph(30000, true);
  8711.  
  8712. }
  8713. void CHARACTER::SetShopSign(const char * name)
  8714. {
  8715. m_stShopSign = name;
  8716. TPacketGCShopSign p;
  8717. p.bHeader = HEADER_GC_SHOP_SIGN;
  8718. p.dwVID = GetVID();
  8719. strlcpy(p.szSign, m_stShopSign.c_str(), sizeof(p.szSign));
  8720. PacketAround(&p, sizeof(TPacketGCShopSign));
  8721. }
  8722.  
  8723. TPrivShop CHARACTER::GetPrivShopTable(DWORD id)
  8724. {
  8725. TPrivShop shop;
  8726. memset(&shop, 0, sizeof(TPrivShop));
  8727. PSHOP_MAP::iterator it = m_mapshops.find(id);
  8728. if (it != m_mapshops.end())
  8729. return it->second;
  8730. return shop;
  8731.  
  8732. }
  8733. void CHARACTER::UpdatePrivShopTable(DWORD id, TPrivShop shop)
  8734. {
  8735. if (m_mapshops.find(id) != m_mapshops.end())
  8736. m_mapshops[id]=shop;
  8737. SendShops();
  8738. }
  8739. void CHARACTER::RemovePrivShopTable(DWORD id)
  8740. {
  8741. if (m_mapshops.find(id) != m_mapshops.end())
  8742. m_mapshops.erase(m_mapshops.find(id));
  8743. SendShops();
  8744. }
  8745. void CHARACTER::UpdateShopItems()
  8746. {
  8747. LPSHOP npcshop = GetMyShop();
  8748. LPCHARACTER owner = CHARACTER_MANAGER::instance().FindByPID(GetPrivShopOwner());
  8749. if (!npcshop || !IsPrivShop())
  8750. return;
  8751. npcshop->RemoveGuests(owner);
  8752. npcshop->SetLocked(true);
  8753. npcshop->ClearItems();
  8754. std::vector<TShopItemTable *> map_shop;
  8755. char szSockets[1024] = { '\0' };
  8756. char *tempSockets = szSockets;
  8757. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++)
  8758. {
  8759. tempSockets += sprintf(tempSockets, "socket%d", i);
  8760.  
  8761. if (i<ITEM_SOCKET_MAX_NUM - 1)
  8762. tempSockets += sprintf(tempSockets, ",");
  8763. }
  8764. char szAttrs[1024] = { '\0' };
  8765. char *tempAttrs = szAttrs;
  8766. for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; i++)
  8767. {
  8768. if (i < 7)
  8769. tempAttrs += sprintf(tempAttrs, "attrtype%d,attrvalue%d", i, i);
  8770. else
  8771. tempAttrs += sprintf(tempAttrs, "applytype%d,applyvalue%d", i - 7, i - 7);
  8772. if (i<ITEM_ATTRIBUTE_MAX_NUM - 1)
  8773. tempAttrs += sprintf(tempAttrs, ",");
  8774. }
  8775. SQLMsg * Msg(DBManager::instance().DirectQuery("SELECT id,vnum,count,display_pos,price,%s,%s from player_shop_items where shop_id='%d'",szSockets,szAttrs, GetPrivShop()));
  8776. SQLResult * Res = Msg->Get();
  8777. if (Res->uiNumRows > 0)
  8778. {
  8779. MYSQL_ROW row;
  8780. while ((row = mysql_fetch_row(Res->pSQLResult)) != NULL)
  8781. {
  8782.  
  8783. int col = 0;
  8784.  
  8785.  
  8786.  
  8787. TShopItemTable *shop = new TShopItemTable;
  8788. memset(shop, 0, sizeof(TShopItemTable));
  8789. DWORD id;
  8790. shop->pos.window_type = INVENTORY;
  8791. str_to_number(id, row[col++]);
  8792. str_to_number(shop->vnum, row[col++]);
  8793. str_to_number(shop->count, row[col++]);
  8794. str_to_number(shop->display_pos, row[col++]);
  8795. //col++;
  8796. str_to_number(shop->price, row[col++]);
  8797.  
  8798. const TItemTable * item_table = ITEM_MANAGER::instance().GetTable(shop->vnum);
  8799.  
  8800.  
  8801. if (!item_table)
  8802. {
  8803. sys_err("Shop: no item table by item vnum #%d", shop->vnum);
  8804. continue;
  8805. }
  8806. for (int i = 0; i < INVENTORY_MAX_NUM; ++i)
  8807. {
  8808. if (NULL != GetInventoryItem(i) && GetInventoryItem(i)->GetRealID() == id)
  8809. GetInventoryItem(i)->RemoveFromCharacter();
  8810. }
  8811. LPITEM item = ITEM_MANAGER::instance().CreateItem(shop->vnum, shop->count, id, false, -1, true);
  8812. if (-1 == (shop->pos.cell = GetEmptyInventory(item_table->bSize)))
  8813. {
  8814. sys_err("no empty position in npc inventory");
  8815. return;
  8816. }
  8817. if (item)
  8818. {
  8819. item->ClearAttribute();
  8820. item->SetRealID(id);
  8821. for (int s = 0;s<ITEM_SOCKET_MAX_NUM;s++)
  8822. {
  8823. DWORD soc;
  8824. str_to_number(soc, row[col++]);
  8825. item->SetSocket(s, soc, false);
  8826. }
  8827. for (int at = 0;at<ITEM_ATTRIBUTE_MAX_NUM;at++)
  8828. {
  8829. DWORD attr;
  8830. long val;
  8831. str_to_number(attr, row[col++]);
  8832. str_to_number(val, row[col++]);
  8833. item->SetForceAttribute(at, attr, val);
  8834. }
  8835.  
  8836. item->AddToCharacter(this, shop->pos);
  8837. }
  8838. else
  8839. {
  8840. sys_err("%d is not item", shop->vnum);
  8841. continue;
  8842. }
  8843. map_shop.push_back(shop);
  8844. }
  8845. }
  8846.  
  8847.  
  8848.  
  8849. if (map_shop.size() == 0 || map_shop.size() > SHOP_HOST_ITEM_MAX_NUM)
  8850. {
  8851. DeleteMyShop();
  8852. return;
  8853. }
  8854. npcshop->SetPrivShopItems(map_shop);
  8855. npcshop->SetLocked(false);
  8856. for (int i = 0;i < SHOP_HOST_ITEM_MAX_NUM;i++)
  8857. npcshop->BroadcastUpdateItem(i);
  8858.  
  8859. if (owner)
  8860. {
  8861. owner->LoadPrivShops();
  8862. owner->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("SHOP_EDIT_SUCCESS"));
  8863. }
  8864. }
  8865. void CHARACTER::LoadPrivShops()
  8866. {
  8867. m_mapshops.clear();
  8868. SQLMsg * pkMsg(DBManager::instance().DirectQuery("SELECT id,shop_vid,replace(replace(name,' ','\\\\'),'#','_'),gold,item_count,(select count(id) from player_shop_items where shop_id=player_shop.id),DATEDIFF(date_close,now()) AS days,UNIX_TIMESTAMP(date_close) from player_shop WHERE player_id=%d", GetPlayerID()));
  8869. SQLResult * pRes = pkMsg->Get();
  8870. if (pRes->uiNumRows>0)
  8871. {
  8872.  
  8873. MYSQL_ROW row;
  8874. while ((row = mysql_fetch_row(pRes->pSQLResult)) != NULL)
  8875. {
  8876. int col = 0;
  8877. TPrivShop shop;
  8878. memset(&shop, 0, sizeof(TPrivShop));
  8879. str_to_number(shop.shop_id, row[col++]);
  8880. str_to_number(shop.shop_vid, row[col++]);
  8881. strlcpy(shop.szSign, row[col++], sizeof(shop.szSign));
  8882. str_to_number(shop.gold, row[col++]);
  8883. str_to_number(shop.item_count, row[col++]);
  8884. str_to_number(shop.rest_count, row[col++]);
  8885. str_to_number(shop.days, row[col++]);
  8886. str_to_number(shop.date_close, row[col++]);
  8887. m_mapshops.insert(std::make_pair(shop.shop_id, shop));
  8888. }
  8889. }
  8890. SendShops();
  8891. }
  8892. void CHARACTER::SendShops(bool isGm)
  8893. {
  8894. ChatPacket(CHAT_TYPE_COMMAND, "shop_clear");
  8895. PSHOP_MAP::iterator it = m_mapshops.begin();
  8896. while (it != m_mapshops.end())
  8897. {
  8898. ChatPacket(CHAT_TYPE_COMMAND, "shop_add %d %d %s %lld %d %d %d %d", it->second.shop_id, it->second.shop_vid, it->second.szSign, it->second.gold, it->second.item_count, it->second.rest_count, it->second.days, it->second.date_close);
  8899. it++;
  8900. }
  8901.  
  8902.  
  8903. }
  8904. void CHARACTER::SendShopCost()
  8905. {
  8906. extern std::map<int, TShopCost> g_ShopCosts;
  8907. ChatPacket(CHAT_TYPE_COMMAND, "shop_cost_clear");
  8908. std::map<int, TShopCost>::iterator it = g_ShopCosts.begin();
  8909. while (it != g_ShopCosts.end())
  8910. {
  8911. ChatPacket(CHAT_TYPE_COMMAND, "shop_cost %d %d %d %lld", it->first,it->second.days, it->second.time, it->second.price);
  8912. it++;
  8913. }
  8914.  
  8915.  
  8916. }
  8917. void CHARACTER::OpenShop(DWORD id, const char *name, bool onboot)
  8918. {
  8919. if (GetMyShop())
  8920. {
  8921. CloseMyShop();
  8922. return;
  8923. }
  8924. if (IsPC())
  8925. return;
  8926. char szSockets[1024] = { '\0' };
  8927. char *tempSockets = szSockets;
  8928. for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++)
  8929. {
  8930. tempSockets += sprintf(tempSockets, "socket%d", i);
  8931.  
  8932. if (i<ITEM_SOCKET_MAX_NUM - 1)
  8933. tempSockets += sprintf(tempSockets, ",");
  8934. }
  8935. char szAttrs[1024] = { '\0' };
  8936. char *tempAttrs = szAttrs;
  8937. for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; i++)
  8938. {
  8939. if (i < 7)
  8940. tempAttrs += sprintf(tempAttrs, "attrtype%d,attrvalue%d", i, i);
  8941. else
  8942. tempAttrs += sprintf(tempAttrs, "applytype%d,applyvalue%d", i - 7, i - 7);
  8943. if (i<ITEM_ATTRIBUTE_MAX_NUM - 1)
  8944. tempAttrs += sprintf(tempAttrs, ",");
  8945. }
  8946. SQLMsg * pkMsg(DBManager::instance().DirectQuery("SELECT id,vnum,count,display_pos,price,%s,%s from player_shop_items where shop_id='%d'",szSockets,szAttrs, id));
  8947.  
  8948. SQLResult * pRes = pkMsg->Get();
  8949. BYTE bItemCount = pRes->uiNumRows;
  8950.  
  8951. TShopItemTable * m_pShopTable = new TShopItemTable[bItemCount];
  8952. memset(&m_pShopTable[0], 0, sizeof(TShopItemTable) * bItemCount);
  8953.  
  8954. std::vector<TShopItemTable *> map_shop;
  8955.  
  8956. if (bItemCount>0)
  8957. {
  8958. bItemCount = 0;
  8959. MYSQL_ROW row;
  8960. int c = 0;
  8961. while ((row = mysql_fetch_row(pRes->pSQLResult)) != NULL)
  8962. {
  8963. int col = 0;
  8964.  
  8965.  
  8966.  
  8967. TShopItemTable *shop = new TShopItemTable;
  8968. memset(shop, 0, sizeof(TShopItemTable));
  8969. DWORD id;
  8970. shop->pos.window_type = INVENTORY;
  8971. str_to_number(id, row[col++]);
  8972. str_to_number(shop->vnum, row[col++]);
  8973. str_to_number(shop->count, row[col++]);
  8974. str_to_number(shop->display_pos, row[col++]);
  8975. //col++;
  8976. str_to_number(shop->price, row[col++]);
  8977.  
  8978. const TItemTable * item_table = ITEM_MANAGER::instance().GetTable(shop->vnum);
  8979.  
  8980.  
  8981. if (!item_table)
  8982. {
  8983. sys_err("Shop: no item table by item vnum #%d", shop->vnum);
  8984. continue;
  8985. }
  8986. if (-1 == (shop->pos.cell = GetEmptyInventory(item_table->bSize)))
  8987. {
  8988. sys_err("no empty position in npc inventory");
  8989. return;
  8990. }
  8991.  
  8992. LPITEM item = ITEM_MANAGER::instance().CreateItem(shop->vnum, shop->count, 0, false, -1, true);
  8993.  
  8994. if (item)
  8995. {
  8996. item->ClearAttribute();
  8997. item->SetSkipSave(true);
  8998. item->SetRealID(id);
  8999. for (int s = 0;s<ITEM_SOCKET_MAX_NUM;s++)
  9000. {
  9001. DWORD soc;
  9002. str_to_number(soc, row[col++]);
  9003. item->SetSocket(s, soc, false);
  9004. }
  9005. for (int at = 0;at<ITEM_ATTRIBUTE_MAX_NUM;at++)
  9006. {
  9007. DWORD attr;
  9008. long val;
  9009. str_to_number(attr, row[col++]);
  9010. str_to_number(val, row[col++]);
  9011. item->SetForceAttribute(at, attr, val);
  9012. }
  9013.  
  9014.  
  9015. item->AddToCharacter(this, shop->pos);
  9016. }
  9017. else
  9018. {
  9019. sys_err("%d is not item", shop->vnum);
  9020. continue;
  9021. }
  9022.  
  9023. map_shop.push_back(shop);
  9024. ++bItemCount;
  9025. }
  9026. }
  9027.  
  9028.  
  9029.  
  9030. if (bItemCount == 0 && !onboot || bItemCount > SHOP_HOST_ITEM_MAX_NUM)
  9031. return;
  9032.  
  9033.  
  9034. m_stShopSign = name;
  9035.  
  9036. if (m_stShopSign.length()>30)
  9037. m_stShopSign.resize(30);
  9038. if (m_stShopSign.length() == 0)
  9039. return;
  9040.  
  9041.  
  9042. TPacketGCShopSign p;
  9043.  
  9044. p.bHeader = HEADER_GC_SHOP_SIGN;
  9045. p.dwVID = GetVID();
  9046. strlcpy(p.szSign, m_stShopSign.c_str(), sizeof(p.szSign));
  9047.  
  9048. PacketAround(&p, sizeof(TPacketGCShopSign));
  9049.  
  9050. m_pkMyShop = CShopManager::instance().CreateNPCShop(this, map_shop);
  9051.  
  9052. }
  9053. void CHARACTER::DeleteMyShop()
  9054. {
  9055. if (GetMyShop())
  9056. {
  9057. SQLMsg * pkMsg(DBManager::instance().DirectQuery("SELECT gold FROM player_shop where id='%d'", GetPrivShop()));
  9058. SQLResult * pRes = pkMsg->Get();
  9059. if (pRes->uiNumRows>0)
  9060. {
  9061. MYSQL_ROW row;
  9062. while ((row = mysql_fetch_row(pRes->pSQLResult)) != NULL)
  9063. {
  9064. long long gold;
  9065. str_to_number(gold, row[0]);
  9066. if (gold>0)
  9067. {
  9068. char query[1024];
  9069. sprintf(query, "INSERT INTO player_gift SET \
  9070. owner_id=%d,vnum=1,count='%s',reason=\"%s\",`from`=replace(\"%s\",' ','_'),status='WAIT',date_add=NOW()",
  9071. GetPrivShopOwner(), row[0], LC_TEXT("SHOP_REASON"), GetName());
  9072. DBManager::instance().DirectQuery(query);
  9073. }
  9074.  
  9075. }
  9076. GetMyShop()->GetItems();
  9077. LPCHARACTER owner = CHARACTER_MANAGER::instance().FindByPID(GetPrivShopOwner());
  9078. if (owner)
  9079. {
  9080. #ifdef GIFT_SYSTEM
  9081. owner->RefreshGift();
  9082. #endif
  9083. owner->RemovePrivShopTable(GetPrivShop());
  9084. owner->SendShops();
  9085. owner->ChatPacket(CHAT_TYPE_INFO, LC_TEXT( "SHOP_SUCCESS_CLOSE"));
  9086. }
  9087. else{
  9088. TPacketShopClose packet;
  9089. packet.shop_id = GetPrivShop();
  9090. packet.pid = GetPrivShopOwner();
  9091. db_clientdesc->DBPacket(HEADER_GD_SHOP_CLOSE, 0, &packet, sizeof(packet));
  9092. }
  9093.  
  9094. DBManager::instance().DirectQuery("delete from player_shop where id='%d'", GetPrivShop());
  9095. CloseMyShop();
  9096. }
  9097. return;
  9098. }
  9099. M2_DESTROY_CHARACTER(this);
  9100. }
  9101.  
  9102.  
  9103.  
  9104. EVENTFUNC(RefreshShopEvent)
  9105. {
  9106. char_event_info* info = dynamic_cast<char_event_info*>(event->info);
  9107. if (info == NULL)
  9108. {
  9109. sys_err("ishop_refresh_event> <Factor> Null pointer");
  9110. return 0;
  9111. }
  9112.  
  9113. LPCHARACTER ch = info->ch;
  9114.  
  9115. if (NULL == ch)
  9116. return 0;
  9117.  
  9118.  
  9119. ch->SendShops();
  9120. ch->SendShopCost();
  9121. if(ch->GetGiftPages()>0)
  9122. ch->ChatPacket(CHAT_TYPE_COMMAND, "gift_info %d", ch->GetGiftPages());
  9123. return PASSES_PER_SEC(10);
  9124. }
  9125. void CHARACTER::StartRefreshShopEvent()
  9126. {
  9127. if (m_pkRefreshShopEvent)
  9128. return;
  9129.  
  9130. char_event_info* info = AllocEventInfo<char_event_info>();
  9131.  
  9132. info->ch = this;
  9133.  
  9134. m_pkRefreshShopEvent = event_create(RefreshShopEvent, info, PASSES_PER_SEC(1)); // 1o?
  9135. }
  9136. #endif
  9137.  
  9138. #ifdef INVENTORY_PROTECT
  9139. DWORD CHARACTER::Inventory_Protect() const
  9140. {
  9141. std::auto_ptr<SQLMsg> pMsg(DBManager::instance().DirectQuery("SELECT kilit FROM player.player WHERE id = '%u';", GetPlayerID()));
  9142. if (pMsg->Get()->uiNumRows == 0)
  9143. return 61;
  9144.  
  9145. MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
  9146. DWORD kilit = 0;
  9147. str_to_number(kilit, row[0]);
  9148. return kilit;
  9149. }
  9150. void CHARACTER::Set_Envanter_Kilidi(int black)
  9151. {
  9152. DBManager::instance().DirectQuery("UPDATE player.player SET kilit = '%d' WHERE id = '%u';", black, GetPlayerID());
  9153. }
  9154. #endif
  9155.  
  9156. #ifdef __CHANGELOOK_SYSTEM__
  9157. void CHARACTER::ChangeLookWindow(bool bOpen, bool bRequest)
  9158. {
  9159. if ((bOpen) && (isChangeLookOpened()))
  9160. {
  9161. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] The window is already opened."));
  9162. return;
  9163. }
  9164.  
  9165. if ((!bOpen) && (!isChangeLookOpened()))
  9166. {
  9167. if (!bRequest)
  9168. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] The window is not opened."));
  9169.  
  9170. return;
  9171. }
  9172.  
  9173. TItemPos tPos;
  9174. tPos.window_type = INVENTORY;
  9175. tPos.cell = 0;
  9176.  
  9177. TPacketChangeLook sPacket;
  9178. sPacket.header = HEADER_GC_CL;
  9179. sPacket.subheader = CL_SUBHEADER_OPEN;
  9180. sPacket.subheader = bOpen == true ? CL_SUBHEADER_OPEN : CL_SUBHEADER_CLOSE;
  9181. sPacket.dwCost = bOpen == true ? CL_TRANSMUTATION_PRICE : 0;
  9182. sPacket.bPos = 0;
  9183. sPacket.tPos = tPos;
  9184. GetDesc()->Packet(&sPacket, sizeof(TPacketChangeLook));
  9185.  
  9186. m_bChangeLook = bOpen;
  9187. ClearClWindowMaterials();
  9188. }
  9189.  
  9190. void CHARACTER::ClearClWindowMaterials()
  9191. {
  9192. LPITEM * pkItemMaterial;
  9193. pkItemMaterial = GetClWindowMaterials();
  9194. for (int i = 0; i < CL_WINDOW_MAX_MATERIALS; ++i)
  9195. {
  9196. if (!pkItemMaterial[i])
  9197. continue;
  9198.  
  9199. pkItemMaterial[i]->Lock(false);
  9200. pkItemMaterial[i] = NULL;
  9201. }
  9202. }
  9203.  
  9204. BYTE CHARACTER::CheckClEmptyMaterialSlot()
  9205. {
  9206. LPITEM * pkItemMaterial;
  9207. pkItemMaterial = GetClWindowMaterials();
  9208. for (int i = 0; i < CL_WINDOW_MAX_MATERIALS; ++i)
  9209. {
  9210. if (!pkItemMaterial[i])
  9211. return i;
  9212. }
  9213.  
  9214. return 255;
  9215. }
  9216.  
  9217. void CHARACTER::AddClMaterial(TItemPos tPos, BYTE bPos)
  9218. {
  9219. if (!isChangeLookOpened())
  9220. return;
  9221. else if (bPos >= CL_WINDOW_MAX_MATERIALS)
  9222. {
  9223. if (bPos != 255)
  9224. return;
  9225.  
  9226. bPos = CheckClEmptyMaterialSlot();
  9227. if (bPos >= CL_WINDOW_MAX_MATERIALS)
  9228. return;
  9229. }
  9230.  
  9231. LPITEM pkItem = GetItem(tPos);
  9232. if (!pkItem)
  9233. return;
  9234. else if ((pkItem->GetCell() >= INVENTORY_MAX_NUM) || (tPos.IsBeltInventoryPosition()))
  9235. return;
  9236. else if (pkItem->IsEquipped())
  9237. {
  9238. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] You cannot transmute an item while it is equipped."));
  9239. return;
  9240. }
  9241. else if ((pkItem->GetType() != ITEM_WEAPON) && (pkItem->GetType() != ITEM_ARMOR) && (pkItem->GetType() != ITEM_COSTUME))
  9242. {
  9243. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] This item cannot be transmuted."));
  9244. return;
  9245. }
  9246. #ifdef __NEW_ARROW_SYSTEM__
  9247. else if ((pkItem->GetType() == ITEM_WEAPON) && ((pkItem->GetSubType() == WEAPON_ARROW) || (pkItem->GetSubType() == WEAPON_MOUNT_SPEAR) || (pkItem->GetSubType() == WEAPON_UNLIMITED_ARROW)))
  9248. #else
  9249. else if ((pkItem->GetType() == ITEM_WEAPON) && ((pkItem->GetSubType() == WEAPON_ARROW) || (pkItem->GetSubType() == WEAPON_MOUNT_SPEAR)))
  9250. #endif
  9251. {
  9252. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] This item cannot be transmuted."));
  9253. return;
  9254. }
  9255. else if ((pkItem->GetType() == ITEM_ARMOR) && (pkItem->GetSubType() != ARMOR_BODY))
  9256. {
  9257. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] This item cannot be transmuted."));
  9258. return;
  9259. }
  9260. #ifdef __WEAPON_COSTUME_SYSTEM__
  9261. else if ((pkItem->GetType() == ITEM_COSTUME) && (pkItem->GetSubType() != COSTUME_BODY) && (pkItem->GetSubType() != COSTUME_HAIR) && (pkItem->GetSubType() != COSTUME_WEAPON))
  9262. #else
  9263. else if ((pkItem->GetType() == ITEM_COSTUME) && (pkItem->GetSubType() != COSTUME_BODY) && (pkItem->GetSubType() != COSTUME_HAIR))
  9264. #endif
  9265. {
  9266. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] This item cannot be transmuted."));
  9267. return;
  9268. }
  9269. #ifdef __SOULBINDING_SYSTEM__
  9270. else if ((pkItem->IsBind()) || (pkItem->IsUntilBind()))
  9271. {
  9272. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] You can't add binded items."));
  9273. return;
  9274. }
  9275. #endif
  9276. else if (pkItem->isLocked())
  9277. {
  9278. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] You can't add locked items."));
  9279. return;
  9280. }
  9281. else if (pkItem->GetTransmutation() != 0)
  9282. {
  9283. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] You can't add items which are transmuted yet."));
  9284. return;
  9285. }
  9286.  
  9287. LPITEM * pkItemMaterial;
  9288. pkItemMaterial = GetClWindowMaterials();
  9289. if ((bPos == 1) && (!pkItemMaterial[0]))
  9290. return;
  9291.  
  9292. if (pkItemMaterial[bPos])
  9293. return;
  9294.  
  9295. if (bPos == 1)
  9296. {
  9297. bool bStop = false;
  9298. if (pkItemMaterial[0]->GetType() != pkItem->GetType())
  9299. bStop = true;
  9300. else if (pkItemMaterial[0]->GetSubType() != pkItem->GetSubType())
  9301. bStop = true;
  9302. else if (pkItemMaterial[0]->GetOriginalVnum() == pkItem->GetOriginalVnum())
  9303. bStop = true;
  9304. else if (((IS_SET(pkItemMaterial[0]->GetAntiFlag(), ITEM_ANTIFLAG_FEMALE)) && (!IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_FEMALE))) || ((IS_SET(pkItemMaterial[0]->GetAntiFlag(), ITEM_ANTIFLAG_MALE)) && (!IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_MALE))))
  9305. bStop = true;
  9306. else if ((pkItem->GetAntiFlag() & ITEM_ANTIFLAG_WARRIOR) && (!IS_SET(pkItemMaterial[0]->GetAntiFlag(), ITEM_ANTIFLAG_WARRIOR)))
  9307. bStop = true;
  9308. else if ((pkItem->GetAntiFlag() & ITEM_ANTIFLAG_ASSASSIN) && (!IS_SET(pkItemMaterial[0]->GetAntiFlag(), ITEM_ANTIFLAG_ASSASSIN)))
  9309. bStop = true;
  9310. else if ((pkItem->GetAntiFlag() & ITEM_ANTIFLAG_SHAMAN) && (!IS_SET(pkItemMaterial[0]->GetAntiFlag(), ITEM_ANTIFLAG_SHAMAN)))
  9311. bStop = true;
  9312. else if ((pkItem->GetAntiFlag() & ITEM_ANTIFLAG_SURA) && (!IS_SET(pkItemMaterial[0]->GetAntiFlag(), ITEM_ANTIFLAG_SURA)))
  9313. bStop = true;
  9314. #ifdef __WOLFMAN_CHARACTER__
  9315. else if ((pkItem->GetAntiFlag() & ITEM_ANTIFLAG_WOLFMAN) && (!IS_SET(pkItemMaterial[0]->GetAntiFlag(), ITEM_ANTIFLAG_WOLFMAN)))
  9316. bStop = true;
  9317. #endif
  9318.  
  9319. if (bStop)
  9320. {
  9321. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] You cannot submit this item."));
  9322. return;
  9323. }
  9324. }
  9325.  
  9326. pkItemMaterial[bPos] = pkItem;
  9327. pkItemMaterial[bPos]->Lock(true);
  9328.  
  9329. TPacketChangeLook sPacket;
  9330. sPacket.header = HEADER_GC_CL;
  9331. sPacket.subheader = CL_SUBHEADER_ADD;
  9332. sPacket.dwCost = 0;
  9333. sPacket.bPos = bPos;
  9334. sPacket.tPos = tPos;
  9335. GetDesc()->Packet(&sPacket, sizeof(TPacketChangeLook));
  9336. }
  9337.  
  9338. void CHARACTER::RemoveClMaterial(BYTE bPos)
  9339. {
  9340. if (bPos >= CL_WINDOW_MAX_MATERIALS)
  9341. return;
  9342.  
  9343. LPITEM * pkItemMaterial;
  9344. pkItemMaterial = GetClWindowMaterials();
  9345.  
  9346. if (!pkItemMaterial[bPos])
  9347. return;
  9348.  
  9349. if (bPos == 1)
  9350. {
  9351. pkItemMaterial[bPos]->Lock(false);
  9352. pkItemMaterial[bPos] = NULL;
  9353. }
  9354. else
  9355. ClearClWindowMaterials();
  9356.  
  9357. TItemPos tPos;
  9358. tPos.window_type = INVENTORY;
  9359. tPos.cell = 0;
  9360.  
  9361. TPacketChangeLook sPacket;
  9362. sPacket.header = HEADER_GC_CL;
  9363. sPacket.subheader = CL_SUBHEADER_REMOVE;
  9364. sPacket.dwCost = 0;
  9365. sPacket.bPos = bPos;
  9366. sPacket.tPos = tPos;
  9367. GetDesc()->Packet(&sPacket, sizeof(TPacketChangeLook));
  9368. }
  9369.  
  9370. void CHARACTER::RefineClMaterials()
  9371. {
  9372. LPITEM * pkItemMaterial;
  9373. pkItemMaterial = GetClWindowMaterials();
  9374. if (!pkItemMaterial[0])
  9375. return;
  9376. else if (!pkItemMaterial[1])
  9377. {
  9378. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] Please submit the item you want to transmute."));
  9379. return;
  9380. }
  9381.  
  9382. //DWORD dwPrice = CL_TRANSMUTATION_PRICE;
  9383. long long dwPrice = CL_TRANSMUTATION_PRICE;// For yang limit ~ Exygo
  9384. if (GetGold() < dwPrice)
  9385. {
  9386. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Transmutation] You don't have enough Yang."));
  9387. return;
  9388. }
  9389.  
  9390. DWORD dwVnum = pkItemMaterial[1]->GetVnum();
  9391. PointChange(POINT_GOLD, -dwPrice);
  9392. DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, pkItemMaterial[0]->GetVnum(), -dwPrice);
  9393. ITEM_MANAGER::instance().RemoveItem(pkItemMaterial[1], "TRANSMUTED (SUCCESSFULLY)");
  9394.  
  9395. pkItemMaterial[0]->SetTransmutation(dwVnum, true);
  9396. ClearClWindowMaterials();
  9397.  
  9398. TItemPos tPos;
  9399. tPos.window_type = INVENTORY;
  9400. tPos.cell = 0;
  9401.  
  9402. TPacketChangeLook sPacket;
  9403. sPacket.header = HEADER_GC_CL;
  9404. sPacket.subheader = CL_SUBHEADER_REFINE;
  9405. sPacket.dwCost = 0;
  9406. sPacket.bPos = 0;
  9407. sPacket.tPos = tPos;
  9408. GetDesc()->Packet(&sPacket, sizeof(TPacketChangeLook));
  9409. }
  9410.  
  9411. bool CHARACTER::CleanTransmutation(LPITEM pkItem, LPITEM pkTarget)
  9412. {
  9413. if (!CanHandleItem())
  9414. return false;
  9415. else if ((!pkItem) || (!pkTarget))
  9416. return false;
  9417. else if ((pkTarget->GetType() != ITEM_WEAPON) && (pkTarget->GetType() != ITEM_ARMOR) && (pkTarget->GetType() != ITEM_COSTUME))
  9418. return false;
  9419. #ifdef __SOULBINDING_SYSTEM__
  9420. else if ((pkTarget->IsBind()) || (pkTarget->IsUntilBind()))
  9421. {
  9422. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't remove the transmute because item is binded."));
  9423. return false;
  9424. }
  9425. #endif
  9426. else if (pkTarget->isLocked())
  9427. {
  9428. ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't remove the transmute because item is locked."));
  9429. return false;
  9430. }
  9431.  
  9432. if (pkTarget->GetTransmutation() == 0)
  9433. return false;
  9434.  
  9435. pkTarget->SetTransmutation(0);
  9436. pkItem->SetCount(pkItem->GetCount() - 1);
  9437. LogManager::instance().ItemLog(this, pkTarget, "USE_DETACHMENT (CLEAN TRANSMUTED)", pkTarget->GetName());
  9438. return true;
  9439. }
  9440. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement