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

char