Advertisement
Guest User

Untitled

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